X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=vimconn_vmware.py;h=7836eff63658b02c08f50461d969414860632cce;hb=ed1be4b1591c258748261f21947dc388c28c6fd9;hp=fb7d49b90072fd3c4783149d97b1c5cee21b04e2;hpb=ef39072eda639958e3ddcf50c99faf9bf45b5b7f;p=osm%2FRO.git diff --git a/vimconn_vmware.py b/vimconn_vmware.py index fb7d49b9..7836eff6 100644 --- a/vimconn_vmware.py +++ b/vimconn_vmware.py @@ -21,13 +21,15 @@ # contact with: nfvlabs@tid.es ## -''' +""" vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director. mbayramov@vmware.com -''' +""" +from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar + +import vimconn import os import traceback - import itertools import requests @@ -53,21 +55,17 @@ import time import uuid import httplib +# global variable for vcd connector type +STANDALONE = 'standalone' + +# global variable for number of retry DELETE_INSTANCE_RETRY = 3 +VCAVERSION = '5.9' + __author__ = "Mustafa Bayramov" __date__ = "$26-Aug-2016 11:09:29$" - -# Error variables -HTTP_Bad_Request = 400 -HTTP_Unauthorized = 401 -HTTP_Not_Found = 404 -HTTP_Method_Not_Allowed = 405 -HTTP_Request_Timeout = 408 -HTTP_Conflict = 409 -HTTP_Not_Implemented = 501 -HTTP_Service_Unavailable = 503 -HTTP_Internal_Server_Error = 500 +__version__ = '0.1' # -1: "Could not be created", # 0: "Unresolved", @@ -101,68 +99,46 @@ netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INA 'ERROR': 'ERROR', 'DELETED': 'DELETED' } +# dict used to store flavor in memory +flavorlist = {} -class vimconnException(Exception): - '''Common and base class Exception for all vimconnector exceptions''' - - def __init__(self, message, http_code=HTTP_Bad_Request): - Exception.__init__(self, message) - self.http_code = http_code - - -class vimconnConnectionException(vimconnException): - '''Connectivity error with the VIM''' - - def __init__(self, message, http_code=HTTP_Service_Unavailable): - vimconnException.__init__(self, message, http_code) - - -class vimconnUnexpectedResponse(vimconnException): - '''Get an wrong response from VIM''' - - def __init__(self, message, http_code=HTTP_Service_Unavailable): - vimconnException.__init__(self, message, http_code) - - -class vimconnAuthException(vimconnException): - '''Invalid credentials or authorization to perform this action over the VIM''' - - def __init__(self, message, http_code=HTTP_Unauthorized): - vimconnException.__init__(self, message, http_code) - - -class vimconnNotFoundException(vimconnException): - '''The item is not found at VIM''' - - def __init__(self, message, http_code=HTTP_Not_Found): - vimconnException.__init__(self, message, http_code) - - -class vimconnConflictException(vimconnException): - '''There is a conflict, e.g. more item found than one''' - - def __init__(self, message, http_code=HTTP_Conflict): - vimconnException.__init__(self, message, http_code) +class vimconnector(vimconn.vimconnector): + def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None, + url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}): + """ + Constructor create vmware connector to vCloud director. -class vimconnNotImplemented(vimconnException): - '''The method is not implemented by the connected''' + By default construct doesn't validate connection state. So client can create object with None arguments. + If client specified username , password and host and VDC name. Connector initialize other missing attributes. - def __init__(self, message, http_code=HTTP_Not_Implemented): - vimconnException.__init__(self, message, http_code) + a) It initialize organization UUID + b) Initialize tenant_id/vdc ID. (This information derived from tenant name) + Args: + uuid - is organization uuid. + name - is organization name that must be presented in vCloud director. + tenant_id - is VDC uuid it must be presented in vCloud director + tenant_name - is VDC name. + url - is hostname or ip address of vCloud director + url_admin - same as above. + user - is user that administrator for organization. Caller must make sure that + username has right privileges. -flavorlist = {} + password - is password for a user. + VMware connector also requires PVDC administrative privileges and separate account. + This variables must be passed via config argument dict contains keys -class vimconnector(): - '''Vmware VIM Connector base class - ''' + dict['admin_username'] + dict['admin_password'] - def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, - log_level="ERROR", config={}): + Returns: + Nothing. + """ - print config + vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, + url_admin, user, passwd, log_level, config) self.id = uuid self.name = name self.org_name = name @@ -177,25 +153,26 @@ class vimconnector(): self.admin_user = None self.logger = logging.getLogger('openmano.vim.vmware') + self.logger.setLevel(10) + + if log_level: + self.logger.setLevel(getattr(logging, log_level)) try: self.admin_user = config['admin_username'] self.admin_password = config['admin_password'] except KeyError: - raise vimconnException(message="Error admin username or admin password is empty.") + raise vimconn.vimconnException(message="Error admin username or admin password is empty.") - self.logger = logging.getLogger('mano.vim.vmware') self.org_uuid = None self.vca = None if not url: - raise TypeError, 'url param can not be NoneType' + raise vimconn.vimconnException('url param can not be NoneType') if not self.url_admin: # try to use normal url self.url_admin = self.url - self.vcaversion = '5.6' - logging.debug("Calling constructor with following paramters") logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.name, self.tenant_id, self.tenant_name)) @@ -203,8 +180,8 @@ class vimconnector(): logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password)) # initialize organization - if self.user is not None and self.passwd is not None: - self.init_org_uuid() + if self.user is not None and self.passwd is not None and self.url: + self.init_organization() def __getitem__(self, index): if index == 'tenant_id': @@ -260,7 +237,9 @@ class vimconnector(): raise KeyError("Invalid key '%s'" % str(index)) def connect_as_admin(self): - """ Method connect as admin user to vCloud director. + """ Method connect as pvdc admin user to vCloud director. + There are certain action that can be done only by provider vdc admin user. + Organization creation / provider network creation etc. Returns: The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc @@ -268,17 +247,16 @@ class vimconnector(): self.logger.debug("Logging in to a vca {} as admin.".format(self.name)) - service_type = 'standalone' - version = '5.6' vca_admin = VCA(host=self.url, username=self.admin_user, - service_type=service_type, - version=version, + service_type=STANDALONE, + version=VCAVERSION, verify=False, log=False) result = vca_admin.login(password=self.admin_password, org='System') if not result: - raise vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.admin_user)) + raise vimconn.vimconnConnectionException( + "Can't connect to a vCloud director as: {}".format(self.admin_user)) result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url) if result is True: self.logger.info( @@ -293,27 +271,35 @@ class vimconnector(): The return vca object that letter can be used to connect to vCloud director as admin for VDC """ - service_type = 'standalone' - version = '5.9' + try: + self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.name, self.user, self.name)) + vca = VCA(host=self.url, + username=self.user, + service_type=STANDALONE, + version=VCAVERSION, + verify=False, + log=False) + + result = vca.login(password=self.passwd, org=self.name) + if not result: + raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user)) + result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url) + if result is True: + self.logger.info( + "Successfully logged to a vcloud direct org: {} as user: {}".format(self.name, self.user)) - self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.name, self.user, self.name)) - vca = VCA(host=self.url, - username=self.user, - service_type=service_type, - version=version, - verify=False, - log=False) - result = vca.login(password=self.passwd, org=self.name) - if not result: - raise vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user)) - result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url) - if result is True: - self.logger.info("Successfully logged to a vcloud direct org: {} as user: {}".format(self.name, self.user)) + except: + raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user)) return vca - def init_org_uuid(self): - """ Method available organization for a logged in tenant + def init_organization(self): + """ Method initialize organization UUID and VDC parameters. + + At bare minimum client must provide organization name that present in vCloud director and VDC. + + The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor. + The Org - UUID will be initialized at the run time if data center present in vCloud director. Returns: The return vca object that letter can be used to connect to vcloud direct as admin @@ -322,8 +308,41 @@ class vimconnector(): if self.org_uuid is None: org_dict = self.get_org_list() for org in org_dict: + # we set org UUID at the init phase but we can do it only when we have valid credential. if org_dict[org] == self.org_name: self.org_uuid = org + self.logger.debug("Setting organization UUID {}".format(self.org_uuid)) + break + + else: + raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name)) + + # if well good we require for org details + org_details_dict = self.get_org(org_uuid=self.org_uuid) + + # we have two case if we want to initialize VDC ID or VDC name at run time + # tenant_name provided but no tenant id + if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict: + vdcs_dict = org_details_dict['vdcs'] + for vdc in vdcs_dict: + if vdcs_dict[vdc] == self.tenant_name: + self.tenant_id = vdc + self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id, + self.name)) + break + else: + raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.") + # case two we have tenant_id but we don't have tenant name so we find and set it. + if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict: + vdcs_dict = org_details_dict['vdcs'] + for vdc in vdcs_dict: + if vdc == self.tenant_id: + self.tenant_name = vdcs_dict[vdc] + self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id, + self.name)) + break + else: + raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director") self.logger.debug("Setting organization uuid {}".format(self.org_uuid)) except: self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name)) @@ -331,42 +350,40 @@ class vimconnector(): self.org_uuid = None def new_tenant(self, tenant_name=None, tenant_description=None): - """ - Adds a new tenant to VIM with this name and description + """ Method adds a new tenant to VIM with this name. + This action requires access to create VDC action in vCloud director. - :param tenant_name: - :param tenant_description: - :return: returns the tenant identifier - """ + Args: + tenant_name is tenant_name to be created. + tenant_description not used for this call + + Return: + returns the tenant identifier in UUID format. + If action is failed method will throw vimconn.vimconnException method + """ vdc_task = self.create_vdc(vdc_name=tenant_name) if vdc_task is not None: vdc_uuid, value = vdc_task.popitem() self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid)) return vdc_uuid else: - raise vimconnException("Failed create tenant {}".format(tenant_name)) + raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name)) - def delete_tenant(self, tenant_id, ): + def delete_tenant(self, tenant_id=None): """Delete a tenant from VIM""" 'Returns the tenant identifier' - - print(" ") - print(" ######## delete_tenant {} ".format(tenant_id)) - print(" ") - - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def get_tenant_list(self, filter_dict={}): - '''Obtain tenants of VIM + """Obtain tenants of VIM filter_dict can contain the following keys: name: filter by tenant name id: filter by tenant uuid/id - Returns the tenant list of dictionaries: + Returns the tenant list of dictionaries: [{'name':', 'id':', ...}, ...] - ''' - + """ org_dict = self.get_org(self.org_uuid) vdcs_dict = org_dict['vdcs'] @@ -374,25 +391,29 @@ class vimconnector(): try: for k in vdcs_dict: entry = {'name': vdcs_dict[k], 'id': k} - filtered_entry = entry.copy() - filtered_dict = set(entry.keys()) - set(filter_dict) - for unwanted_key in filtered_dict: del entry[unwanted_key] - if filter_dict == entry: - vdclist.append(filtered_entry) + # if caller didn't specify dictionary we return all tenants. + if filter_dict is not None and filter_dict: + filtered_entry = entry.copy() + filtered_dict = set(entry.keys()) - set(filter_dict) + for unwanted_key in filtered_dict: del entry[unwanted_key] + if filter_dict == entry: + vdclist.append(filtered_entry) + else: + vdclist.append(entry) except: self.logger.debug("Error in get_tenant_list()") self.logger.debug(traceback.format_exc()) - pass + raise vimconn.vimconnException("Incorrect state. {}") return vdclist def new_network(self, net_name, net_type, ip_profile=None, shared=False): - '''Adds a tenant network to VIM + """Adds a tenant network to VIM net_name is the name net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised - ip_profile is a dict containing the IP parameters of the network + ip_profile is a dict containing the IP parameters of the network shared is a boolean - Returns the network identifier''' + Returns the network identifier""" self.logger.debug( "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name, net_type, ip_profile, shared)) @@ -405,7 +426,7 @@ class vimconnector(): if network_uuid is not None: return network_uuid else: - raise vimconnUnexpectedResponse("Failed create a new network {}".format(net_name)) + raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name)) def get_vcd_network_list(self): """ Method available organization for a logged in tenant @@ -451,7 +472,7 @@ class vimconnector(): return network_list def get_network_list(self, filter_dict={}): - '''Obtain tenant networks of VIM + """Obtain tenant networks of VIM Filter_dict can be: name: network name OR/AND id: network uuid OR/AND @@ -465,7 +486,7 @@ class vimconnector(): Returns the network list of dictionaries: [{}, ...] List can be empty - ''' + """ vca = self.connect() if not vca: @@ -501,11 +522,14 @@ class vimconnector(): filter_entry["type"] = "bridge" filtered_entry = filter_entry.copy() - # we remove all the key : value we dont' care and match only - # respected field - filtered_dict = set(filter_entry.keys()) - set(filter_dict) - for unwanted_key in filtered_dict: del filter_entry[unwanted_key] - if filter_dict == filter_entry: + if filter_dict is not None and filter_dict: + # we remove all the key : value we don't care and match only + # respected field + filtered_dict = set(filter_entry.keys()) - set(filter_dict) + for unwanted_key in filtered_dict: del filter_entry[unwanted_key] + if filter_dict == filter_entry: + network_list.append(filtered_entry) + else: network_list.append(filtered_entry) except: self.logger.debug("Error in get_vcd_network_list") @@ -515,7 +539,7 @@ class vimconnector(): return network_list def get_network(self, net_id): - """Method bbtain network details of net_id VIM network + """Method obtains network details of net_id VIM network Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]""" vca = self.connect() @@ -559,37 +583,33 @@ class vimconnector(): vca = self.connect() if not vca: - raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed".format(self.tenant_name)) + raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name)) - if self.delete_network_action(net_id): - return net_id + vcd_network = self.get_vcd_network(network_uuid=net_id) + if vcd_network is not None and vcd_network: + if self.delete_network_action(network_uuid=net_id): + return net_id else: raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id)) def refresh_nets_status(self, net_list): - '''Get the status of the networks + """Get the status of the networks Params: the list of network identifiers Returns a dictionary with: net_id: #VIM id of this network status: #Mandatory. Text with one of: # DELETED (not found at vim) - # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) + # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) # OTHER (Vim reported other status not understood) # ERROR (VIM indicates an ERROR status) - # ACTIVE, INACTIVE, DOWN (admin down), + # ACTIVE, INACTIVE, DOWN (admin down), # BUILD (on building process) # - error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR + error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR vim_info: #Text with plain information obtained from vim (yaml.safe_dump) - ''' - - # for net in net_list: - # net['status'] - # net['error_msg'] - # net['vim_info'] + """ - # vim vimcon failed == ERROR vca = self.connect() if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed") @@ -597,29 +617,31 @@ class vimconnector(): dict_entry = {} try: for net in net_list: - status = '' errormsg = '' vcd_network = self.get_vcd_network(network_uuid=net) - if vcd_network is not None: + if vcd_network is not None and vcd_network: if vcd_network['status'] == 1: status = 'ACTIVE' else: status = 'DOWN' else: status = 'DELETED' - errormsg = 'network not found' - dict_entry['net'] = {'status': status, 'error_msg': errormsg, - 'vm_info': yaml.safe_dump(vcd_network)} + errormsg = 'Network not found.' + + dict_entry[net] = {'status': status, 'error_msg': errormsg, + 'vm_info': yaml.safe_dump(vcd_network)} except: self.logger.debug("Error in refresh_nets_status") self.logger.debug(traceback.format_exc()) return dict_entry - def get_flavor(flavor_id): + def get_flavor(self, flavor_id): """Obtain flavor details from the VIM Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete """ + if flavor_id not in flavorlist: + raise vimconn.vimconnNotFoundException("Flavor not found.") return flavorlist[flavor_id] def new_flavor(self, flavor_data): @@ -654,62 +676,83 @@ class vimconnector(): def delete_flavor(self, flavor_id): """Deletes a tenant flavor from VIM identify by its id - Returns the used id or raise an exception + Returns the used id or raise an exception """ + if flavor_id not in flavorlist: + raise vimconn.vimconnNotFoundException("Flavor not found.") - # if key not present it will raise KeyError - # TODO check do I need raise any specific exception flavorlist.pop(flavor_id, None) return flavor_id def new_image(self, image_dict): - ''' + """ Adds a tenant image to VIM Returns: 200, image-id if the image is created <0, message if there is an error - ''' + """ return self.get_image_id_from_path(image_dict['location']) def delete_image(self, image_id): - '''Deletes a tenant image from VIM''' - '''Returns the HTTP response code and a message indicating details of the success or fail''' + """ - print " ################################################################### " - print " delete_image contains {}".format(image_id) - print " ################################################################### " + :param image_id: + :return: + """ - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def catalog_exists(self, catalog_name, catalogs): + """ + + :param catalog_name: + :param catalogs: + :return: + """ for catalog in catalogs: if catalog.name == catalog_name: return True return False - def create_vimcatalog(self, vca, catalog_name): - """Create Catalog entry in VIM""" - task = vca.create_catalog(catalog_name, catalog_name) - result = vca.block_until_completed(task) - if not result: + def create_vimcatalog(self, vca=None, catalog_name=None): + """ Create new catalog entry in vCloud director. + + Args + vca: vCloud director. + catalog_name catalog that client wish to create. Note no validation done for a name. + Client must make sure that provide valid string representation. + + Return (bool) True if catalog created. + + """ + try: + task = vca.create_catalog(catalog_name, catalog_name) + result = vca.block_until_completed(task) + if not result: + return False + catalogs = vca.get_catalogs() + except: return False - catalogs = vca.get_catalogs() return self.catalog_exists(catalog_name, catalogs) - def upload_ovf(self, vca, catalog_name, item_name, media_file_name, description='', display_progress=False, - chunk_bytes=128 * 1024): + # noinspection PyIncorrectDocstring + def upload_ovf(self, vca=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 + :param chunk_bytes: + :param progress: + :param description: + :param image_name: + :param vca: :param catalog_name: (str): The name of the catalog to upload the media. - :param item_name: (str): The name of the media file in the catalog. :param 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. """ os.path.isfile(media_file_name) statinfo = os.stat(media_file_name) - statinfo.st_size # 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 @@ -723,7 +766,7 @@ class vimconnector(): assert len(link) == 1 data = """ %s vApp Template - """ % (escape(item_name), escape(description)) + """ % (escape(image_name), escape(description)) headers = vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml' response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger) @@ -738,13 +781,13 @@ class vimconnector(): if response.status_code == requests.codes.ok: media = mediaType.parseString(response.content, True) - link = \ - filter(lambda link: link.get_rel() == 'upload:default', - media.get_Files().get_File()[0].get_Link())[ - 0] + link = filter(lambda link: link.get_rel() == 'upload:default', + media.get_Files().get_File()[0].get_Link())[0] headers = vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'Content-Type text/xml' - response = Http.put(link.get_href(), data=open(media_file_name, 'rb'), headers=headers, + response = Http.put(link.get_href(), + data=open(media_file_name, 'rb'), + headers=headers, verify=vca.verify, logger=self.logger) if response.status_code != requests.codes.ok: self.logger.debug( @@ -759,54 +802,68 @@ class vimconnector(): format(catalog_name, media_file_name)) # uploading VMDK file - # check status of OVF upload - response = Http.get(template, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify, + # check status of OVF upload and upload remaining files. + response = Http.get(template, + headers=vca.vcloud_session.get_vcloud_headers(), + verify=vca.verify, logger=self.logger) + if response.status_code == requests.codes.ok: media = mediaType.parseString(response.content, True) - link = \ - filter(lambda link: link.get_rel() == 'upload:default', - media.get_Files().get_File()[0].get_Link())[ - 0] - - # The OVF file and VMDK must be in a same directory - head, tail = os.path.split(media_file_name) - filevmdk = head + '/' + os.path.basename(link.get_href()) - - os.path.isfile(filevmdk) - statinfo = os.stat(filevmdk) - - # TODO debug output remove it - # print media.get_Files().get_File()[0].get_Link()[0].get_href() - # print media.get_Files().get_File()[1].get_Link()[0].get_href() - # print link.get_href() - - # in case first element is pointer to OVF. - hrefvmdk = link.get_href().replace("descriptor.ovf", "Cirros-disk1.vmdk") - - f = open(filevmdk, 'rb') - bytes_transferred = 0 - while bytes_transferred < statinfo.st_size: - my_bytes = f.read(chunk_bytes) - if len(my_bytes) <= chunk_bytes: - headers = vca.vcloud_session.get_vcloud_headers() - headers['Content-Range'] = 'bytes %s-%s/%s' % ( - bytes_transferred, len(my_bytes) - 1, statinfo.st_size) - headers['Content-Length'] = str(len(my_bytes)) - response = Http.put(hrefvmdk, - headers=headers, - data=my_bytes, - verify=vca.verify, - logger=None) - if response.status_code == requests.codes.ok: - bytes_transferred += len(my_bytes) - self.logger.debug('transferred %s of %s bytes' % (str(bytes_transferred), - str(statinfo.st_size))) - else: - self.logger.debug('file upload failed with error: [%s] %s' % (response.status_code, - response.content)) + number_of_files = len(media.get_Files().get_File()) + for index in xrange(0, number_of_files): + links_list = filter(lambda link: link.get_rel() == 'upload:default', + media.get_Files().get_File()[index].get_Link()) + for link in links_list: + # we skip ovf since it already uploaded. + if 'ovf' in link.get_href(): + continue + # The OVF file and VMDK must be in a same directory + head, tail = os.path.split(media_file_name) + file_vmdk = head + '/' + link.get_href().split("/")[-1] + if not os.path.isfile(file_vmdk): + return False + statinfo = os.stat(file_vmdk) + if statinfo.st_size == 0: return False - f.close() + hrefvmdk = link.get_href() + + if progress: + print("Uploading file: {}".format(file_vmdk)) + 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 = vca.vcloud_session.get_vcloud_headers() + headers['Content-Range'] = 'bytes %s-%s/%s' % ( + bytes_transferred, len(my_bytes) - 1, statinfo.st_size) + headers['Content-Length'] = str(len(my_bytes)) + response = Http.put(hrefvmdk, + headers=headers, + data=my_bytes, + verify=vca.verify, + logger=None) + + 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() return True else: self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}". @@ -816,58 +873,112 @@ class vimconnector(): self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name)) return False - def upload_vimimage(self, vca, catalog_name, media_name, medial_file_name): + def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False): """Upload media file""" - # TODO add named parameters for readbility - return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True) + # TODO add named parameters for readability + + return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0], + media_file_name=medial_file_name, description='medial_file_name', progress=progress) + + def validate_uuid4(self, uuid_string=None): + """ Method validate correct format of UUID. + + Return: true if string represent valid uuid + """ + try: + val = uuid.UUID(uuid_string, version=4) + except ValueError: + return False + return True + + 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 + """ - def get_catalogid(self, catalog_name, catalogs): for catalog in catalogs: if catalog.name == catalog_name: catalog_id = catalog.get_id().split(":") return catalog_id[3] return None - def get_catalogbyid(self, catalog_id, catalogs): + def get_catalogbyid(self, catalog_uuid=None, catalogs=None): + """ Method check catalog and return catalog name lookup done by catalog UUID. + + Args + catalog_name: catalog name as string + catalogs: list of catalogs. + + Return: catalogs name or None + """ + + if not self.validate_uuid4(uuid_string=catalog_uuid): + return None + for catalog in catalogs: - catalogid = catalog.get_id().split(":")[3] - if catalogid == catalog_id: + catalog_id = catalog.get_id().split(":")[3] + if catalog_id == catalog_uuid: return catalog.name return None - def get_image_id_from_path(self, path): - '''Get the image id from image path in the VIM database''' - '''Returns: - 0,"Image not found" if there are no images with that path - 1,image-id if there is one image with that path - <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.) - ''' + def get_image_id_from_path(self, path=None, progress=False): + """ Method upload OVF image to vCloud director. + + Each OVF image represented as single catalog entry in vcloud director. + The method check for existing catalog entry. The check done by file name without file extension. + + if given catalog name already present method will respond with existing catalog uuid otherwise + it will create new catalog entry and upload OVF file to newly created catalog. + If method can't create catalog entry or upload a file it will throw exception. + + Method accept boolean flag progress that will output progress bar. It useful method + for standalone upload use case. In case to test large file upload. + + Args + path: - valid path to OVF file. + progress - boolean progress bar show progress bar. + + Return: if image uploaded correct method will provide image catalog UUID. + """ vca = self.connect() if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") + raise vimconn.vimconnConnectionException("self.connect() is failed.") + + if path is None: + raise vimconn.vimconnException("Image path can't be None.") - self.logger.debug("get_image_id_from_path path {}".format(path)) + if not os.path.isfile(path): + raise vimconn.vimconnException("Can't read file. File not found.") + + if not os.access(path, os.R_OK): + raise vimconn.vimconnException("Can't read file. Check file permission to read.") + + self.logger.debug("get_image_id_from_path() client requesting {} ".format(path)) dirpath, filename = os.path.split(path) flname, file_extension = os.path.splitext(path) if file_extension != '.ovf': - self.logger.debug("Wrong file extension {}".format(file_extension)) - return -1, "Wrong container. vCloud director supports only OVF." + self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension)) + raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.") catalog_name = os.path.splitext(filename)[0] - self.logger.debug("File name {} Catalog Name {} file path {}".format(filename, catalog_name, path)) - self.logger.debug("Catalog name {}".format(catalog_name)) catalogs = vca.get_catalogs() if len(catalogs) == 0: - self.logger.info("Creating new catalog entry {} in vcloud director".format(catalog_name)) + self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name)) result = self.create_vimcatalog(vca, catalog_name) if not result: - return -1, "Failed create new catalog {} ".format(catalog_name) - result = self.upload_vimimage(vca, catalog_name, filename, path) + raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_name)) + result = self.upload_vimimage(vca=vca, catalog_name=catalog_name, + media_name=filename, medial_file_name=path, progress=progress) if not result: - return -1, "Failed create vApp template for catalog {} ".format(catalog_name) + raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name)) return self.get_catalogid(catalog_name, vca.get_catalogs()) else: for catalog in catalogs: @@ -880,14 +991,16 @@ class vimconnector(): catalogs))) return self.get_catalogid(catalog_name, vca.get_catalogs()) - # if we didn't find existing catalog we create a new one. + # 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)) result = self.create_vimcatalog(vca, catalog_name) if not result: - return -1, "Failed create new catalog {} ".format(catalog_name) - result = self.upload_vimimage(vca, catalog_name, filename, path) + raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_name)) + + result = self.upload_vimimage(vca=vca, catalog_name=catalog_name, + media_name=filename, medial_file_name=path, progress=progress) if not result: - return -1, "Failed create vApp template for catalog {} ".format(catalog_name) + raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name)) return self.get_catalogid(catalog_name, vca.get_catalogs()) @@ -895,17 +1008,12 @@ class vimconnector(): """ Method takes vdc object and vApp name and returns vapp uuid or None Args: - vca: Connector to VCA vdc: The VDC object. vapp_name: is application vappp name identifier - Returns: + Returns: The return vApp name otherwise None """ - - """ Take vdc object and vApp name and returns vapp uuid or None - """ - if vdc is None or vapp_name is None: return None # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf @@ -919,12 +1027,8 @@ class vimconnector(): return False return None - def check_vapp(self, vdc, vapp_id): - """ Take VDC object and vApp ID and return True if given ID in vCloud director - otherwise return False - """ - - """ Method Method returns vApp name from vCD and lookup done by vapp_id. + def check_vapp(self, vdc=None, vapp_uuid=None): + """ Method Method returns True or False if vapp deployed in vCloud director Args: vca: Connector to VCA @@ -932,7 +1036,9 @@ class vimconnector(): vappid: vappid is application identifier Returns: - The return vApp name otherwise None + The return True if vApp deployed + :param vdc: + :param vapp_uuid: """ try: refs = filter(lambda ref: @@ -941,20 +1047,20 @@ class vimconnector(): for ref in refs: vappid = ref.href.split("vapp")[1][1:] # find vapp with respected vapp uuid - if vappid == vapp_id: + if vappid == vapp_uuid: return True except Exception as e: self.logger.exception(e) return False return False - def get_namebyvappid(self, vca, vdc, vapp_id): + def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None): """Method returns vApp name from vCD and lookup done by vapp_id. Args: vca: Connector to VCA vdc: The VDC object. - vapp_id: vappid is application identifier + vapp_uuid: vappid is application identifier Returns: The return vApp name otherwise None @@ -966,7 +1072,7 @@ class vimconnector(): for ref in refs: # we care only about UUID the rest doesn't matter vappid = ref.href.split("vapp")[1][1:] - if vappid == vapp_id: + if vappid == vapp_uuid: response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify, logger=self.logger) tree = XmlElementTree.fromstring(response.content) @@ -976,7 +1082,8 @@ class vimconnector(): return None return None - def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None): + def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={}, + cloud_config=None): """Adds a VM instance to VIM Params: start: indicates if VM must start or boot in pause mode. Ignored @@ -1006,56 +1113,79 @@ class vimconnector(): if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed.") + #new vm name = vmname + tenant_id + uuid + new_vm_name = [name, '-', str(uuid.uuid4())] + full_name = ''.join(new_vm_name) + # if vm already deployed we return existing uuid - vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name) - if vapp_uuid is not None: - return vapp_uuid + # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name) + # if vapp_uuid is not None: + # return vapp_uuid # we check for presence of VDC, Catalog entry and Flavor. vdc = vca.get_vdc(self.tenant_name) if vdc is None: - return -1, " Failed create vApp {}: (Failed reprieve VDC information)".format(name) + raise vimconn.vimconnNotFoundException( + "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name)) catalogs = vca.get_catalogs() if catalogs is None: - return -2, " Failed create vApp {}: (Failed reprieve Catalog information)".format(name) - flavor = flavorlist[flavor_id] - if catalogs is None: - return -3, " Failed create vApp {}: (Failed reprieve Flavor information)".format(name) + raise vimconn.vimconnNotFoundException( + "new_vminstance(): Failed create vApp {}: ""(Failed retrieve catalog information)".format(name)) + + vm_cpus = None + vm_memory = None + if flavor_id is not None: + flavor = flavorlist[flavor_id] + if flavor is None: + raise vimconn.vimconnNotFoundException( + "new_vminstance(): Failed create vApp {}: (Failed retrieve flavor information)".format(name)) + else: + try: + vm_cpus = flavor['vcpus'] + vm_memory = flavor['ram'] + except KeyError: + raise vimconn.vimconnException("Corrupted flavor. {}".format(flavor_id)) # image upload creates template name as catalog name space Template. - templateName = self.get_catalogbyid(image_id, catalogs) + ' Template' + templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs) + ' Template' power_on = 'false' if start: power_on = 'true' # client must provide at least one entry in net_list if not we report error - primary_net = net_list[0] - if primary_net is None: - return -4, "Failed create vApp {}: (Network list is empty)".format(name) - - primary_net_id = primary_net['net_id'] - primary_net_name = self.get_network_name_by_id(primary_net_id) - network_mode = primary_net['use'] + primary_net_name = None + if net_list is not None and len(net_list) > 0: + primary_net = net_list[0] + if primary_net is None: + raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name)) + else: + try: + primary_net_id = primary_net['net_id'] + primary_net_name = self.get_network_name_by_id(primary_net_id) + network_mode = primary_net['use'] + except KeyError: + raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net)) # use: 'data', 'bridge', 'mgmt' # create vApp. Set vcpu and ram based on flavor id. - vapptask = vca.create_vapp(self.tenant_name, name, templateName, + vapptask = vca.create_vapp(self.tenant_name, full_name, templateName, self.get_catalogbyid(image_id, catalogs), - network_name=primary_net_name, + network_name=primary_net_name, # can be None if net_list None network_mode='bridged', - vm_name=name, - vm_cpus=flavor['vcpus'], - vm_memory=flavor['ram']) + vm_name=full_name, + vm_cpus=vm_cpus, # can be None if flavor is None + vm_memory=vm_memory) # can be None if flavor is None if vapptask is None or vapptask is False: - return -1, "create_vapp(): failed deploy vApp {}".format(name) + raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(full_name)) if type(vapptask) is VappTask: vca.block_until_completed(vapptask) # we should have now vapp in undeployed state. - vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), name) + vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), full_name) if vapp is None: - return -1, "get_vapp(): failed retrieve vApp {}".format(name) + raise vimconn.vimconnUnexpectedResponse( + "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(full_name)) # add first NIC try: @@ -1075,7 +1205,7 @@ class vimconnector(): vca.block_until_completed(task) # connect network to VM # TODO figure out mapping between openmano representation to vCloud director. - # one idea use first nic as managment DHCP all remaining in bridge mode + # one idea use first nic as management DHCP all remaining in bridge mode task = vapp.connect_vms(nets[0].name, connection_index=nicIndex, connections_primary_index=nicIndex, ip_allocation_mode='DHCP') @@ -1083,9 +1213,9 @@ class vimconnector(): vca.block_until_completed(task) nicIndex += 1 except KeyError: - # TODO # it might be a case if specific mandatory entry in dict is empty self.logger.debug("Key error {}".format(KeyError.message)) + raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name)) # deploy and power on vm task = vapp.poweron() @@ -1096,11 +1226,11 @@ class vimconnector(): vca.block_until_completed(deploytask) # check if vApp deployed and if that the case return vApp UUID otherwise -1 - vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name) + vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), full_name) if vapp_uuid is not None: return vapp_uuid - - return -1, " Failed create vApp {}".format(name) + else: + raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name)) ## ## @@ -1117,8 +1247,8 @@ class vimconnector(): # error_msg: # interfaces: … # - def get_vminstance(self, vim_vm_uuid): - '''Returns the VM instance information from VIM''' + def get_vminstance(self, vim_vm_uuid=None): + """Returns the VM instance information from VIM""" self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid)) vca = self.connect() @@ -1127,43 +1257,35 @@ class vimconnector(): vdc = vca.get_vdc(self.tenant_name) if vdc is None: - return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name) + raise vimconn.vimconnConnectionException( + "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) - vm_name = self.get_namebyvappid(vca, vdc, vim_vm_uuid) - if vm_name is None: + vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid) + if not vm_info_dict: self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid)) - return None, "Failed to get vApp name by UUID {}".format(vim_vm_uuid) - - the_vapp = vca.get_vapp(vdc, vm_name) - vm_info = the_vapp.get_vms_details() + raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid)) - vm_dict = {'description': vm_info[0]['name'], 'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()], - 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()], - 'vim_info': yaml.safe_dump(the_vapp.get_vms_details()), 'interfaces': []} - - # get networks - vm_app_networks = the_vapp.get_vms_network_info() - - interfaces = [] + status_key = vm_info_dict['status'] + error = '' try: - org_network_dict = self.get_org(self.org_uuid)['networks'] - for vapp_network in vm_app_networks: - for vm_network in vapp_network: - if vm_network['name'] == vm_name: - interface = {} - # interface['vim_info'] = yaml.safe_dump(vm_network) - interface["mac_address"] = vm_network['mac'] - for net_uuid in org_network_dict: - if org_network_dict[net_uuid] == vm_network['network_name']: - interface["vim_net_id"] = net_uuid - interface["vim_interface_id"] = vm_network['network_name'] - interface['ip_address'] = vm_network['ip'] - interfaces.append(interface) + vm_dict = {'created': vm_info_dict['created'], + 'description': vm_info_dict['name'], + 'status': vcdStatusCode2manoFormat[int(status_key)], + 'hostId': vm_info_dict['vmuuid'], + 'error_msg': error, + 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []} + + if 'interfaces' in vm_info_dict: + vm_dict['interfaces'] = vm_info_dict['interfaces'] + else: + vm_dict['interfaces'] = [] except KeyError: - self.logger.debug("Error in respond {}".format(KeyError.message)) - self.logger.debug(traceback.format_exc()) - - vm_dict['interfaces'] = interfaces + vm_dict = {'created': '', + 'description': '', + 'status': vcdStatusCode2manoFormat[int(-1)], + 'hostId': vm_info_dict['vmuuid'], + 'error_msg': "Inconsistency state", + 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []} return vm_dict @@ -1186,7 +1308,8 @@ class vimconnector(): if vdc is None: self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format( self.tenant_name)) - raise vimconnException("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) + raise vimconn.vimconnException( + "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) try: vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid) @@ -1230,16 +1353,17 @@ class vimconnector(): if type(task) is GenericTask: vca.block_until_completed(delete_task) if not delete_task: - self.loggger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid)) + self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid)) retry += 1 - if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None: - return vm__vim_uuid except: self.logger.debug(traceback.format_exc()) - raise vimconnException("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) + raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid)) - return -1 + if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None: + return vm__vim_uuid + else: + raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid)) def refresh_vms_status(self, vm_list): """Get the status of the virtual machines and their interfaces/ports @@ -1272,7 +1396,7 @@ class vimconnector(): vdc = vca.get_vdc(self.tenant_name) if vdc is None: - raise vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) + raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)) vms_dict = {} for vmuuid in vm_list: @@ -1283,10 +1407,9 @@ class vimconnector(): vm_info = the_vapp.get_vms_details() vm_status = vm_info[0]['status'] - vm_dict = {'status': None, 'error_msg': None, 'vim_info': None, 'interfaces': []} - vm_dict['status'] = vcdStatusCode2manoFormat[the_vapp.me.get_status()] - vm_dict['error_msg'] = vcdStatusCode2manoFormat[the_vapp.me.get_status()] - vm_dict['vim_info'] = yaml.safe_dump(the_vapp.get_vms_details()) + vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()], + 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()], + 'vim_info': yaml.safe_dump(the_vapp.get_vms_details()), 'interfaces': []} # get networks try: @@ -1294,12 +1417,11 @@ class vimconnector(): for vapp_network in vm_app_networks: for vm_network in vapp_network: if vm_network['name'] == vmname: - interface = {} + interface = {"mac_address": vm_network['mac'], + "vim_net_id": self.get_network_name_by_id(vm_network['network_name']), + "vim_interface_id": vm_network['network_name'], + 'ip_address': vm_network['ip']} # interface['vim_info'] = yaml.safe_dump(vm_network) - interface["mac_address"] = vm_network['mac'] - interface["vim_net_id"] = self.get_network_name_by_id(vm_network['network_name']) - interface["vim_interface_id"] = vm_network['network_name'] - interface['ip_address'] = vm_network['ip'] vm_dict["interfaces"].append(interface) # add a vm to vm dict vms_dict.setdefault(vmuuid, vm_dict) @@ -1315,7 +1437,7 @@ class vimconnector(): self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict)) if vm__vim_uuid is None or action_dict is None: - raise vimconnException("Invalid request. VM id or action is None.") + raise vimconn.vimconnException("Invalid request. VM id or action is None.") vca = self.connect() if not vca: @@ -1328,7 +1450,7 @@ class vimconnector(): vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid) if vapp_name is None: self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) - raise vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) + raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid)) else: self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid)) @@ -1347,10 +1469,10 @@ class vimconnector(): the_vapp.poweron() elif "pause" in action_dict: pass - ##server.pause() + ## server.pause() elif "resume" in action_dict: pass - ##server.resume() + ## server.resume() elif "shutoff" in action_dict or "shutdown" in action_dict: the_vapp.shutdown() elif "forceOff" in action_dict: @@ -1378,54 +1500,54 @@ class vimconnector(): port: the http, ssh, ... port suffix: extra text, e.g. the http path and query string """ - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") # NOT USED METHODS in current version def host_vim2gui(self, host, server_dict): - '''Transform host dictionary from VIM format to GUI format, + """Transform host dictionary from VIM format to GUI format, and append to the server_dict - ''' - raise vimconnNotImplemented("Should have implemented this") + """ + raise vimconn.vimconnNotImplemented("Should have implemented this") def get_hosts_info(self): - '''Get the information of deployed hosts - Returns the hosts content''' - raise vimconnNotImplemented("Should have implemented this") + """Get the information of deployed hosts + Returns the hosts content""" + raise vimconn.vimconnNotImplemented("Should have implemented this") def get_hosts(self, vim_tenant): - '''Get the hosts and deployed instances - Returns the hosts content''' - raise vimconnNotImplemented("Should have implemented this") + """Get the hosts and deployed instances + Returns the hosts content""" + raise vimconn.vimconnNotImplemented("Should have implemented this") def get_processor_rankings(self): - '''Get the processor rankings in the VIM database''' - raise vimconnNotImplemented("Should have implemented this") + """Get the processor rankings in the VIM database""" + raise vimconn.vimconnNotImplemented("Should have implemented this") def new_host(self, host_data): - '''Adds a new host to VIM''' + """Adds a new host to VIM""" '''Returns status code of the VIM response''' - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def new_external_port(self, port_data): - '''Adds a external port to VIM''' + """Adds a external port to VIM""" '''Returns the port identifier''' - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def new_external_network(self, net_name, net_type): - '''Adds a external network to VIM (shared)''' + """Adds a external network to VIM (shared)""" '''Returns the network identifier''' - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def connect_port_network(self, port_id, network_id, admin=False): - '''Connects a external port to a network''' + """Connects a external port to a network""" '''Returns status code of the VIM response''' - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def new_vminstancefromJSON(self, vm_data): - '''Adds a VM instance to VIM''' + """Adds a VM instance to VIM""" '''Returns the instance identifier''' - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def get_network_name_by_id(self, network_name=None): """Method gets vcloud director network named based on supplied uuid. @@ -1521,15 +1643,16 @@ class vimconnector(): Method retrieves available organization in vCloud Director Args: - vca - is active VCA connection. - vdc_name - is a vdc name that will be used to query vms action + org_uuid - is a organization uuid. Returns: - The return dictionary and key for each entry vapp UUID + The return dictionary with following key + "network" - for network list under the org + "catalogs" - for network list under the org + "vdcs" - for vdc list under org """ org_dict = {} - vca = self.connect() if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed") @@ -1688,7 +1811,7 @@ class vimconnector(): def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False): """ - Method retrieves VM's list deployed vCloud director. It returns a dictionary + Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary contains a list of all VM's deployed for queried VDC. The key for a dictionary is VM UUID @@ -1712,23 +1835,21 @@ class vimconnector(): try: vm_list_xmlroot = XmlElementTree.fromstring(content) for vm_xml in vm_list_xmlroot: - if vm_xml.tag.split("}")[1] == 'VMRecord': + if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false': + # lookup done by UUID if isuuid: - # lookup done by UUID if vapp_name in vm_xml.attrib['container']: rawuuid = vm_xml.attrib['href'].split('/')[-1:] if 'vm-' in rawuuid[0]: - # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove - # vm and use raw UUID as key vm_dict[rawuuid[0][3:]] = vm_xml.attrib - # lookup done by Name - else: - if vapp_name in vm_xml.attrib['name']: - rawuuid = vm_xml.attrib['href'].split('/')[-1:] - if 'vm-' in rawuuid[0]: - vm_dict[rawuuid[0][3:]] = vm_xml.attrib - # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove - # vm and use raw UUID as key + break + # lookup done by Name + else: + if vapp_name in vm_xml.attrib['name']: + rawuuid = vm_xml.attrib['href'].split('/')[-1:] + if 'vm-' in rawuuid[0]: + vm_dict[rawuuid[0][3:]] = vm_xml.attrib + break except: pass @@ -1870,7 +1991,8 @@ class vimconnector(): The return network uuid or return None """ - content = self.create_network_rest(network_name=network_name, + new_network_name = [network_name, '-', str(uuid.uuid4())] + content = self.create_network_rest(network_name=''.join(new_network_name), parent_network_uuid=parent_network_uuid, isshared=isshared) if content is None: @@ -1902,7 +2024,7 @@ class vimconnector(): vca = self.connect_as_admin() if not vca: - raise vimconn.vimconnConnectionException("self.connect() is failed") + raise vimconn.vimconnConnectionException("self.connect() is failed.") if network_name is None: return None @@ -2017,7 +2139,6 @@ class vimconnector(): xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name) if xml_content is not None: - print xml_content try: task_resp_xmlroot = XmlElementTree.fromstring(xml_content) for child in task_resp_xmlroot: @@ -2043,8 +2164,6 @@ class vimconnector(): """ self.logger.info("Creating new vdc {}".format(vdc_name)) - print ("Creating new vdc {}".format(vdc_name)) - vca = self.connect() if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed") @@ -2114,7 +2233,6 @@ class vimconnector(): """ self.logger.info("Creating new vdc {}".format(vdc_name)) - print ("Creating new vdc {}".format(vdc_name)) vca = self.connect_as_admin() if not vca: @@ -2153,7 +2271,6 @@ class vimconnector(): return None response = self.get_provider_rest(vca=vca) - print response try: vm_list_xmlroot = XmlElementTree.fromstring(response) for child in vm_list_xmlroot: @@ -2165,8 +2282,6 @@ class vimconnector(): self.logger.debug("Respond body {}".format(response)) return None - print "Add vdc {}".format(add_vdc_rest_url) - print "Provider ref {}".format(provider_vdc_ref) if add_vdc_rest_url is not None and provider_vdc_ref is not None: data = """ {1:s} ReservationPool @@ -2181,15 +2296,134 @@ class vimconnector(): escape(vdc_name), provider_vdc_ref) - print data headers = vca.vcloud_session.get_vcloud_headers() headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml' response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify, logger=vca.logger) - print response.status_code - print response.content # if we all ok we respond with content otherwise by default None if response.status_code == 201: return response.content return None + + def get_vapp_details_rest(self, vapp_uuid=None): + """ + Method retrieve vapp detail from vCloud director + + Args: + vapp_uuid - is vapp identifier. + + Returns: + The return network uuid or return None + """ + + parsed_respond = {} + + vca = self.connect() + if not vca: + raise vimconn.vimconnConnectionException("self.connect() is failed") + if vapp_uuid is None: + return None + + url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid] + get_vapp_restcall = ''.join(url_list) + if not (not vca.vcloud_session or not vca.vcloud_session.organization): + response = Http.get(url=get_vapp_restcall, + headers=vca.vcloud_session.get_vcloud_headers(), + verify=vca.verify, + logger=vca.logger) + + if response.status_code != requests.codes.ok: + self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall, + response.status_code)) + return parsed_respond + + try: + xmlroot_respond = XmlElementTree.fromstring(response.content) + parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded'] + + namespaces_ovf = {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'} + namespace_vmm = {'vmw': 'http://www.vmware.com/schema/ovf'} + namespace_vm = {'vm': 'http://www.vmware.com/vcloud/v1.5'} + + created_section = xmlroot_respond.find('vm:DateCreated', namespace_vm) + if created_section is not None: + parsed_respond['created'] = created_section.text + + network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm) + if network_section is not None and 'networkName' in network_section.attrib: + parsed_respond['networkname'] = network_section.attrib['networkName'] + + ipscopes_section = \ + xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes', + namespace_vm) + if ipscopes_section is not None: + for ipscope in ipscopes_section: + for scope in ipscope: + tag_key = scope.tag.split("}")[1] + if tag_key == 'IpRanges': + ip_ranges = scope.getchildren() + for ipblock in ip_ranges: + for block in ipblock: + parsed_respond[block.tag.split("}")[1]] = block.text + else: + parsed_respond[tag_key] = scope.text + + # parse children section for other attrib + children_section = xmlroot_respond.find('vm:Children/', namespace_vm) + if children_section is not None: + parsed_respond['name'] = children_section.attrib['name'] + parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] + parsed_respond['deployed'] = children_section.attrib['deployed'] + parsed_respond['status'] = children_section.attrib['status'] + parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1] + network_adapter = children_section.find('vm:NetworkConnectionSection', namespace_vm) + nic_list = [] + for adapters in network_adapter: + adapter_key = adapters.tag.split("}")[1] + if adapter_key == 'PrimaryNetworkConnectionIndex': + parsed_respond['primarynetwork'] = adapters.text + if adapter_key == 'NetworkConnection': + vnic = {} + if 'network' in adapters.attrib: + vnic['network'] = adapters.attrib['network'] + for adapter in adapters: + setting_key = adapter.tag.split("}")[1] + vnic[setting_key] = adapter.text + nic_list.append(vnic) + + for link in children_section: + if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib: + if link.attrib['rel'] == 'screen:acquireTicket': + parsed_respond['acquireTicket'] = link.attrib + if link.attrib['rel'] == 'screen:acquireMksTicket': + parsed_respond['acquireMksTicket'] = link.attrib + + parsed_respond['interfaces'] = nic_list + except: + pass + + return parsed_respond + + def acuire_console(self, vm_uuid=None): + + vca = self.connect() + if not vca: + raise vimconn.vimconnConnectionException("self.connect() is failed") + if vm_uuid is None: + return None + + if not (not vca.vcloud_session or not vca.vcloud_session.organization): + vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid) + console_dict = vm_dict['acquireTicket'] + console_rest_call = console_dict['href'] + + response = Http.post(url=console_rest_call, + headers=vca.vcloud_session.get_vcloud_headers(), + verify=vca.verify, + logger=vca.logger) + + if response.status_code == requests.codes.ok: + return response.content + + return None \ No newline at end of file