X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=vimconn_vmware.py;h=069c0bc8db8b3dd7f3a233e7404477cf301bae9c;hb=1f3a67138592443a8bd68ab936e071cb5bccda55;hp=fb7d49b90072fd3c4783149d97b1c5cee21b04e2;hpb=ef39072eda639958e3ddcf50c99faf9bf45b5b7f;p=osm%2FRO.git diff --git a/vimconn_vmware.py b/vimconn_vmware.py index fb7d49b9..069c0bc8 100644 --- a/vimconn_vmware.py +++ b/vimconn_vmware.py @@ -25,9 +25,10 @@ vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director. mbayramov@vmware.com ''' + +import vimconn import os import traceback - import itertools import requests @@ -53,22 +54,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 - # -1: "Could not be created", # 0: "Unresolved", # 1: "Resolved", @@ -101,68 +97,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,14 +151,15 @@ class vimconnector(): self.admin_user = None self.logger = logging.getLogger('openmano.vim.vmware') + 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 @@ -194,8 +169,6 @@ class vimconnector(): 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 +176,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 +233,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 +243,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 +267,34 @@ 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' - - 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)) + 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)) + + 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 +303,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 org_details_dict.has_key('vdcs'): + 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 org_details_dict.has_key('vdcs'): + 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 +345,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 +386,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 +421,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 +467,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 +481,7 @@ class vimconnector(): Returns the network list of dictionaries: [{}, ...] List can be empty - ''' + """ vca = self.connect() if not vca: @@ -501,11 +517,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") @@ -567,22 +586,22 @@ class vimconnector(): 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'] @@ -616,7 +635,7 @@ class vimconnector(): 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 """ @@ -675,12 +694,7 @@ class vimconnector(): 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 " ################################################################### " - - raise vimconnNotImplemented("Should have implemented this") + raise vimconn.vimconnNotImplemented("Should have implemented this") def catalog_exists(self, catalog_name, catalogs): for catalog in catalogs: @@ -688,13 +702,26 @@ class vimconnector(): 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, @@ -740,8 +767,7 @@ class vimconnector(): media = mediaType.parseString(response.content, True) link = \ filter(lambda link: link.get_rel() == 'upload:default', - media.get_Files().get_File()[0].get_Link())[ - 0] + 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, @@ -764,49 +790,47 @@ class vimconnector(): 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)) - return False - f.close() + links_list = filter(lambda link: link.get_rel() == 'upload:default', + media.get_Files().get_File()[0].get_Link()) + + for link in links_list: + # The OVF file and VMDK must be in a same directory + head, tail = os.path.split(media_file_name) + media_file_name = link.get_href() + if 'ovf' in media_file_name: + print "skiping {}".format(media_file_name) + continue + file_vmdk = head + '/' + os.path.basename(link.get_href()) + os.path.isfile(file_vmdk) + statinfo = os.stat(file_vmdk) + + # in case first element is pointer to OVF. + hrefvmdk = link.get_href().replace("descriptor.ovf", media_file_name) + print("Uploading file {}".format(hrefvmdk)) + + f = open(file_vmdk, '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)) + return False + f.close() return True else: self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}". @@ -821,28 +845,68 @@ class vimconnector(): # TODO add named parameters for readbility return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True) - def get_catalogid(self, catalog_name, catalogs): + 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 + """ + 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): + """ 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. + + Args + path: valid path to OVF file. + Return: if image uploaded correct method will provide image catalog UUID. + """ vca = self.connect() if not vca: raise vimconn.vimconnConnectionException("self.connect() is failed") @@ -853,9 +917,8 @@ class vimconnector(): 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." + 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)) @@ -864,10 +927,10 @@ class vimconnector(): self.logger.info("Creating 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) + raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_name)) result = self.upload_vimimage(vca, catalog_name, filename, path) 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: @@ -884,10 +947,10 @@ class vimconnector(): 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) + raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_name)) result = self.upload_vimimage(vca, catalog_name, filename, path) 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()) @@ -899,13 +962,9 @@ class vimconnector(): 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 @@ -976,7 +1035,7 @@ 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 @@ -1014,48 +1073,73 @@ class vimconnector(): # 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.vimconnUnexpectedResponse( + "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.vimconnUnexpectedResponse( + "new_vminstance(): Failed create vApp {}: 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.vimconnUnexpectedResponse( + "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' + + print image_id + + 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 = None + 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)) - primary_net_id = primary_net['net_id'] - primary_net_name = self.get_network_name_by_id(primary_net_id) - network_mode = primary_net['use'] + 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, 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_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(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) 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(name)) # add first NIC try: @@ -1075,7 +1159,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 +1167,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() @@ -1099,8 +1183,8 @@ class vimconnector(): vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), 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)) ## ## @@ -1186,7 +1270,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 +1315,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 +1358,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: @@ -1315,7 +1401,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 +1414,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 +1433,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,7 +1464,7 @@ 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 @@ -1386,46 +1472,46 @@ class vimconnector(): '''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") + 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") + 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") + raise vimconn.vimconnNotImplemented("Should have implemented this") def new_host(self, host_data): '''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''' '''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)''' '''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''' '''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''' '''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 +1607,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 +1775,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 +1799,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 @@ -2017,7 +2102,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 +2127,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 +2196,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 +2234,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 +2245,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,14 +2259,11 @@ 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