blob: 834068de6501dcd717a42391f94141ee4a8b2f01 [file] [log] [blame]
# -*- coding: utf-8 -*-
# #
# Copyright 2016-2017 VMware Inc.
# This file is part of ETSI OSM
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact: osslegalrouting@vmware.com
# #
import logging
import os
from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
from pyvcloud.vcd.client import BasicLoginCredentials, Client
from pyvcloud.vcd.org import Org
import re
import requests
import sys
import time
from xml.etree import ElementTree as XmlElementTree
API_VERSION = '5.6'
class vCloudconfig(object):
def __init__(self, host=None, user=None, password=None, orgname=None, logger=None):
self.url = host
self.user = user
self.password = password
self.org = orgname
self.logger = logging.getLogger('vmware_ovf_upload')
self.logger.setLevel(10)
def connect(self):
""" Method connect as normal user to vCloud director.
Returns:
The return vca object that letter can be used to connect to vCloud director as admin for VDC
"""
try:
self.logger.debug("Logging in to a vcd {} as user {}".format(self.org,
self.user))
client = Client(self.url, verify_ssl_certs=False)
client.set_credentials(BasicLoginCredentials(self.user, self.org, self.password))
except Exception:
raise Exception("Can't connect to a vCloud director org: "
"{} as user: {}".format(self.org, self.user))
return client
def get_catalog_id_from_path(self, catalog_name=None, path=None, progress=False):
"""
Args
catalog - catalog name to be created
path: - valid path to OVF file.
progress - boolean progress bar show progress bar.
Return: if image uploaded correct method will provide image catalog UUID.
"""
if not path:
raise Exception("Image path can't be None.")
if not os.path.isfile(path):
raise Exception("Can't read file. File not found.")
if not os.access(path, os.R_OK):
raise Exception("Can't read file. Check file permission to read.")
self.logger.debug("get_catalog_id_from_path() client requesting {} ".format(path))
_, filename = os.path.split(path)
_, file_extension = os.path.splitext(path)
if file_extension != '.ovf':
self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
raise Exception("Wrong container. vCloud director supports only OVF.")
self.logger.debug("File name {} Catalog Name {} file path {} ".format(filename,
catalog_name,
path))
try:
client = self.connect()
if not client:
raise Exception("Failed to connect vCD")
org = Org(client, resource=client.get_org())
catalogs = org.list_catalogs()
except Exception as exp:
self.logger.debug("Failed get catalogs() with Exception {} ".format(exp))
raise Exception("Failed get catalogs() with Exception {} ".format(exp))
if len(catalogs) == 0:
self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
result = org.create_catalog(catalog_name, catalog_name)
if result is None:
raise Exception("Failed to create new catalog {} ".format(catalog_name))
result = self.upload_ovf(org=org, catalog_name=catalog_name, image_name=filename.split(".")[0],
media_file_name=path, description='medial_file_name', progress=progress)
if not result:
raise Exception("Failed to create vApp template for catalog {} ".format(catalog_name))
return self.get_catalogid(catalog_name, catalogs)
else:
for catalog in catalogs:
# search for existing catalog if we find same name we return ID
if catalog['name'] == catalog_name:
self.logger.debug("Found existing catalog entry for {} "
"catalog id {}".format(catalog_name,
self.get_catalogid(catalog_name, catalogs)))
return self.get_catalogid(catalog_name, catalogs)
# if we didn't find existing catalog we create a new one and upload image.
self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_name))
result = org.create_catalog(catalog_name, catalog_name)
if result is None:
raise Exception("Failed to create new catalog {} ".format(catalog_name))
result = self.upload_ovf(org=org, catalog_name=catalog_name, image_name=filename.split(".")[0],
media_file_name=path, description='medial_file_name', progress=progress)
if not result:
raise Exception("Failed create vApp template for catalog {} ".format(catalog_name))
def get_catalogid(self, catalog_name=None, catalogs=None):
""" Method check catalog and return catalog ID in UUID format.
Args
catalog_name: catalog name as string
catalogs: list of catalogs.
Return: catalogs uuid
"""
for catalog in catalogs:
if catalog['name'] == catalog_name:
catalog_id = catalog['id']
return catalog_id
return None
def upload_ovf(self, org=None, catalog_name=None, image_name=None, media_file_name=None,
description='', progress=False, chunk_bytes=128 * 1024):
"""
Uploads a OVF file to a vCloud catalog
org : organization object
catalog_name: (str): The name of the catalog to upload the media.
media_file_name: (str): The name of the local media file to upload.
return: (bool) True if the media file was successfully uploaded, false otherwise.
"""
client = self.connect()
if not client:
raise Exception("Failed to connect vCD!")
os.path.isfile(media_file_name)
statinfo = os.stat(media_file_name)
# find a catalog entry where we upload OVF.
# create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
# status change.
# if VCD can parse OVF we upload VMDK file
try:
for catalog in org.list_catalogs():
if catalog_name != catalog['name']:
continue
catalog_href = "{}/api/catalog/{}/action/upload".format(self.url, catalog['id'])
data = """
<UploadVAppTemplateParams name="{}" xmlns="http://www.vmware.com/vcloud/v1.5"
xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1">
<Description>{} vApp Template</Description></UploadVAppTemplateParams>
""".format(catalog_name, description)
if client:
headers = {'Accept': 'application/*+xml;version=' + API_VERSION,
'x-vcloud-authorization': client._session.headers['x-vcloud-authorization']}
headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
response = requests.post(url=catalog_href,
headers=headers,
data=data,
verify=False)
if response.status_code != 201:
self.logger.debug("Failed to create vApp template")
raise Exception("Failed to create vApp template")
if response.status_code == requests.codes.created:
catalogItem = XmlElementTree.fromstring(response.content)
entity = [child for child in catalogItem if
child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
href = entity.get('href')
template = href
response = requests.get(url=href,
headers=headers,
verify=False)
if response.status_code == requests.codes.ok:
headers['Content-Type'] = 'Content-Type text/xml'
result = re.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"', response.content)
if result:
transfer_href = result.group(1)
response = requests.put(url=transfer_href, headers=headers,
data=open(media_file_name, 'rb'),
verify=False)
if response.status_code != requests.codes.ok:
self.logger.debug(
"Failed create vApp template for catalog name {} and image {}".format(
catalog_name,
media_file_name))
return False
# TODO fix this with aync block
time.sleep(5)
self.logger.debug("vApp template for catalog name {} and image {}".format(
catalog_name,
media_file_name))
# uploading VMDK file
# check status of OVF upload and upload remaining files.
response = requests.get(url=template,
headers=headers,
verify=False)
if response.status_code == requests.codes.ok:
result = re.search('rel="upload:default"\s*href="(.*?vmdk)"', response.content)
if result:
link_href = result.group(1)
# we skip ovf since it already uploaded.
if 'ovf' in link_href:
continue
# The OVF file and VMDK must be in a same directory
head, _ = os.path.split(media_file_name)
file_vmdk = head + '/' + link_href.split("/")[-1]
if not os.path.isfile(file_vmdk):
return False
statinfo = os.stat(file_vmdk)
if statinfo.st_size == 0:
return False
hrefvmdk = link_href
if progress:
widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
FileTransferSpeed()]
progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
bytes_transferred = 0
f = open(file_vmdk, 'rb')
while bytes_transferred < statinfo.st_size:
my_bytes = f.read(chunk_bytes)
if len(my_bytes) <= chunk_bytes:
headers['Content-Range'] = 'bytes %s-%s/%s' % (
bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
headers['Content-Length'] = str(len(my_bytes))
response = requests.put(url=hrefvmdk,
headers=headers,
data=my_bytes,
verify=False)
if response.status_code == requests.codes.ok:
bytes_transferred += len(my_bytes)
if progress:
progress_bar.update(bytes_transferred)
else:
self.logger.debug(
'file upload failed with error: [%s] %s' % (response.status_code,
response.content))
f.close()
return False
f.close()
if progress:
progress_bar.finish()
time.sleep(60)
return True
else:
self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
format(catalog_name, media_file_name))
return False
except Exception as exp:
self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
.format(catalog_name, media_file_name, exp))
raise Exception(
"Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
.format(catalog_name, media_file_name, exp))
self.logger.debug("Failed to retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
return False
if __name__ == "__main__":
print("This file is deprecated. Please use ovf_uplader_cli instead.")
# vmware vcloud director credentials
vcd_hostname = sys.argv[1]
vcd_username = sys.argv[2]
vcd_password = sys.argv[3]
orgname = sys.argv[4]
# OVF image path to be upload to vCD
ovf_file_path = sys.argv[5]
logging.basicConfig(filename='ovf_upload.log', level=logging.DEBUG)
logger = logging.getLogger(__name__)
obj = vCloudconfig(vcd_hostname, vcd_username, vcd_password, orgname, logger)
dirpath, filename = os.path.split(ovf_file_path)
filename_name, file_extension = os.path.splitext(filename)
# Get image name from cirros vnfd
cirros_yaml = '../descriptor-packages/vnfd/cirros_vnf/src/cirros_vnfd.yaml'
rh = open(cirros_yaml, 'r')
match = re.search("image:\s'(.*?)'\n", rh.read())
if match:
catalog = match.group(1)
if file_extension == '.ovf':
obj.get_catalog_id_from_path(catalog_name=catalog, path=ovf_file_path,
progress=True)