33"""
44
55__author__ = 'VMware, Inc.'
6- __copyright__ = 'Copyright 2018 VMware, Inc. All rights reserved.'
6+ __copyright__ = 'Copyright 2019 VMware, Inc. All rights reserved.'
77
88import argparse
99import boto3
@@ -68,18 +68,51 @@ def _make_lib(name, id=uuid.uuid4(), creation=datetime.datetime.now(), version=1
6868
6969
7070def _make_item (directory , vcsp_type , name , files , description = "" , properties = {},
71- identifier = uuid .uuid4 (), creation = datetime .datetime .now (), version = 2 ):
72- return {
73- "created" : creation .strftime (ISO_FORMAT ),
74- "description" : description ,
75- "version" : str (version ),
76- "files" : files ,
77- "id" : "urn:uuid:%s" % identifier ,
78- "name" : name ,
79- "properties" : properties ,
80- "selfHref" : "%s/%s" % (directory , ITEM_FILE ),
81- "type" : vcsp_type
82- }
71+ identifier = uuid .uuid4 (), creation = datetime .datetime .now (), version = 2 ,
72+ library_id = "" , is_vapp_template = "false" ):
73+ '''
74+ add type adapter metadata for OVF template
75+ '''
76+ if "urn:uuid:" not in str (identifier ):
77+ item_id = "urn:uuid:%s" % identifier
78+ else :
79+ item_id = identifier
80+ type_metadata = None
81+ if vcsp_type == VCSP_TYPE_OVF :
82+ # generate sample type metadata for OVF template so that subscriber can show OVF VM type
83+ type_metadata_value = "{\" id\" :\" %s\" ,\" version\" :\" %s\" ,\" libraryIdParent\" :\" %s\" ,\" isVappTemplate\" :\" %s\" ,\" vmTemplate\" :null,\" vappTemplate\" :null,\" networks\" :[],\" storagePolicyGroups\" :null}" % (item_id , str (version ), library_id , is_vapp_template )
84+ type_metadata = {
85+ "key" : "type-metadata" ,
86+ "value" : type_metadata_value ,
87+ "type" : "String" ,
88+ "domain" : "SYSTEM" ,
89+ "visibility" : "READONLY"
90+ }
91+ if type_metadata :
92+ return {
93+ "created" : creation .strftime (ISO_FORMAT ),
94+ "description" : description ,
95+ "version" : str (version ),
96+ "files" : files ,
97+ "id" : item_id ,
98+ "name" : name ,
99+ "metadata" : [type_metadata ],
100+ "properties" : properties ,
101+ "selfHref" : "%s/%s" % (directory , ITEM_FILE ),
102+ "type" : vcsp_type
103+ }
104+ else :
105+ return {
106+ "created" : creation .strftime (ISO_FORMAT ),
107+ "description" : description ,
108+ "version" : str (version ),
109+ "files" : files ,
110+ "id" : item_id ,
111+ "name" : name ,
112+ "properties" : properties ,
113+ "selfHref" : "%s/%s" % (directory , ITEM_FILE ),
114+ "type" : vcsp_type
115+ }
83116
84117
85118def _make_items (items , version = 1 ):
@@ -88,7 +121,7 @@ def _make_items(items, version=1):
88121 }
89122
90123
91- def _dir2item (path , directory , md5_enabled ):
124+ def _dir2item (path , directory , md5_enabled , lib_id ):
92125 files_items = []
93126 name = os .path .split (path )[- 1 ]
94127 vcsp_type = VCSP_TYPE_OTHER
@@ -111,6 +144,8 @@ def _dir2item(path, directory, md5_enabled):
111144 m .update (os .path .dirname (p ).encode ('utf-8' ))
112145 if ".ovf" in p :
113146 vcsp_type = VCSP_TYPE_OVF
147+ # TODO: ready ovf descriptor for type metadata
148+ is_vapp = "false"
114149 elif ".iso" in p :
115150 vcsp_type = VCSP_TYPE_ISO
116151 size = os .path .getsize (p )
@@ -125,10 +160,10 @@ def _dir2item(path, directory, md5_enabled):
125160 "etag" : folder_md5 ,
126161 "hrefs" : [ href ]
127162 })
128- return _make_item (name , vcsp_type , name , files_items , identifier = uuid .uuid4 ())
163+ return _make_item (name , vcsp_type , name , files_items , identifier = uuid .uuid4 (), library_id = lib_id , is_vapp_template = is_vapp )
129164
130165
131- def _dir2item_s3 (s3_client , bucket_name , path , item_name , skip_cert ):
166+ def _dir2item_s3 (s3_client , bucket_name , path , item_name , skip_cert , lib_id , old_item = "" ):
132167 """
133168 Generate items jsons for the given item path on s3
134169
@@ -142,22 +177,35 @@ def _dir2item_s3(s3_client, bucket_name, path, item_name, skip_cert):
142177 path: item path on S3 bucket
143178 item_name: name of the item
144179 skip_cert: whether or not to skip cert file
180+ lib_id: library id
181+ old_item: old item json
145182
146183 Returns:
147184 map of item name to item json
148185 """
149- items_jsons = {}
186+ items_json = {}
150187 files_items = []
151188 vcsp_type = None
152189 response = s3_client .list_objects_v2 (Bucket = bucket_name , Prefix = path , Delimiter = "/" )
153190
191+ is_vapp = "false"
154192 for content in response ['Contents' ]:
155193 file_path = content ['Key' ]
156194 if file_path == path or file_path .endswith ("item.json" ):
157195 continue
158196 file_name = file_path .split ("/" )[- 1 ]
159197 if ".ovf" in file_name :
160198 vcsp_type = VCSP_TYPE_OVF
199+ # check if the existing item json already contains "type-metadata" metadata, if not
200+ # download the OVF file and parse the descriptor for metadata and search for "<VirtualSystemCollection"
201+ if "type-metadata" not in old_item :
202+ try :
203+ s3_ovf_obj = s3_client .get_object (Bucket = bucket_name , Key = file_path )
204+ ovf_desc = s3_ovf_obj ['Body' ].read ().decode ('utf-8' )
205+ if "<VirtualSystemCollection" in ovf_desc :
206+ is_vapp = "true"
207+ except :
208+ logger .error ("Failed to read ovf descriptor: %s" % file_path )
161209 if vcsp_type != VCSP_TYPE_OVF and ".iso" not in file_name :
162210 vcsp_type = VCSP_TYPE_OTHER
163211 if vcsp_type not in [VCSP_TYPE_OVF , VCSP_TYPE_OTHER ] and ".iso" in file_name :
@@ -168,7 +216,7 @@ def _dir2item_s3(s3_client, bucket_name, path, item_name, skip_cert):
168216 file_name = file_path .split ("/" )[- 1 ]
169217 href = "%s/%s" % (item_name , file_name )
170218
171- if file_path == path or file_path .endswith ("item.json" ):
219+ if file_path == path or file_path .endswith ("item.json" ):
172220 continue # skip the item folder and item.json meta data file
173221
174222 size = content ['Size' ]
@@ -186,17 +234,23 @@ def _dir2item_s3(s3_client, bucket_name, path, item_name, skip_cert):
186234 child_item_name = file_name [:extension_index ]
187235 # note: it is not necessary to create a child iso item folder if not exist
188236 item_path = item_name + "/" + child_item_name
189- items_jsons [child_item_name ] = _make_item (item_path , vcsp_type , child_item_name , [file_json ], identifier = uuid .uuid4 ())
237+ items_json [child_item_name ] = _make_item (item_path , vcsp_type , child_item_name , [file_json ], identifier = uuid .uuid4 ())
190238 else :
191239 if vcsp_type == VCSP_TYPE_OVF and file_name .endswith (FILE_EXTENSION_CERT ) and skip_cert :
192240 # skip adding cert file if skip_cert is true
193241 continue
194242 files_items .append (file_json )
195243
196244 if vcsp_type != VCSP_TYPE_ISO :
197- items_jsons [item_name ] = _make_item (item_name , vcsp_type , item_name , files_items , identifier = uuid .uuid4 ())
245+ identifier = uuid .uuid4 ()
246+ if old_item != "" :
247+ identifier = old_item ["id" ]
248+ items_json [item_name ] = _make_item (item_name , vcsp_type , item_name , files_items , identifier = identifier , library_id = lib_id , is_vapp_template = is_vapp )
249+ if vcsp_type == VCSP_TYPE_OVF and "type-metadata" in old_item :
250+ # TODO: avoid other item metadata update
251+ items_json [item_name ]["metadata" ] = old_item ["metadata" ]
198252
199- return items_jsons
253+ return items_json
200254
201255
202256def make_vcsp (lib_name , lib_path , md5_enabled ):
@@ -241,21 +295,21 @@ def make_vcsp(lib_name, lib_path, md5_enabled):
241295 p = os .path .join (lib_path , item_path )
242296 if not os .path .isdir (p ):
243297 continue # not interesting
244- items_json = _dir2item (p , item_path , md5_enabled )
298+ item_json = _dir2item (p , item_path , md5_enabled , "urn:uuid:%s" % lib_id )
245299 if item_path not in old_items and updating_lib :
246300 changed = True
247301 elif item_path in old_items :
248302 file_changed = False
249- items_json ["id" ] = old_items [item_path ]["id" ]
250- items_json ["created" ] = old_items [item_path ]["created" ]
251- items_json ["version" ] = old_items [item_path ]["version" ]
252- file_names = set ([i ["name" ] for i in items_json ["files" ]])
303+ item_json ["id" ] = old_items [item_path ]["id" ]
304+ item_json ["created" ] = old_items [item_path ]["created" ]
305+ item_json ["version" ] = old_items [item_path ]["version" ]
306+ file_names = set ([i ["name" ] for i in item_json ["files" ]])
253307 old_file_names = set ([i ["name" ] for i in old_items [item_path ]["files" ]])
254308 if file_names != old_file_names :
255309 # files added or removed
256310 changed = True
257311 file_changed = True
258- for f in items_json ["files" ]:
312+ for f in item_json ["files" ]:
259313 if file_changed :
260314 break
261315 for old_f in old_items [item_path ]["files" ]:
@@ -264,13 +318,13 @@ def make_vcsp(lib_name, lib_path, md5_enabled):
264318 file_changed = True
265319 break
266320 if file_changed :
267- item_version = int (items_json ["version" ])
268- items_json ["version" ] = str (item_version + 1 )
321+ item_version = int (item_json ["version" ])
322+ item_json ["version" ] = str (item_version + 1 )
269323 del old_items [item_path ]
270324 json_item_file = '' .join ((p , os .sep , ITEM_FILE ))
271325 with open (json_item_file , "w" ) as f :
272- json .dump (items_json , f , indent = 2 )
273- items .append (items_json )
326+ json .dump (item_json , f , indent = 2 )
327+ items .append (item_json )
274328
275329 if updating_lib and len (old_items ) != 0 :
276330 changed = True # items were removed
@@ -361,36 +415,43 @@ def make_vcsp_s3(lib_name, lib_path, skip_cert):
361415
362416 items = []
363417 changed = False
418+ update_items_json = False
364419
365420 response = s3_client .list_objects_v2 (Bucket = bucket_name , Prefix = lib_folder_path , Delimiter = "/" )
366421 if response ['CommonPrefixes' ]:
367422 # skip items generation if no child folders
368423 for child in response ['CommonPrefixes' ]:
369424 p = child ['Prefix' ]
370425 item_path = p .split ("/" )[- 2 ]
371- items_jsons = _dir2item_s3 (s3_client , bucket_name , p , item_path , skip_cert )
426+ old_item = ""
427+ if item_path in old_items :
428+ old_item = old_items [item_path ]
429+ items_json = _dir2item_s3 (s3_client , bucket_name , p , item_path , skip_cert , "urn:uuid:%s" % lib_id , old_item )
372430
373- for item_path , items_json in items_jsons .items ():
374- items_json ["contentVersion" ] = '2' # default to content version 2
431+ for item_path , item_json in items_json .items ():
432+ item_json ["contentVersion" ] = '2' # default to content version 2
375433 if item_path not in old_items and updating_lib :
376434 changed = True
377435 elif item_path in old_items :
378436 file_changed = False
379- items_json ["id" ] = old_items [item_path ]["id" ]
380- items_json ["created" ] = old_items [item_path ]["created" ]
381- items_json ["version" ] = old_items [item_path ]["version" ]
437+ item_json ["id" ] = old_items [item_path ]["id" ]
438+ item_json ["created" ] = old_items [item_path ]["created" ]
439+ item_json ["version" ] = old_items [item_path ]["version" ]
382440 if "contentVersion" in old_items [item_path ]:
383- items_json ["contentVersion" ] = old_items [item_path ]["contentVersion" ]
441+ item_json ["contentVersion" ] = old_items [item_path ]["contentVersion" ]
384442 else :
385443 changed = True
386444
387- file_names = set ([i ["name" ] for i in items_json ["files" ]])
445+ if "type-metadata" not in str (old_items [item_path ]) and "type-metadata" in str (item_json ):
446+ update_items_json = True
447+
448+ file_names = set ([i ["name" ] for i in item_json ["files" ]])
388449 old_file_names = set ([i ["name" ] for i in old_items [item_path ]["files" ]])
389450 if file_names != old_file_names :
390451 # files added or removed
391452 changed = True
392453 file_changed = True
393- for f in items_json ["files" ]:
454+ for f in item_json ["files" ]:
394455 if file_changed :
395456 break
396457 for old_f in old_items [item_path ]["files" ]:
@@ -400,15 +461,15 @@ def make_vcsp_s3(lib_name, lib_path, skip_cert):
400461 break
401462 if file_changed :
402463 # bump up version and content version
403- item_version = int (items_json ["version" ])
404- items_json ["version" ] = str (item_version + 1 )
405- item_content_version = int (items_json ["contentVersion" ])
406- items_json ["contentVersion" ] = str (item_content_version + 1 )
464+ item_version = int (item_json ["version" ])
465+ item_json ["version" ] = str (item_version + 1 )
466+ item_content_version = int (item_json ["contentVersion" ])
467+ item_json ["contentVersion" ] = str (item_content_version + 1 )
407468 del old_items [item_path ]
408- json_item_file = lib_folder_path + items_json ['selfHref' ]
469+ json_item_file = lib_folder_path + item_json ['selfHref' ]
409470 obj = s3 .Object (bucket_name , json_item_file )
410- obj .put (Body = json .dumps (items_json , indent = 2 ))
411- items .append (items_json )
471+ obj .put (Body = json .dumps (item_json , indent = 2 ))
472+ items .append (item_json )
412473
413474 if updating_lib and len (old_items ) != 0 :
414475 changed = True # items were removed, and delete old iso item folders
@@ -424,7 +485,11 @@ def make_vcsp_s3(lib_name, lib_path, skip_cert):
424485 objects .delete ()
425486
426487 if updating_lib and not changed :
427- logger .info ("Nothing to update, quitting" )
488+ logger .info ("Nothing to update on the library" )
489+ if update_items_json :
490+ logger .info ("items json needs to be updated, updating items json..." )
491+ obj = s3 .Object (bucket_name , lib_items_json_path )
492+ obj .put (Body = json .dumps (_make_items (items , lib_version ), indent = 2 ))
428493 return
429494 if changed :
430495 lib_version = int (lib_version )
@@ -482,8 +547,8 @@ def usage():
482547 '''
483548 The usage message for the argument parser.
484549 '''
485- return """Usage: python make-vcsp-2018 .py -n <library-name> -t <storage-type:local or s3, default local> -p <library-storage-path> --etag <true or false, default true>
486- --skip-cert <true or fale, default true
550+ return """Usage: python vcsp_maker .py -n <library-name> -t <storage-type:local or s3, default local> -p <library-storage-path> --etag <true or false, default true>
551+ --skip-cert <true or fale, default true>
487552
488553 Note that s3 requires the following configurations:
489554 1. ~/.aws/config
0 commit comments