1 # -*- coding: utf-8 -*-
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: osslegalrouting@vmware.com
25 from xml
.etree
import ElementTree
as XmlElementTree
26 from pyvcloud
.vcd
.client
import BasicLoginCredentials
,Client
27 from pyvcloud
.vcd
.vdc
import VDC
28 from pyvcloud
.vcd
.org
import Org
35 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
40 class vCloudconfig(object):
41 def __init__(self
, host
=None, user
=None, password
=None,orgname
=None, logger
=None):
44 self
.password
= password
46 self
.logger
= logging
.getLogger('vmware_ovf_upload')
47 self
.logger
.setLevel(10)
50 """ Method connect as normal user to vCloud director.
53 The return vca object that letter can be used to connect to vCloud director as admin for VDC
57 self
.logger
.debug("Logging in to a vcd {} as user {}".format(self
.org
,
59 client
= Client(self
.url
, verify_ssl_certs
=False)
60 client
.set_credentials(BasicLoginCredentials(self
.user
, self
.org
, self
.password
))
62 raise Exception("Can't connect to a vCloud director org: "
63 "{} as user: {}".format(self
.org
, self
.user
))
67 def get_catalog_id_from_path(self
, catalog_name
=None, path
=None, progress
=False):
70 catalog - catalog name to be created
71 path: - valid path to OVF file.
72 progress - boolean progress bar show progress bar.
74 Return: if image uploaded correct method will provide image catalog UUID.
77 raise Exception("Image path can't be None.")
79 if not os
.path
.isfile(path
):
80 raise Exception("Can't read file. File not found.")
82 if not os
.access(path
, os
.R_OK
):
83 raise Exception("Can't read file. Check file permission to read.")
85 self
.logger
.debug("get_catalog_id_from_path() client requesting {} ".format(path
))
87 dirpath
, filename
= os
.path
.split(path
)
88 flname
, file_extension
= os
.path
.splitext(path
)
89 if file_extension
!= '.ovf':
90 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
91 raise Exception("Wrong container. vCloud director supports only OVF.")
93 self
.logger
.debug("File name {} Catalog Name {} file path {} ".format(filename
,
97 client
= self
.connect()
99 raise Exception("Failed to connect vCD")
100 org
= Org(client
, resource
=client
.get_org())
101 catalogs
= org
.list_catalogs()
102 except Exception as exp
:
103 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
104 raise Exception("Failed get catalogs() with Exception {} ".format(exp
))
106 if len(catalogs
) == 0:
107 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
108 result
= org
.create_catalog(catalog_name
, catalog_name
)
110 raise Exception("Failed to create new catalog {} ".format(catalog_name
))
111 result
= self
.upload_ovf(org
=org
, catalog_name
=catalog_name
, image_name
=filename
.split(".")[0],
112 media_file_name
=path
, description
='medial_file_name', progress
=progress
)
114 raise Exception("Failed to create vApp template for catalog {} ".format(catalog_name
))
115 return self
.get_catalogid(catalog_name
, catalogs
)
117 for catalog
in catalogs
:
118 # search for existing catalog if we find same name we return ID
119 if catalog
['name'] == catalog_name
:
120 self
.logger
.debug("Found existing catalog entry for {} "
121 "catalog id {}".format(catalog_name
,
122 self
.get_catalogid(catalog_name
, catalogs
)))
123 return self
.get_catalogid(catalog_name
, catalogs
)
125 # if we didn't find existing catalog we create a new one and upload image.
126 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_name
))
127 result
= org
.create_catalog(catalog_name
, catalog_name
)
129 raise Exception("Failed to create new catalog {} ".format(catalog_name
))
131 result
= self
.upload_ovf(org
=org
, catalog_name
=catalog_name
, image_name
=filename
.split(".")[0],
132 media_file_name
=path
, description
='medial_file_name', progress
=progress
)
134 raise Exception("Failed create vApp template for catalog {} ".format(catalog_name
))
136 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
137 """ Method check catalog and return catalog ID in UUID format.
140 catalog_name: catalog name as string
141 catalogs: list of catalogs.
143 Return: catalogs uuid
146 for catalog
in catalogs
:
147 if catalog
['name'] == catalog_name
:
148 catalog_id
= catalog
['id']
152 def upload_ovf(self
, org
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
153 description
='', progress
=False, chunk_bytes
=128 * 1024):
155 Uploads a OVF file to a vCloud catalog
157 org : organization object
158 catalog_name: (str): The name of the catalog to upload the media.
159 media_file_name: (str): The name of the local media file to upload.
160 return: (bool) True if the media file was successfully uploaded, false otherwise.
162 client
= self
.connect()
164 raise Exception("Failed to connect vCD!")
166 os
.path
.isfile(media_file_name
)
167 statinfo
= os
.stat(media_file_name
)
169 # find a catalog entry where we upload OVF.
170 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
172 # if VCD can parse OVF we upload VMDK file
174 for catalog
in org
.list_catalogs():
175 if catalog_name
!= catalog
['name']:
177 catalog_href
= "{}/api/catalog/{}/action/upload".format(self
.url
, catalog
['id'])
179 <UploadVAppTemplateParams name="{}" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>{} vApp Template</Description></UploadVAppTemplateParams>
180 """.format(catalog_name
, description
)
183 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
184 'x-vcloud-authorization': client
._session
.headers
['x-vcloud-authorization']}
185 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
187 response
= requests
.post(url
=catalog_href
,
191 if response
.status_code
!= 201:
192 self
.logger
.debug("Failed to create vApp template")
193 raise Exception("Failed to create vApp template")
195 if response
.status_code
== requests
.codes
.created
:
196 catalogItem
= XmlElementTree
.fromstring(response
.content
)
197 entity
= [child
for child
in catalogItem
if
198 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
199 href
= entity
.get('href')
202 response
= requests
.get(url
=href
,
205 if response
.status_code
== requests
.codes
.ok
:
206 headers
['Content-Type'] = 'Content-Type text/xml'
207 result
= re
.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"',response
.content
)
209 transfer_href
= result
.group(1)
211 response
= requests
.put(url
=transfer_href
, headers
=headers
,
212 data
=open(media_file_name
, 'rb'),
215 if response
.status_code
!= requests
.codes
.ok
:
217 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
221 # TODO fix this with aync block
224 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
226 # uploading VMDK file
227 # check status of OVF upload and upload remaining files.
229 response
= requests
.get(url
=template
,
233 if response
.status_code
== requests
.codes
.ok
:
234 result
= re
.search('rel="upload:default"\s*href="(.*?vmdk)"',response
.content
)
236 link_href
= result
.group(1)
237 # we skip ovf since it already uploaded.
238 if 'ovf' in link_href
:
240 # The OVF file and VMDK must be in a same directory
241 head
, tail
= os
.path
.split(media_file_name
)
242 file_vmdk
= head
+ '/' + link_href
.split("/")[-1]
243 if not os
.path
.isfile(file_vmdk
):
245 statinfo
= os
.stat(file_vmdk
)
246 if statinfo
.st_size
== 0:
250 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
252 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
254 bytes_transferred
= 0
255 f
= open(file_vmdk
, 'rb')
256 while bytes_transferred
< statinfo
.st_size
:
257 my_bytes
= f
.read(chunk_bytes
)
258 if len(my_bytes
) <= chunk_bytes
:
259 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
260 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
261 headers
['Content-Length'] = str(len(my_bytes
))
262 response
= requests
.put(url
=hrefvmdk
,
266 if response
.status_code
== requests
.codes
.ok
:
267 bytes_transferred
+= len(my_bytes
)
269 progress_bar
.update(bytes_transferred
)
272 'file upload failed with error: [%s] %s' % (response
.status_code
,
279 progress_bar
.finish()
283 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
284 format(catalog_name
, media_file_name
))
286 except Exception as exp
:
287 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
288 .format(catalog_name
,media_file_name
, exp
))
290 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
291 .format(catalog_name
,media_file_name
, exp
))
292 self
.logger
.debug("Failed to retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
295 if __name__
== "__main__":
297 # vmware vcloud director credentials
298 vcd_hostname
= sys
.argv
[1]
299 vcd_username
= sys
.argv
[2]
300 vcd_password
= sys
.argv
[3]
301 orgname
= sys
.argv
[4]
302 # OVF image path to be upload to vCD
303 ovf_file_path
= sys
.argv
[5]
305 # changing virtual system type in ovf file
306 fh
= open(ovf_file_path
,'r')
308 content
= content
.replace('<vssd:VirtualSystemType>vmx-7','<vssd:VirtualSystemType>vmx-07')
310 fh1
= open(ovf_file_path
,'w')
315 logging
.basicConfig(filename
='ovf_upload.log',level
=logging
.DEBUG
)
316 logger
= logging
.getLogger(__name__
)
318 obj
= vCloudconfig(vcd_hostname
, vcd_username
, vcd_password
, orgname
, logger
)
320 dirpath
, filename
= os
.path
.split(ovf_file_path
)
321 filename_name
, file_extension
= os
.path
.splitext(filename
)
323 # Get image name from cirros vnfd
324 cirros_yaml
= '../descriptor-packages/vnfd/cirros_vnf/src/cirros_vnfd.yaml'
325 rh
= open(cirros_yaml
,'r')
326 match
= re
.search("image:\s'(.*?)'\n",rh
.read())
327 if match
: catalog
= match
.group(1)
329 if file_extension
== '.ovf':
330 obj
.get_catalog_id_from_path(catalog_name
=catalog
, path
=ovf_file_path
,