Imágenes personalizadas grandes – Carga de archivos > 5GB en el almacenamiento de objetos Fujitsu K5 (Swift)

2016-10-08

Imágenes personalizadas grandes – Carga de archivos > 5GB en el almacenamiento de objetos Fujitsu K5 (Swift)

Machine-translated — the English original is authoritative.

En una publicación anterior, detallé un proceso sencillo para cargar imágenes personalizadas en la plataforma IaaS Fujitsu K5. Un desafío que pasé por alto fue el tamaño de las imágenes personalizadas grandes, que abordaré aquí.

Antecedentes: Los objetos de OpenStack Swift tienen una limitación de tamaño máximo de 5GB. Sin embargo, los contenedores de Swift pueden tener miles de estos objetos. Los archivos que superan los 5GB deben dividirse en un subconjunto de archivos más pequeños antes de cargarlos en el contenedor. Una vez que se han cargado todos los archivos componentes, se carga en el contenedor un archivo de cero bytes con un encabezado de 'manifiesto'. Este encabezado está compuesto por el nombre del contenedor y el prefijo utilizado para construir los nombres de los archivos componentes. Cuando se hace referencia a este archivo de cero bytes a través de la API de Swift, todos los archivos componentes se concatenan y se descarga el archivo grande original.

El siguiente script de Python cargará imágenes personalizadas de menos de 1 GB directamente en el almacenamiento de objetos de K5. Los archivos más grandes se dividen en fragmentos de 1 GB antes de cargarlos en el almacenamiento de objetos. El tamaño predeterminado de 1 GB se puede cambiar utilizando los parámetros de la línea de comandos.

Una vez cargada la imagen, se registra con el proyecto predeterminado de K5. La imagen debe compartirse ahora con otros proyectos de miembros que deseen consumir esta imagen; esto se compartirá en la próxima publicación.

Requisitos previos : El script depende de un archivo de configuración, k5contractsettings.py, que debe contener todos sus detalles del contrato y colocarse en el mismo directorio, por ejemplo:

Este archivo contiene texto Unicode oculto o bidireccional que puede interpretarse o compilarse de manera diferente a lo que aparece a continuación. Para revisarlo, abra el archivo en un editor que revele caracteres Unicode ocultos.
Más información sobre caracteres Unicode bidireccionales

Mostrar caracteres ocultos

#!/usr/bin/python
adminUser = 'username'
adminPassword = 'password'
contract = 'contract_name'
contractid = 'contract_id'
defaultid = 'default_project_id'
project = 'working_project'
region = 'uk-1'

ver original
k5contractsettings.py
alojado con ❤ por GitHub

Script de ejemplo de carga de imagen

Este archivo contiene texto Unicode oculto o bidireccional que puede interpretarse o compilarse de manera diferente a lo que aparece a continuación. Para revisarlo, abra el archivo en un editor que revele caracteres Unicode ocultos.
Más información sobre caracteres Unicode bidireccionales

Mostrar caracteres ocultos

#!/usr/bin/python
# Autor : Graham Land
# Fecha: 08/10/2016
#
# Propósito: Cargar una imagen personalizada en el Almacenamiento de Objetos K5 y luego registrarla con K5 Glance
# Si la imagen es mayor de 1GB, se dividirá en fragmentos de 1GB
# y luego se cargará
# Parámetros de la línea de comandos –
# -i image_path
# -c container_name
# -s chunk_size (bytes)
# -n display_name
# -t image_type
# -p project
#
# Requisitos previos: archivo k5contractsettings.py en el mismo directorio con los detalles de inicio de sesión
#
# adminUser = 'username'
# adminPassword = 'password'
# contract = 'contract_name'
# contractid = 'contract_id'
# defaultid = 'default_project_id'
# project = 'working_project'
# region = 'uk-1'
#
# blog: https://allthingscloud.eu
# twitter: @allthingsclowd
import sys
import os
import requests
import uuid
import base64
import time
import getopt
import ntpath
# cargar los detalles de su contrato K5 desde el archivo k5contractsettings.py
from k5contractsettings import *
# obtener un token de autenticación con ámbito
def get_scoped_token(uname,upassword,uproject,udomain,uregion):
identityURL = 'https://identity.' + uregion + '.cloud.global.fujitsu.com/v3/auth/tokens'
response = requests.post(identityURL,
headers={'Content-Type': 'application/json','Accept':'application/json'},
json={"auth":
{"identity":
{"methods":["password"],"password":
{"user":
{"domain":
{"name":udomain},
"name":uname,
"password": upassword
}}},
"scope":
{ "project":
{"id":uproject
}}}})
return response.headers['X-Subject-Token']
def get_unscoped_token(uname,upassword,udomain,uregion):
identityURL = 'https://identity.' + uregion + '.cloud.global.fujitsu.com/v3/auth/tokens'
response = requests.post(identityURL,
headers={'Content-Type': 'application/json','Accept':'application/json'},
json={"auth":
{"identity":
{"methods":["password"],"password":
{"user":
{"domain":
{"name":udomain},
"name":uname,
"password": upassword
}}}}})
return response.headers['X-Subject-Token']
# obtener un token del portal de identidad central
def get_unscoped_idtoken(uname,upassword,udomain):
response = requests.post('https://auth-api.jp-east-1.paas.cloud.global.fujitsu.com/API/paas/auth/token',
headers={'Content-Type': 'application/json'},
json={"auth":
{"identity":
{"password":
{"user":
{"contract_number":udomain,
"name":uname,
"password": upassword
}}}}})
return response.headers['X-Access-Token']
# crear un contenedor
def create_new_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
print scoped_k5token
identityURL = 'https://objectstorage.' + region + '.cloud.global.fujitsu.com/v1/AUTH_' + project + '/' + container_name
print identityURL
response = requests.put(identityURL,
headers={'X-Auth-Token':scoped_k5token,'Content-Type': 'application/json'})
return response
def upload_file_to_container(adminUser,adminPassword,project,container_name,file_name,file_path,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
uploadfile = open(file_path, 'rb')
data = uploadfile.read()
identityURL = 'https://objectstorage.' + region + '.cloud.global.fujitsu.com/v1/AUTH_' + project + '/' + container_name + '/' + file_name
response = requests.put(identityURL,
data=data,
headers={'X-Auth-Token':scoped_k5token,'Content-Type': 'application/octet-stream'})
uploadfile.close
return response
def import_from_container_to_k5(adminUser,adminPassword,project,container_name,file_name,display_name,file_path,os_type,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
k5ContainerURL = '/v1/AUTH_' + project + '/' + container_name + '/' + file_name
image_id = str(uuid.uuid4())
encodedPassword = base64.b64encode(adminPassword)
vmimportURL = 'https://vmimport.' + region + '.cloud.global.fujitsu.com/v1/imageimport'
response = requests.post(vmimportURL,
headers={'X-Auth-Token':scoped_k5token},
json={"name":display_name,
"location":k5ContainerURL,
"id":image_id,
"conversion": True,
"os_type":os_type,
"user_name":adminUser,
"password":encodedPassword,
"domain_name":contract})
return response.json()
def verify_image_import_status(adminUser,adminPassword,project,image_id,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
vmimportURL = 'https://vmimport.' + region + '.cloud.global.fujitsu.com/v1/imageimport/' + image_id + '/status'
response = requests.get(vmimportURL,
headers={'X-Auth-Token':scoped_k5token})
return response.json()
def upload_manifest_to_container(adminUser,adminPassword,project,container_name,file_name,prefix,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
identityURL = 'https://objectstorage.' + region + '.cloud.global.fujitsu.com/v1/AUTH_' + project + '/' + container_name + '/' + file_name
response = requests.put(identityURL,
headers={'X-Auth-Token':scoped_k5token,'X-Object-Manifest': container_name + '/' + prefix})
return response
# listar elementos en un contenedor
def view_items_in_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
identityURL = 'https://objectstorage.' + region + '.cloud.global.fujitsu.com/v1/AUTH_' + project + '/' + container_name + '?format=json'
response = requests.get(identityURL,
headers={'X-Auth-Token':scoped_k5token,'Content-Type': 'application/json'})
return response
# descargar elemento en un contenedor
def download_item_in_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtener un token con ámbito de dominio regional para realizar consultas que faciliten la conversión de nombres de objetos a IDs
scoped_k5token = get_scoped_token(adminUser,adminPassword,project,contract,region)
identityURL = 'https://objectstorage.' + region + '.cloud.global.fujitsu.com/v1/AUTH_' + project + '/' + container_name + '/manifest'
print identityURL
response = requests.get(identityURL,
headers={'X-Auth-Token':scoped_k5token,'Content-Type': 'application/json'})
return response
def make_out_filename(prefix, idx):
'''Crear un nombre de archivo con un sufijo de número de serie.'''
return prefix + str(idx).zfill(4)
def bsplit(in_filename, bytes_per_file,os_type):
'''Dividir el archivo de entrada in_filename en archivos de salida de
bytes_per_file bytes cada uno. El último archivo puede tener menos bytes.'''
in_fil = open(in_filename, "rb")
outfil_idx = 1
out_filename = make_out_filename(os_type, outfil_idx)
out_fil = open(out_filename, "wb")
byte_count = tot_byte_count = file_count = 0
c = in_fil.read(1)
# Bucle sobre la entrada y dividirla en múltiples archivos
# de bytes_per_file bytes cada uno (excepto posiblemente el
# último archivo, que puede tener menos bytes.
while c != '':
byte_count += 1
out_fil.write(c)
# Incrementar variables; cambiar al siguiente archivo de salida.
if byte_count >= bytes_per_file:
tot_byte_count += byte_count
byte_count = 0
file_count += 1
out_fil.close()
result = upload_file_to_container(adminUser,adminPassword,defaultid,container_name,out_filename,out_filename,contract,region)
print "Paquete subido – " + str(file_count)
os.remove(out_filename)
outfil_idx += 1
out_filename = make_out_filename(os_type, outfil_idx)
out_fil = open(out_filename, "wb")
c = in_fil.read(1)
# Limpiar.
in_fil.close()
if not out_fil.closed:
out_fil.close()
result = upload_file_to_container(adminUser,adminPassword,defaultid,container_name,out_filename,out_filename,contract,region)
print "\nPaquete subido – " + str(file_count)
os.remove(out_filename)
if byte_count == 0:
os.remove(out_filename)
# ahora crear archivo de manifiesto
result = upload_manifest_to_container(adminUser,adminPassword,defaultid,container_name,file_name,os_type,contract,region)
return result
def main():
try:
# asegurar que se hayan proporcionado los parámetros mínimos de la línea de comandos
if (len(sys.argv)<6):
print("Uso1: %s -i 'ruta_a_la_imagen' -c 'nombre_contenedor' -n 'nombre_mostrar_imagen' -p '{proyecto1,proyecto2,proyecto3}' -t [ubuntu
sys.exit(2)
# cargar los parámetros de la línea de comandos
myopts, args = getopt.getopt(sys.argv[1:],"i:c:n:p:t:s:",["imagepath=","container=","name=","projects=","type=","size="])
except getopt.GetoptError:
# si los parámetros son incorrectos, mostrar mensaje de error
print("Uso2: %s -i 'ruta_a_la_imagen' -c 'nombre_contenedor' -n 'nombre_mostrar_imagen' -p '{proyecto1,proyecto2,proyecto3}' -t [ubuntu
sys.exit(2)
# definir variables globales a partir de los parámetros de la línea de comandos
global container_name
global display_name
global bytes_per_file
global os_type
global file_path
global file_name
# establecer tamaño de fragmento predeterminado para imágenes grandes que necesitan ser divididas, debe ser inferior a 5GB para el Almacenamiento de Objetos Swift
bytes_per_file = 1048576000 #5242880 #1048576000 #262144000 # fragmentos de 250Mb
###############################
# o == opción
# a == argumento pasado a la o
###############################
for o, a in myopts:
if o in ('-i','–imagepath'):
file_path=a
elif o in ('-c','–container'):
container_name=a
elif o in ('-n','–name'):
display_name=a
elif o in ('-p','–projects'):
projects=a
elif o in ('-t','–type'):
os_type=a
elif o in ('-s','–size'):
bytes_per_file=int(a)
else:
print("Uso3: %s -i 'ruta_a_la_imagen' -c 'nombre_contenedor' -n 'nombre_mostrar_imagen' -p '{proyecto1,proyecto2,proyecto3}' -t [ubuntu
# extraer nombre de archivo de la ruta de archivo proporcionada en la CLI
file_name = ntpath.basename(file_path)
# intentar leer el contenido del contenedor para ver si ya existe
result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
# verificar si el contenedor ya existe, si no, crearlo
if (result.status_code == 404):
# crear contenedor
print "\nCreando nuevo contenedor : " + container_name
result = create_new_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
print "\nNuevo contenedor creado : " + container_name
# verificar que el tamaño del archivo a cargar sea menor de 250GB, si no, dividirlo en fragmentos más pequeños para la carga
if (os.path.getsize(file_path) > bytes_per_file):
# bucle a través del archivo de imagen para carga multiparte
print "\n———- Iniciando carga de archivo multiparte al almacenamiento de objetos K5 —— \n"
result = bsplit(file_path, bytes_per_file,os_type)
print "\n———- Finalizada la carga de archivo multiparte al almacenamiento de objetos K5 —— \n"
else:
# carga de archivo simple al contenedor
print "\n———- Iniciando carga de archivo simple al almacenamiento de objetos K5 —— \n"
result = upload_file_to_container(adminUser,adminPassword,defaultid,container_name,file_name,file_path,contract,region)
print "\n———- Finalizada la carga de archivo simple al almacenamiento de objetos K5 —— \n"
# listar contenedor
print "\n———- Listar contenido del contenedor del almacenamiento de objetos K5 inicio —— \n"
result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
print result
print "\n———- Listar contenido del contenedor del almacenamiento de objetos K5 fin —— \n"
# Registrar imagen con K5
print "\n———- Registrando imagen con K5 —— \n"
result = import_from_container_to_k5(adminUser,adminPassword,defaultid,container_name,file_name,display_name,file_path,os_type,contract,region)
image_id = result['import_id']
print result
print "\n———- K5 Image import_id : " + image_id + "\n"
# Obtener estado de importación
print "\n———- Verificar estado de importación ———- \n\n"
result = verify_image_import_status(adminUser,adminPassword,defaultid,image_id,contract,region)
print result
while ((result['import_status'] != "succeeded") and (result['import_status'] != "failed")):
time.sleep(300)
print "\n———- Verificar estado de importación ———- \n"
result = verify_image_import_status(adminUser,adminPassword,defaultid,image_id,contract,region)
print result
print "Fin del Proceso de Importación – Estado de importación >>> " + result['import_status']
if __name__ == "__main__":
main()

ver original
k5ImageUpload.py
alojado con ❤ por GitHub

Salida de ejemplo del script:

C:\Users\landg\>python K5ImageUpload.py -i "c:\Users\landg\Downloads\cirrosuploadtest.vmdk" -c uploaddemo12 -n "h
ello k5 milti image"  -t ubuntu -p NotUsed -s 5242880

Creando nuevo contenedor : uploaddemo12
31c0f27e562c4b3089a546c175c144e4
https://objectstorage.uk-1.cloud.global.fujitsu.com/v1/AUTH_eadb882573ac40b1b101
eac93009a313/uploaddemo12

Nuevo contenedor creado : uploaddemo12

---------- Iniciando carga de archivo multiparte al almacenamiento de objetos K5 ------

Paquete subido - 1
Paquete subido - 2
Paquete subido - 3
Paquete subido - 4

Paquete subido - 4

---------- Finalizada la carga de archivo multiparte al almacenamiento de objetos K5 ------

---------- Listar contenido del contenedor del almacenamiento de objetos K5 inicio ------

<Response [200]>

---------- Listar contenido del contenedor del almacenamiento de objetos K5 fin ------

---------- Registrando imagen con K5 ------

{u'import_id': u'6a0b58c5-bcda-4a64-919e-23f06b8338ad'}

---------- K5 Image import_id : 6a0b58c5-bcda-4a64-919e-23f06b8338ad

---------- Verificar estado de importación ----------

{u'conversion': True, u'name': u'hello k5 milti image', u'container_format': u'b
are', u'min_ram': u'0', u'ovf_location': u'', u'disk_format': u'raw', u'domain_n
ame': u'YssmW1yI', u'location': u'/v1/AUTH_eadb882573ac40b1b101eac93009a313/uplo
addemo12/cirrosuploadtest.vmdk', u'min_disk': u'0', u'progress': u'0', u'os_type
': u'ubuntu', u'password': u'*', u'user_name': u'landg', u'id': u'70a38639-f819-
4375-b3d2-cfc99c2a148e', u'import_status': u'queued'}

---------- Verificar estado de importación ----------

{u'conversion': True, u'name': u'hello k5 milti image', u'container_format': u'b
are', u'min_ram': u'0', u'ovf_location': u'', u'disk_format': u'raw', u'domain_n
ame': u'YssmW1yI', u'location': u'/v1/AUTH_eadb882573ac40b1b101eac93009a313/uplo
addemo12/cirrosuploadtest.vmdk', u'min_disk': u'0', u'progress': 0, u'os_type':
u'ubuntu', u'password': u'*', u'user_name': u'landg', u'id': u'70a38639-f819-437
5-b3d2-cfc99c2a148e', u'import_status': u'processing'}

---------- Verificar estado de importación ----------

{u'container_format': u'bare', u'min_ram': 0, u'updated_at': u'2016-10-08T17:24:
38Z', u'file': u'/v2/images/70a38639-f819-4375-b3d2-cfc99c2a148e/file', u'owner'
: u'eadb882573ac40b1b101eac93009a313', u'id': u'70a38639-f819-4375-b3d2-cfc99c2a
148e', u'size': 41126400, u'conversion': True, u'self': u'/v2/images/70a38639-f8
19-4375-b3d2-cfc99c2a148e', u'disk_format': u'raw', u'domain_name': u'YssmW1yI',
 u'location': u'/v1/AUTH_eadb882573ac40b1b101eac93009a313/uploaddemo12/cirrosupl
oadtest.vmdk', u'progress': 100, u'user_name': u'landg', u'schema': u'/v2/schema
s/image', u'status': u'active', u'import_status': u'succeeded', u'tags': [], u'v
isibility': u'private', u'BaseImageId': u'415b3a0a513aebc27d34c68bd8cdae8c', u'm
in_disk': 0, u'password': u'*', u'name': u'hello k5 milti image', u'created_at':
 u'2016-10-08T17:24:32Z', u'ovf_location': u'', u'fcx.centos': u'true', u'protec
ted': False, u'os_type': u'ubuntu'}
Fin del Proceso de Importación - Estado de importación >>>  succeeded

¡Feliz apilamiento!

Originally published on allthingscloud.eu (2016-10-08).

← All posts