Immagini personalizzate di grandi dimensioni – Caricamento di file > 5GB su Fujitsu K5 Object Storage (Swift)

2016-10-08

Immagini personalizzate di grandi dimensioni – Caricamento di file > 5GB su Fujitsu K5 Object Storage (Swift)

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

Mostra caratteri nascosti

#!/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

Mostra caratteri nascosti

#!/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).

← All posts