Images personnalisées de grande taille – Téléchargement de fichiers > 5 Go sur le stockage objet Fujitsu K5 (Swift)

2016-10-08

Images personnalisées de grande taille – Téléchargement de fichiers > 5 Go sur le stockage objet Fujitsu K5 (Swift)

Machine-translated — the English original is authoritative.

Dans un article de blog précédent, j’ai détaillé un processus simple pour télécharger des images personnalisées sur la plateforme IaaS Fujitsu K5. Un défi que j’avais négligé concerne les tailles d’images personnalisées volumineuses, que j’aborderai ici.

Contexte : Les objets OpenStack Swift ont une limitation de taille maximale de 5 Go. Cependant, les conteneurs Swift peuvent contenir des milliers de ces objets. Les fichiers de plus de 5 Go doivent être divisés en un sous-ensemble de fichiers plus petits avant d’être téléchargés dans le conteneur. Une fois tous les fichiers composants téléchargés, un fichier de zéro octet avec un en-tête « manifeste » est téléchargé dans le conteneur. Cet en-tête est composé du nom du conteneur et du préfixe utilisés pour construire les noms des fichiers composants. Lorsque ce fichier de zéro octet est référencé via l’API Swift, tous les fichiers composants sont concaténés et le fichier volumineux original est téléchargé.

Le script Python suivant téléchargera des images personnalisées de moins de 1 Go directement dans le stockage objet de K5. Les fichiers plus volumineux sont divisés en chunks de 1 Go avant d’être téléchargés dans le stockage objet. La taille par défaut de 1 Go peut être modifiée à l’aide des paramètres de la ligne de commande.

Une fois l’image téléchargée, elle est ensuite enregistrée avec le projet K5 par défaut. L’image doit maintenant être partagée avec les autres projets membres qui souhaitent consommer cette image – cela sera partagé dans le prochain article.

Prérequis : Le script s’appuie sur un fichier de paramètres, k5contractsettings.py, qui doit contenir tous vos détails de contrat et être placé dans le même répertoire – par exemple :

Ce fichier contient des caractères Unicode cachés ou bidirectionnels qui peuvent être interprétés ou compilés différemment de ce qui est affiché ci-dessous. Pour les examiner, ouvrez le fichier dans un éditeur qui révèle les caractères Unicode cachés.
En savoir plus sur les caractères Unicode bidirectionnels

Afficher les caractères cachés

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

voir le code brut
k5contractsettings.py
hébergé avec ❤ par GitHub

Script d’exemple de téléchargement d’image

Ce fichier contient des caractères Unicode cachés ou bidirectionnels qui peuvent être interprétés ou compilés différemment de ce qui est affiché ci-dessous. Pour les examiner, ouvrez le fichier dans un éditeur qui révèle les caractères Unicode cachés.
En savoir plus sur les caractères Unicode bidirectionnels

Afficher les caractères cachés

#!/usr/bin/python
# Auteur : Graham Land
# Date : 08/10/2016
#
# Objectif : Télécharger une image personnalisée dans le stockage objet K5, puis l’enregistrer dans K5 Glance
# Si l’image fait plus de 1 Go, elle sera décomposée en chunks de 1 Go
# puis téléchargée
# Paramètres de la ligne de commande –
# -i image_path
# -c container_name
# -s chunk_size (octets)
# -n display_name
# -t image_type
# -p project
#
# Prérequis : fichier k5contractsettings.py dans le même répertoire avec les identifiants de connexion
#
# 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
# charger vos détails de contrat K5 depuis le fichier k5contractsettings.py
from k5contractsettings import *
# obtenir un jeton d’authentification avec scope
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']
# obtenir un jeton du portail d’identité 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']
# créer un conteneur
def create_new_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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
# lister les éléments dans un conteneur
def view_items_in_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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
# télécharger un élément dans un conteneur
def download_item_in_storage_container(adminUser,adminPassword,project,container_name,contract,region):
# obtenir un jeton avec scope de domaine régional pour effectuer des requêtes afin de faciliter la conversion des noms d’objets en identifiants
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):
'''Créer un nom de fichier avec un suffixe de numéro de série.'''
return prefix + str(idx).zfill(4)
def bsplit(in_filename, bytes_per_file,os_type):
'''Diviser le fichier d’entrée in_filename en fichiers de sortie de
bytes_per_file octets chacun. Le dernier fichier peut contenir moins d’octets.'''
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)
# Boucler sur l’entrée et la diviser en plusieurs fichiers
# de bytes_per_file octets chacun (sauf possiblement pour le
# dernier fichier, qui peut contenir moins d’octets.
while c != '':
byte_count += 1
out_fil.write(c)
# Incrémenter les variables ; passer au fichier de sortie suivant.
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 "Package téléchargé – " + 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)
# Nettoyage.
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 "\nPackage téléchargé – " + str(file_count)
os.remove(out_filename)
if byte_count == 0:
os.remove(out_filename)
# maintenant créer le fichier manifeste
result = upload_manifest_to_container(adminUser,adminPassword,defaultid,container_name,file_name,os_type,contract,region)
return result
def main():
try:
# s’assurer que les paramètres de ligne de commande minimaux ont été fournis
if (len(sys.argv)<6):
print("Usage1: %s -i 'path_to_image' -c 'container_name' -n 'image_display_name' -p '{project1,project2,project3}' -t [ubuntu
sys.exit(2)
# charger les paramètres de la ligne de commande
myopts, args = getopt.getopt(sys.argv[1:],"i:c:n:p:t:s:",["imagepath=","container=","name=","projects=","type=","size="])
except getopt.GetoptError:
# si les paramètres sont incorrects, afficher le message d’erreur
print("Usage2: %s -i 'path_to_image' -c 'container_name' -n 'image_display_name' -p '{project1,project2,project3}' -t [ubuntu
sys.exit(2)
# définir les variables globales à partir des paramètres de la ligne de commande
global container_name
global display_name
global bytes_per_file
global os_type
global file_path
global file_name
# définir la taille par défaut du chunk pour les images volumineuses qui doivent être décomposées, elle doit être inférieure à 5 Go pour le stockage objet Swift
bytes_per_file = 1048576000 #5242880 #1048576000 #262144000 # chunks de 250 Mo
###############################
# o == option
# a == argument passé à l’option 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("Usage3: %s -i 'path_to_image' -c 'container_name' -n 'image_display_name' -p '{project1,project2,project3}' -t [ubuntu
# extraire le nom de fichier du chemin d’accès fourni en ligne de commande
file_name = ntpath.basename(file_path)
# tenter de lire le contenu du conteneur pour voir s’il existe déjà
result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
# vérifier si le conteneur existe déjà, sinon le créer
if (result.status_code == 404):
# créer le conteneur
print "\nCréation du nouveau conteneur : " + container_name
result = create_new_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
print "\nNouveau conteneur créé : " + container_name
# vérifier que la taille du fichier à télécharger est inférieure à 250 Go, sinon le diviser en chunks plus petits pour le téléchargement
if (os.path.getsize(file_path) > bytes_per_file):
# boucler sur le fichier image pour le téléchargement multi-parties
print "\n———- Démarrage du téléchargement de fichier multi-parties vers le stockage objet K5 —— \n"
result = bsplit(file_path, bytes_per_file,os_type)
print "\n———- Fin du téléchargement de fichier multi-parties vers le stockage objet K5 —— \n"
else:
# téléchargement simple de fichier dans le conteneur
print "\n———- Démarrage du téléchargement simple de fichier vers le stockage objet K5 —— \n"
result = upload_file_to_container(adminUser,adminPassword,defaultid,container_name,file_name,file_path,contract,region)
print "\n———- Fin du téléchargement simple de fichier vers le stockage objet K5 —— \n"
# lister le conteneur
print "\n———- Début de la liste du contenu du conteneur du stockage objet K5 —— \n"
result = view_items_in_storage_container(adminUser,adminPassword,defaultid,container_name,contract,region)
print result
print "\n———- Fin de la liste du contenu du conteneur du stockage objet K5 —— \n"
# Enregistrer l’image avec K5
print "\n———- Enregistrement de l’image avec 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"
# Obtenir le statut d’importation
print "\n———- Vérification du statut d’importation ———- \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———- Vérification du statut d’importation ———- \n"
result = verify_image_import_status(adminUser,adminPassword,defaultid,image_id,contract,region)
print result
print "Fin du processus d’importation – Statut d’importation >>> " + result['import_status']
if __name__ == "__main__":
main()

voir le code brut
k5ImageUpload.py
hébergé avec ❤ par GitHub

Sortie du script d’exemple :

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