Immagini personalizzate di grandi dimensioni – Caricamento di file > 5GB su Fujitsu K5 Object Storage (Swift)
2016-10-08
Machine-translated — the English original is authoritative.
In un precedente post del blog ho descritto un processo semplice per il caricamento di immagini personalizzate sulla piattaforma IaaS Fujitsu K5. Una sfida che avevo trascurato riguarda le dimensioni delle immagini personalizzate di grandi dimensioni, che affronterò in questo post.
Contesto: Gli oggetti OpenStack Swift hanno una limitazione di dimensione massima di 5GB. Tuttavia, i contenitori Swift possono contenere migliaia di questi oggetti. I file di dimensioni superiori a 5GB devono essere suddivisi in un sottoinsieme di file più piccoli prima di essere caricati nel contenitore. Una volta caricati tutti i file componenti, viene caricato nel contenitore un file a zero byte con un'intestazione 'manifest'. Questa intestazione è composta dal nome del contenitore e dal prefisso utilizzati per costruire i nomi dei file componenti. Quando questo file a zero byte viene referenziato tramite l'API Swift, tutti i file componenti vengono concatenati e il file grande originale viene scaricato.
Lo script Python seguente caricherà immagini personalizzate inferiori a 1 GB direttamente nell'object storage di K5. I file di dimensioni maggiori vengono suddivisi in chunk da 1 GB prima del caricamento nell'object storage. La dimensione predefinita di 1 GB può essere modificata utilizzando i parametri della riga di comando.
Una volta caricata l'immagine, questa viene registrata con il progetto K5 predefinito. L'immagine deve ora essere condivisa con altri progetti membri che desiderano utilizzare questa immagine – questo verrà trattato nel prossimo post.
Prerequisiti : Lo script si basa su un file di impostazioni, k5contractsettings.py, che deve contenere tutti i dettagli del contratto ed essere collocato nella stessa directory – ad esempio:
Questo file contiene caratteri Unicode nascosti o bidirezionali che potrebbero essere interpretati o compilati in modo diverso rispetto a quanto appare di seguito. Per rivederli, apri il file in un editor che riveli i caratteri Unicode nascosti.
Maggiori informazioni sui caratteri Unicode bidirezionali
| #!/usr/bin/python | |
| adminUser = 'username' | |
| adminPassword = 'password' | |
| contract = 'contract_name' | |
| contractid = 'contract_id' | |
| defaultid = 'default_project_id' | |
| project = 'working_project' | |
| region = 'uk-1' |
visualizza raw
k5contractsettings.py
ospitato con ❤ da GitHub
Script di esempio per il caricamento dell'immagine
Questo file contiene caratteri Unicode nascosti o bidirezionali che potrebbero essere interpretati o compilati in modo diverso rispetto a quanto appare di seguito. Per rivederli, apri il file in un editor che riveli i caratteri Unicode nascosti.
Maggiori informazioni sui caratteri Unicode bidirezionali
| #!/usr/bin/python | |
| # Autore : Graham Land | |
| # Data: 08/10/2016 | |
| # | |
| # Scopo: Caricare un'immagine personalizzata su K5 Object Storage e poi registrarla su K5 Glance | |
| # Se l'immagine è superiore a 1GB, verrà suddivisa in chunk da 1GB | |
| # e poi caricata | |
| # Parametri della riga di comando – | |
| # -i image_path | |
| # -c container_name | |
| # -s chunk_size (bytes) | |
| # -n display_name | |
| # -t image_type | |
| # -p project | |
| # | |
| # Prerequisiti: file k5contractsettings.py nella stessa directory con i dettagli di accesso | |
| # | |
| # 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 | |
| # carica i dettagli del contratto K5 dal file k5contractsettings.py | |
| from k5contractsettings import * | |
| # ottieni un token di autenticazione scoped | |
| 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'] | |
| # ottieni un token del portale di identità centrale | |
| 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'] | |
| # crea un contenitore | |
| def create_new_storage_container(adminUser,adminPassword,project,container_name,contract,region): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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 | |
| # elenca gli elementi in un contenitore | |
| def view_items_in_storage_container(adminUser,adminPassword,project,container_name,contract,region): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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 | |
| # scarica un elemento in un contenitore | |
| def download_item_in_storage_container(adminUser,adminPassword,project,container_name,contract,region): | |
| # ottieni un token scoped sul dominio regionale per effettuare query che facilitino la conversione dei nomi degli oggetti in ID | |
| 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): | |
| '''Crea un nome di file con un suffisso numerico seriale.''' | |
| return prefix + str(idx).zfill(4) | |
| def bsplit(in_filename, bytes_per_file,os_type): | |
| '''Dividi il file di input in_filename in file di output di | |
| bytes_per_file byte ciascuno. L'ultimo file potrebbe avere meno byte.''' | |
| 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) | |
| # Ciclo sul file di input e dividilo in più file | |
| # di bytes_per_file byte ciascuno (tranne possibilmente per l'ultimo | |
| # file, che potrebbe avere meno byte. | |
| while c != '': | |
| byte_count += 1 | |
| out_fil.write(c) | |
| # Incrementa le variabili; passa al file di output successivo. | |
| 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 "Pacchetto caricato – " + 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) | |
| # Pulizia. | |
| 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 "\nPacchetto caricato – " + str(file_count) | |
| os.remove(out_filename) | |
| if byte_count == 0: | |
| os.remove(out_filename) | |
| # ora crea il file manifest | |
| result = upload_manifest_to_container(adminUser,adminPassword,defaultid,container_name,file_name,os_type,contract,region) | |
| return result | |
| def main(): | |
| try: | |
| # assicurati che siano stati forniti i parametri minimi della riga di comando | |
| if (len(sys.argv)<6): | |
| print("Utilizzo1: %s -i 'percorso_immagine' -c 'nome_contenitore' -n 'nome_visualizzazione_immagine' -p '{project1,project2,project3}' -t [ubuntu | |
| sys.exit(2) | |
| # carica i parametri della riga di comando | |
| myopts, args = getopt.getopt(sys.argv[1:],"i:c:n:p:t:s:",["imagepath=","container=","name=","projects=","type=","size="]) | |
| except getopt.GetoptError: | |
| # se i parametri non sono corretti visualizza il messaggio di errore | |
| print("Utilizzo2: %s -i 'percorso_immagine' -c 'nome_contenitore' -n 'nome_visualizzazione_immagine' -p '{project1,project2,project3}' -t [ubuntu | |
| sys.exit(2) | |
| # definisci le variabili globali dai parametri della riga di comando | |
| global container_name | |
| global display_name | |
| global bytes_per_file | |
| global os_type | |
| global file_path | |
| global file_name | |
| # imposta la dimensione predefinita del chunk per le immagini di grandi dimensioni che devono essere suddivise; deve essere inferiore a 5GB per Swift Object Storage | |
| bytes_per_file = 1048576000 #5242880 #1048576000 #262144000 # chunk da 250Mb | |
| ############################### | |
| # o == opzione | |
| # a == argomento passato all'opzione 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("Utilizzo3: %s -i 'percorso_immagine' -c 'nome_contenitore' -n 'nome_visualizzazione_immagine' -p '{project1,project2,project3}' -t [ubuntu | |
| # estrai il nome del file dal percorso del file fornito da riga di comando | |
| file_name = ntpath.basename(file_path) | |
| # tenta di leggere il contenuto del contenitore per vedere se esiste già | |
| result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region) | |
| # controlla se il contenitore esiste già, se non esiste allora crealo | |
| if (result.status_code == 404): | |
| # crea contenitore | |
| print "\nCreazione di un nuovo contenitore : " + container_name | |
| result = create_new_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region) | |
| print "\nCreato nuovo contenitore : " + container_name | |
| # controlla che la dimensione del file da caricare sia inferiore a 250GB, se non lo è suddividilo in chunk più piccoli per il caricamento | |
| if (os.path.getsize(file_path) > bytes_per_file): | |
| # ciclo attraverso il file immagine per il caricamento multi-part | |
| print "\n———- Avvio del caricamento del file multi-part su K5 object storage —— \n" | |
| result = bsplit(file_path, bytes_per_file,os_type) | |
| print "\n———- Fine del caricamento del file multi-part su K5 object storage —— \n" | |
| else: | |
| # caricamento semplice del file nel contenitore | |
| print "\n———- Avvio del caricamento semplice del file su K5 object storage —— \n" | |
| result = upload_file_to_container(adminUser,adminPassword,defaultid,container_name,file_name,file_path,contract,region) | |
| print "\n———- Fine del caricamento semplice del file su K5 object storage —— \n" | |
| # elenca il contenitore | |
| print "\n———- Inizio elenco contenuti contenitore K5 object storage —— \n" | |
| result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region) | |
| print result | |
| print "\n———- Fine elenco contenuti contenitore K5 object storage —— \n" | |
| # Registra l'immagine con K5 | |
| print "\n———- Registrazione dell'immagine 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" | |
| # Ottieni lo stato di importazione | |
| print "\n———- Controllo stato importazione ———- \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———- Controllo stato importazione ———- \n" | |
| result = verify_image_import_status(adminUser,adminPassword,defaultid,image_id,contract,region) | |
| print result | |
| print "Fine del processo di importazione – Stato importazione >>> " + result['import_status'] | |
| if __name__ == "__main__": | |
| main() | |
visualizza raw
k5ImageUpload.py
ospitato con ❤ da GitHub
Output di esempio dello 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
Creating new container : uploaddemo12
31c0f27e562c4b3089a546c175c144e4
https://objectstorage.uk-1.cloud.global.fujitsu.com/v1/AUTH_eadb882573ac40b1b101
eac93009a313/uploaddemo12
Created new container : uploaddemo12
---------- Starting multi-part file upload to K5 object storage ------
Uploaded Package - 1
Uploaded Package - 2
Uploaded Package - 3
Uploaded Package - 4
Uploaded Package - 4
---------- Finished multi-part file upload to K5 object storage ------
---------- List container contents K5 object storage start ------
<Response [200]>
---------- List container contents K5 object storage end ------
---------- Registering image with K5 ------
{u'import_id': u'6a0b58c5-bcda-4a64-919e-23f06b8338ad'}
---------- K5 Image import_id : 6a0b58c5-bcda-4a64-919e-23f06b8338ad
---------- Check import status ----------
{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'}
---------- Check import status ----------
{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'}
---------- Check import status ----------
{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'}
End of Import Process - Import status >>> succeeded
Happy Stacking!
Originally published on allthingscloud.eu (2016-10-08).