blob: 834068de6501dcd717a42391f94141ee4a8b2f01 [file] [log] [blame]
kasar12aa2cf2018-02-02 02:51:07 -08001# -*- coding: utf-8 -*-
2
beierlc6b00f02019-10-07 13:09:24 -04003# #
kasar12aa2cf2018-02-02 02:51:07 -08004# Copyright 2016-2017 VMware Inc.
5# This file is part of ETSI OSM
6# All Rights Reserved.
7#
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
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
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
18# under the License.
19#
20# For those usages not covered by the Apache License, Version 2.0 please
21# contact: osslegalrouting@vmware.com
beierlc6b00f02019-10-07 13:09:24 -040022# #
kasar12aa2cf2018-02-02 02:51:07 -080023
kasar12aa2cf2018-02-02 02:51:07 -080024import logging
beierlc6b00f02019-10-07 13:09:24 -040025import os
kasara4347782018-11-01 04:30:50 -070026from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
beierlc6b00f02019-10-07 13:09:24 -040027from pyvcloud.vcd.client import BasicLoginCredentials, Client
28from pyvcloud.vcd.org import Org
29import re
30import requests
31import sys
32import time
33from xml.etree import ElementTree as XmlElementTree
kasar12aa2cf2018-02-02 02:51:07 -080034
kasara4347782018-11-01 04:30:50 -070035API_VERSION = '5.6'
kasar12aa2cf2018-02-02 02:51:07 -080036
beierlc6b00f02019-10-07 13:09:24 -040037
kasar12aa2cf2018-02-02 02:51:07 -080038class vCloudconfig(object):
beierlc6b00f02019-10-07 13:09:24 -040039
40 def __init__(self, host=None, user=None, password=None, orgname=None, logger=None):
kasar12aa2cf2018-02-02 02:51:07 -080041 self.url = host
42 self.user = user
43 self.password = password
44 self.org = orgname
kasara4347782018-11-01 04:30:50 -070045 self.logger = logging.getLogger('vmware_ovf_upload')
46 self.logger.setLevel(10)
kasar12aa2cf2018-02-02 02:51:07 -080047
48 def connect(self):
49 """ Method connect as normal user to vCloud director.
50
51 Returns:
52 The return vca object that letter can be used to connect to vCloud director as admin for VDC
53 """
54
55 try:
kasara4347782018-11-01 04:30:50 -070056 self.logger.debug("Logging in to a vcd {} as user {}".format(self.org,
beierlc6b00f02019-10-07 13:09:24 -040057 self.user))
kasara4347782018-11-01 04:30:50 -070058 client = Client(self.url, verify_ssl_certs=False)
59 client.set_credentials(BasicLoginCredentials(self.user, self.org, self.password))
beierlc6b00f02019-10-07 13:09:24 -040060 except Exception:
kasara4347782018-11-01 04:30:50 -070061 raise Exception("Can't connect to a vCloud director org: "
beierlc6b00f02019-10-07 13:09:24 -040062 "{} as user: {}".format(self.org, self.user))
kasar12aa2cf2018-02-02 02:51:07 -080063
kasara4347782018-11-01 04:30:50 -070064 return client
kasar12aa2cf2018-02-02 02:51:07 -080065
kasara4347782018-11-01 04:30:50 -070066 def get_catalog_id_from_path(self, catalog_name=None, path=None, progress=False):
67 """
68 Args
69 catalog - catalog name to be created
70 path: - valid path to OVF file.
71 progress - boolean progress bar show progress bar.
72
73 Return: if image uploaded correct method will provide image catalog UUID.
74 """
75 if not path:
76 raise Exception("Image path can't be None.")
77
78 if not os.path.isfile(path):
79 raise Exception("Can't read file. File not found.")
80
81 if not os.access(path, os.R_OK):
82 raise Exception("Can't read file. Check file permission to read.")
83
84 self.logger.debug("get_catalog_id_from_path() client requesting {} ".format(path))
85
beierlc6b00f02019-10-07 13:09:24 -040086 _, filename = os.path.split(path)
87 _, file_extension = os.path.splitext(path)
kasara4347782018-11-01 04:30:50 -070088 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.")
91
92 self.logger.debug("File name {} Catalog Name {} file path {} ".format(filename,
beierlc6b00f02019-10-07 13:09:24 -040093 catalog_name,
94 path))
kasara4347782018-11-01 04:30:50 -070095 try:
96 client = self.connect()
97 if not client:
beierlc6b00f02019-10-07 13:09:24 -040098 raise Exception("Failed to connect vCD")
kasara4347782018-11-01 04:30:50 -070099 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))
104
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)
108 if result is None:
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],
beierlc6b00f02019-10-07 13:09:24 -0400111 media_file_name=path, description='medial_file_name', progress=progress)
kasara4347782018-11-01 04:30:50 -0700112 if not result:
113 raise Exception("Failed to create vApp template for catalog {} ".format(catalog_name))
114 return self.get_catalogid(catalog_name, catalogs)
115 else:
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 {} "
beierlc6b00f02019-10-07 13:09:24 -0400120 "catalog id {}".format(catalog_name,
121 self.get_catalogid(catalog_name, catalogs)))
kasara4347782018-11-01 04:30:50 -0700122 return self.get_catalogid(catalog_name, catalogs)
123
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))
beierlc6b00f02019-10-07 13:09:24 -0400126 result = org.create_catalog(catalog_name, catalog_name)
kasara4347782018-11-01 04:30:50 -0700127 if result is None:
128 raise Exception("Failed to create new catalog {} ".format(catalog_name))
129
130 result = self.upload_ovf(org=org, catalog_name=catalog_name, image_name=filename.split(".")[0],
beierlc6b00f02019-10-07 13:09:24 -0400131 media_file_name=path, description='medial_file_name', progress=progress)
kasara4347782018-11-01 04:30:50 -0700132 if not result:
133 raise Exception("Failed create vApp template for catalog {} ".format(catalog_name))
134
135 def get_catalogid(self, catalog_name=None, catalogs=None):
136 """ Method check catalog and return catalog ID in UUID format.
137
138 Args
139 catalog_name: catalog name as string
140 catalogs: list of catalogs.
141
142 Return: catalogs uuid
143 """
144
145 for catalog in catalogs:
146 if catalog['name'] == catalog_name:
147 catalog_id = catalog['id']
148 return catalog_id
149 return None
150
151 def upload_ovf(self, org=None, catalog_name=None, image_name=None, media_file_name=None,
kasar12aa2cf2018-02-02 02:51:07 -0800152 description='', progress=False, chunk_bytes=128 * 1024):
153 """
154 Uploads a OVF file to a vCloud catalog
155
kasara4347782018-11-01 04:30:50 -0700156 org : organization object
kasar12aa2cf2018-02-02 02:51:07 -0800157 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.
160 """
kasara4347782018-11-01 04:30:50 -0700161 client = self.connect()
162 if not client:
163 raise Exception("Failed to connect vCD!")
kasar12aa2cf2018-02-02 02:51:07 -0800164
165 os.path.isfile(media_file_name)
166 statinfo = os.stat(media_file_name)
167
kasar12aa2cf2018-02-02 02:51:07 -0800168 # 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
170 # status change.
171 # if VCD can parse OVF we upload VMDK file
172 try:
kasara4347782018-11-01 04:30:50 -0700173 for catalog in org.list_catalogs():
174 if catalog_name != catalog['name']:
kasar12aa2cf2018-02-02 02:51:07 -0800175 continue
kasara4347782018-11-01 04:30:50 -0700176 catalog_href = "{}/api/catalog/{}/action/upload".format(self.url, catalog['id'])
kasar12aa2cf2018-02-02 02:51:07 -0800177 data = """
beierlc6b00f02019-10-07 13:09:24 -0400178 <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>
kasara4347782018-11-01 04:30:50 -0700181 """.format(catalog_name, description)
182
183 if client:
beierlc6b00f02019-10-07 13:09:24 -0400184 headers = {'Accept': 'application/*+xml;version=' + API_VERSION,
185 'x-vcloud-authorization': client._session.headers['x-vcloud-authorization']}
kasara4347782018-11-01 04:30:50 -0700186 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
187
188 response = requests.post(url=catalog_href,
beierlc6b00f02019-10-07 13:09:24 -0400189 headers=headers,
190 data=data,
191 verify=False)
kasara4347782018-11-01 04:30:50 -0700192 if response.status_code != 201:
193 self.logger.debug("Failed to create vApp template")
beierlc6b00f02019-10-07 13:09:24 -0400194 raise Exception("Failed to create vApp template")
kasara4347782018-11-01 04:30:50 -0700195
kasar12aa2cf2018-02-02 02:51:07 -0800196 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')
201 template = href
kasar12aa2cf2018-02-02 02:51:07 -0800202
kasara4347782018-11-01 04:30:50 -0700203 response = requests.get(url=href,
beierlc6b00f02019-10-07 13:09:24 -0400204 headers=headers,
205 verify=False)
kasar12aa2cf2018-02-02 02:51:07 -0800206 if response.status_code == requests.codes.ok:
kasar12aa2cf2018-02-02 02:51:07 -0800207 headers['Content-Type'] = 'Content-Type text/xml'
beierlc6b00f02019-10-07 13:09:24 -0400208 result = re.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"', response.content)
kasara4347782018-11-01 04:30:50 -0700209 if result:
210 transfer_href = result.group(1)
211
212 response = requests.put(url=transfer_href, headers=headers,
beierlc6b00f02019-10-07 13:09:24 -0400213 data=open(media_file_name, 'rb'),
214 verify=False)
kasara4347782018-11-01 04:30:50 -0700215
kasar12aa2cf2018-02-02 02:51:07 -0800216 if response.status_code != requests.codes.ok:
217 self.logger.debug(
beierlc6b00f02019-10-07 13:09:24 -0400218 "Failed create vApp template for catalog name {} and image {}".format(
219 catalog_name,
220 media_file_name))
kasar12aa2cf2018-02-02 02:51:07 -0800221 return False
222
223 # TODO fix this with aync block
224 time.sleep(5)
225
beierlc6b00f02019-10-07 13:09:24 -0400226 self.logger.debug("vApp template for catalog name {} and image {}".format(
227 catalog_name,
228 media_file_name))
kasar12aa2cf2018-02-02 02:51:07 -0800229
230 # uploading VMDK file
231 # check status of OVF upload and upload remaining files.
kasara4347782018-11-01 04:30:50 -0700232
233 response = requests.get(url=template,
beierlc6b00f02019-10-07 13:09:24 -0400234 headers=headers,
kasara4347782018-11-01 04:30:50 -0700235 verify=False)
kasar12aa2cf2018-02-02 02:51:07 -0800236
237 if response.status_code == requests.codes.ok:
beierlc6b00f02019-10-07 13:09:24 -0400238 result = re.search('rel="upload:default"\s*href="(.*?vmdk)"', response.content)
kasara4347782018-11-01 04:30:50 -0700239 if result:
240 link_href = result.group(1)
241 # we skip ovf since it already uploaded.
242 if 'ovf' in link_href:
243 continue
244 # The OVF file and VMDK must be in a same directory
beierlc6b00f02019-10-07 13:09:24 -0400245 head, _ = os.path.split(media_file_name)
kasara4347782018-11-01 04:30:50 -0700246 file_vmdk = head + '/' + link_href.split("/")[-1]
247 if not os.path.isfile(file_vmdk):
248 return False
249 statinfo = os.stat(file_vmdk)
250 if statinfo.st_size == 0:
251 return False
252 hrefvmdk = link_href
253 if progress:
254 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
beierlc6b00f02019-10-07 13:09:24 -0400255 FileTransferSpeed()]
kasara4347782018-11-01 04:30:50 -0700256 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
257
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,
beierlc6b00f02019-10-07 13:09:24 -0400267 headers=headers,
268 data=my_bytes,
269 verify=False)
kasara4347782018-11-01 04:30:50 -0700270 if response.status_code == requests.codes.ok:
271 bytes_transferred += len(my_bytes)
272 if progress:
273 progress_bar.update(bytes_transferred)
274 else:
275 self.logger.debug(
276 'file upload failed with error: [%s] %s' % (response.status_code,
beierlc6b00f02019-10-07 13:09:24 -0400277 response.content))
kasara4347782018-11-01 04:30:50 -0700278
279 f.close()
kasar12aa2cf2018-02-02 02:51:07 -0800280 return False
kasara4347782018-11-01 04:30:50 -0700281 f.close()
282 if progress:
283 progress_bar.finish()
284 time.sleep(60)
285 return True
286 else:
287 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
288 format(catalog_name, media_file_name))
beierlc6b00f02019-10-07 13:09:24 -0400289 return False
kasar12aa2cf2018-02-02 02:51:07 -0800290 except Exception as exp:
291 self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
beierlc6b00f02019-10-07 13:09:24 -0400292 .format(catalog_name, media_file_name, exp))
kasara4347782018-11-01 04:30:50 -0700293 raise Exception(
294 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
beierlc6b00f02019-10-07 13:09:24 -0400295 .format(catalog_name, media_file_name, exp))
kasara4347782018-11-01 04:30:50 -0700296 self.logger.debug("Failed to retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
beierlc6b00f02019-10-07 13:09:24 -0400297 return False
298
kasar12aa2cf2018-02-02 02:51:07 -0800299
300if __name__ == "__main__":
301
beierlc6b00f02019-10-07 13:09:24 -0400302 print("This file is deprecated. Please use ovf_uplader_cli instead.")
303
kasar12aa2cf2018-02-02 02:51:07 -0800304 # 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]
311
beierlc6b00f02019-10-07 13:09:24 -0400312 logging.basicConfig(filename='ovf_upload.log', level=logging.DEBUG)
kasar12aa2cf2018-02-02 02:51:07 -0800313 logger = logging.getLogger(__name__)
314
315 obj = vCloudconfig(vcd_hostname, vcd_username, vcd_password, orgname, logger)
316
317 dirpath, filename = os.path.split(ovf_file_path)
318 filename_name, file_extension = os.path.splitext(filename)
319
320 # Get image name from cirros vnfd
321 cirros_yaml = '../descriptor-packages/vnfd/cirros_vnf/src/cirros_vnfd.yaml'
beierlc6b00f02019-10-07 13:09:24 -0400322 rh = open(cirros_yaml, 'r')
323 match = re.search("image:\s'(.*?)'\n", rh.read())
324 if match:
325 catalog = match.group(1)
kasar12aa2cf2018-02-02 02:51:07 -0800326
kasar12aa2cf2018-02-02 02:51:07 -0800327 if file_extension == '.ovf':
kasara4347782018-11-01 04:30:50 -0700328 obj.get_catalog_id_from_path(catalog_name=catalog, path=ovf_file_path,
beierlc6b00f02019-10-07 13:09:24 -0400329 progress=True)