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
26 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
27 from pyvcloud
.vcd
.client
import BasicLoginCredentials
, Client
28 from pyvcloud
.vcd
.org
import Org
33 from xml
.etree
import ElementTree
as XmlElementTree
38 class vCloudconfig(object):
40 def __init__(self
, host
=None, user
=None, password
=None, orgname
=None, logger
=None):
43 self
.password
= password
45 self
.logger
= logging
.getLogger('vmware_ovf_upload')
46 self
.logger
.setLevel(10)
49 """ Method connect as normal user to vCloud director.
52 The return vca object that letter can be used to connect to vCloud director as admin for VDC
56 self
.logger
.debug("Logging in to a vcd {} as user {}".format(self
.org
,
58 client
= Client(self
.url
, verify_ssl_certs
=False)
59 client
.set_credentials(BasicLoginCredentials(self
.user
, self
.org
, self
.password
))
61 raise Exception("Can't connect to a vCloud director org: "
62 "{} as user: {}".format(self
.org
, self
.user
))
66 def get_catalog_id_from_path(self
, catalog_name
=None, path
=None, progress
=False):
69 catalog - catalog name to be created
70 path: - valid path to OVF file.
71 progress - boolean progress bar show progress bar.
73 Return: if image uploaded correct method will provide image catalog UUID.
76 raise Exception("Image path can't be None.")
78 if not os
.path
.isfile(path
):
79 raise Exception("Can't read file. File not found.")
81 if not os
.access(path
, os
.R_OK
):
82 raise Exception("Can't read file. Check file permission to read.")
84 self
.logger
.debug("get_catalog_id_from_path() client requesting {} ".format(path
))
86 _
, filename
= os
.path
.split(path
)
87 _
, file_extension
= os
.path
.splitext(path
)
88 if file_extension
!= '.ovf':
89 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
90 raise Exception("Wrong container. vCloud director supports only OVF.")
92 self
.logger
.debug("File name {} Catalog Name {} file path {} ".format(filename
,
96 client
= self
.connect()
98 raise Exception("Failed to connect vCD")
99 org
= Org(client
, resource
=client
.get_org())
100 catalogs
= org
.list_catalogs()
101 except Exception as exp
:
102 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
103 raise Exception("Failed get catalogs() with Exception {} ".format(exp
))
105 if len(catalogs
) == 0:
106 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
107 result
= org
.create_catalog(catalog_name
, catalog_name
)
109 raise Exception("Failed to create new catalog {} ".format(catalog_name
))
110 result
= self
.upload_ovf(org
=org
, catalog_name
=catalog_name
, image_name
=filename
.split(".")[0],
111 media_file_name
=path
, description
='medial_file_name', progress
=progress
)
113 raise Exception("Failed to create vApp template for catalog {} ".format(catalog_name
))
114 return self
.get_catalogid(catalog_name
, catalogs
)
116 for catalog
in catalogs
:
117 # search for existing catalog if we find same name we return ID
118 if catalog
['name'] == catalog_name
:
119 self
.logger
.debug("Found existing catalog entry for {} "
120 "catalog id {}".format(catalog_name
,
121 self
.get_catalogid(catalog_name
, catalogs
)))
122 return self
.get_catalogid(catalog_name
, catalogs
)
124 # if we didn't find existing catalog we create a new one and upload image.
125 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_name
))
126 result
= org
.create_catalog(catalog_name
, catalog_name
)
128 raise Exception("Failed to create new catalog {} ".format(catalog_name
))
130 result
= self
.upload_ovf(org
=org
, catalog_name
=catalog_name
, image_name
=filename
.split(".")[0],
131 media_file_name
=path
, description
='medial_file_name', progress
=progress
)
133 raise Exception("Failed create vApp template for catalog {} ".format(catalog_name
))
135 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
136 """ Method check catalog and return catalog ID in UUID format.
139 catalog_name: catalog name as string
140 catalogs: list of catalogs.
142 Return: catalogs uuid
145 for catalog
in catalogs
:
146 if catalog
['name'] == catalog_name
:
147 catalog_id
= catalog
['id']
151 def upload_ovf(self
, org
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
152 description
='', progress
=False, chunk_bytes
=128 * 1024):
154 Uploads a OVF file to a vCloud catalog
156 org : organization object
157 catalog_name: (str): The name of the catalog to upload the media.
158 media_file_name: (str): The name of the local media file to upload.
159 return: (bool) True if the media file was successfully uploaded, false otherwise.
161 client
= self
.connect()
163 raise Exception("Failed to connect vCD!")
165 os
.path
.isfile(media_file_name
)
166 statinfo
= os
.stat(media_file_name
)
168 # find a catalog entry where we upload OVF.
169 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
171 # if VCD can parse OVF we upload VMDK file
173 for catalog
in org
.list_catalogs():
174 if catalog_name
!= catalog
['name']:
176 catalog_href
= "{}/api/catalog/{}/action/upload".format(self
.url
, catalog
['id'])
178 <UploadVAppTemplateParams name="{}" xmlns="http://www.vmware.com/vcloud/v1.5"
179 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1">
180 <Description>{} vApp Template</Description></UploadVAppTemplateParams>
181 """.format(catalog_name
, description
)
184 headers
= {'Accept': 'application/*+xml;version=' + API_VERSION
,
185 'x-vcloud-authorization': client
._session
.headers
['x-vcloud-authorization']}
186 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
188 response
= requests
.post(url
=catalog_href
,
192 if response
.status_code
!= 201:
193 self
.logger
.debug("Failed to create vApp template")
194 raise Exception("Failed to create vApp template")
196 if response
.status_code
== requests
.codes
.created
:
197 catalogItem
= XmlElementTree
.fromstring(response
.content
)
198 entity
= [child
for child
in catalogItem
if
199 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
200 href
= entity
.get('href')
203 response
= requests
.get(url
=href
,
206 if response
.status_code
== requests
.codes
.ok
:
207 headers
['Content-Type'] = 'Content-Type text/xml'
208 result
= re
.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"', response
.content
)
210 transfer_href
= result
.group(1)
212 response
= requests
.put(url
=transfer_href
, headers
=headers
,
213 data
=open(media_file_name
, 'rb'),
216 if response
.status_code
!= requests
.codes
.ok
:
218 "Failed create vApp template for catalog name {} and image {}".format(
223 # TODO fix this with aync block
226 self
.logger
.debug("vApp template for catalog name {} and image {}".format(
230 # uploading VMDK file
231 # check status of OVF upload and upload remaining files.
233 response
= requests
.get(url
=template
,
237 if response
.status_code
== requests
.codes
.ok
:
238 result
= re
.search('rel="upload:default"\s*href="(.*?vmdk)"', response
.content
)
240 link_href
= result
.group(1)
241 # we skip ovf since it already uploaded.
242 if 'ovf' in link_href
:
244 # The OVF file and VMDK must be in a same directory
245 head
, _
= os
.path
.split(media_file_name
)
246 file_vmdk
= head
+ '/' + link_href
.split("/")[-1]
247 if not os
.path
.isfile(file_vmdk
):
249 statinfo
= os
.stat(file_vmdk
)
250 if statinfo
.st_size
== 0:
254 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
256 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
258 bytes_transferred
= 0
259 f
= open(file_vmdk
, 'rb')
260 while bytes_transferred
< statinfo
.st_size
:
261 my_bytes
= f
.read(chunk_bytes
)
262 if len(my_bytes
) <= chunk_bytes
:
263 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
264 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
265 headers
['Content-Length'] = str(len(my_bytes
))
266 response
= requests
.put(url
=hrefvmdk
,
270 if response
.status_code
== requests
.codes
.ok
:
271 bytes_transferred
+= len(my_bytes
)
273 progress_bar
.update(bytes_transferred
)
276 'file upload failed with error: [%s] %s' % (response
.status_code
,
283 progress_bar
.finish()
287 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
288 format(catalog_name
, media_file_name
))
290 except Exception as exp
:
291 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
292 .format(catalog_name
, media_file_name
, exp
))
294 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
295 .format(catalog_name
, media_file_name
, exp
))
296 self
.logger
.debug("Failed to retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
300 if __name__
== "__main__":
302 print("This file is deprecated. Please use ovf_uplader_cli instead.")
304 # vmware vcloud director credentials
305 vcd_hostname
= sys
.argv
[1]
306 vcd_username
= sys
.argv
[2]
307 vcd_password
= sys
.argv
[3]
308 orgname
= sys
.argv
[4]
309 # OVF image path to be upload to vCD
310 ovf_file_path
= sys
.argv
[5]
312 logging
.basicConfig(filename
='ovf_upload.log', level
=logging
.DEBUG
)
313 logger
= logging
.getLogger(__name__
)
315 obj
= vCloudconfig(vcd_hostname
, vcd_username
, vcd_password
, orgname
, logger
)
317 dirpath
, filename
= os
.path
.split(ovf_file_path
)
318 filename_name
, file_extension
= os
.path
.splitext(filename
)
320 # Get image name from cirros vnfd
321 cirros_yaml
= '../descriptor-packages/vnfd/cirros_vnf/src/cirros_vnfd.yaml'
322 rh
= open(cirros_yaml
, 'r')
323 match
= re
.search("image:\s'(.*?)'\n", rh
.read())
325 catalog
= match
.group(1)
327 if file_extension
== '.ovf':
328 obj
.get_catalog_id_from_path(catalog_name
=catalog
, path
=ovf_file_path
,