Imágenes personalizadas grandes – Carga de archivos > 5GB en el almacenamiento de objetos Fujitsu K5 (Swift)
2016-10-08
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
| #!/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
| #!/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).