def _format_exception(self, exception): '''Transform a keystone, nova, neutron exception into a vimconn exception''' if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed, neClient.exceptions.ConnectionFailed)): raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception)) elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException, neExceptions.NeutronException, nvExceptions.BadRequest)): raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + str(exception)) elif isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound)): raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + str(exception)) elif isinstance(exception, nvExceptions.Conflict): raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + str(exception)) else: # () raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))
def new_image(self, image_dict): """ Adds a tenant image to VIM. imge_dict is a dictionary with: name: name disk_format: qcow2, vhd, vmdk, raw (by default), ... location: path or URI public: "yes" or "no" metadata: metadata of the image Returns the image id or raises an exception if failed """ self.logger.debug('VIM new_image with args: {}'.format(locals())) img_id = '{}'.format(uuid.uuid4()) desc = { 'name': image_dict.get('name'), 'uuid': img_id, 'uri': image_dict.get('location') } try: self.fos_api.image.add(desc) except fimerrors.FIMAResouceExistingException as free: raise vimconn.vimconnConflictException( "Image {} already exist at VIM. Error {}".format(img_id, free)) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) return img_id
def get_flavor_id_from_data(self, flavor_dict): """Obtain flavor id that match the flavor description Params: 'flavor_dict': dictionary that contains: 'disk': main hard disk in GB 'ram': meomry in MB 'vcpus': number of virtual cpus #TODO: complete parameters for EPA Returns the flavor_id or raises a vimconnNotFoundException """ self.logger.debug('VIM get_flavor_id_from_data with args : {}'.format( locals())) try: flvs = self.fos_api.flavor.list() except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) r = [ x.get('uuid') for x in flvs if (x.get('cpu_min_count') == flavor_dict.get('vcpus') and x.get('ram_size_mb') == flavor_dict.get('ram') and x.get('storage_size_gb') == flavor_dict.get('disk')) ] if len(r) == 0: raise vimconn.vimconnNotFoundException("No flavor found") return r[0]
def format_vimconn_exception(self, e): """Params: an Exception object Returns: Raises the exception 'e' passed in mehtod parameters """ self.conn = None self.conn_vpc = None raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e))
def format_vimconn_exception(self, e): """ Params: an Exception object :param e: :return: Raises the proper vimconnException """ self.conn = None self.conn_vnet = None raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e))
def get_network_list(self, filter_dict={}): """Obtain tenant networks of VIM Params: 'filter_dict' (optional) contains entries to return only networks that matches ALL entries: name: string => returns only networks with this name id: string => returns networks with this VIM id, this imply returns one network at most shared: boolean >= returns only networks that are (or are not) shared tenant_id: sting => returns only networks that belong to this tenant/project ,#(not used yet) admin_state_up: boolean => returns only networks that are (or are not) in admin state active #(not used yet) status: 'ACTIVE','ERROR',... => filter networks that are on this status Returns the network list of dictionaries. each dictionary contains: 'id': (mandatory) VIM network id 'name': (mandatory) VIM network name 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER' 'network_type': (optional) can be 'vxlan', 'vlan' or 'flat' 'segmentation_id': (optional) in case network_type is vlan or vxlan this field contains the segmentation id 'error_msg': (optional) text that explains the ERROR status other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param List can be empty if no network map the filter_dict. Raise an exception only upon VIM connectivity, authorization, or some other unspecific error """ self.logger.debug('get_network_list: {}'.format(filter_dict)) res = [] try: nets = self.fos_api.network.list() except Exception as e: raise vimconn.vimconnConnectionException( "Cannot get network list from VIM, connection error. Error {}". format(e)) filters = [ partial(self.__name_filter, filter_name=filter_dict.get('name')), partial(self.__id_filter, filter_id=filter_dict.get('id')) ] r1 = [] for n in nets: match = True for f in filters: match = match and f(n) if match: r1.append(n) for n in r1: osm_net = { 'id': n.get('uuid'), 'name': n.get('name'), 'status': 'ACTIVE' } res.append(osm_net) return res
def get_vminstance(self, vm_id): """Returns the VM instance information from VIM""" self.logger.debug('VIM get_vminstance with args: {}'.format(locals())) try: intsinfo = self.fos_api.fdu.instance_info(vm_id) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) if intsinfo is None: raise vimconn.vimconnNotFoundException( 'VM with id {} not found!'.format(vm_id)) return intsinfo
def delete_flavor(self, flavor_id): """Deletes a tenant flavor from VIM identify by its id Returns the used id or raise an exception""" try: self.fos_api.flavor.remove(flavor_id) except fimerrors.FIMNotFoundException as fnfe: raise vimconn.vimconnNotFoundException( "Flavor {} not found at VIM (already deleted?). Error {}". format(flavor_id, fnfe)) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) return flavor_id
def check_vim_connectivity(self): """Checks VIM can be reached and user credentials are ok. Returns None if success or raised vimconnConnectionException, vimconnAuthException, ... """ try: self.fos_api.check() return None except fimerrors.FIMAuthExcetpion as fae: raise vimconn.vimconnAuthException( "Unable to authenticate to the VIM. Error {}".format(fae)) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e))
def get_flavor(self, flavor_id): """Obtain flavor details from the VIM Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } Raises an exception upon error or if not found """ self.logger.debug('VIM get_flavor with args: {}'.format(locals())) try: r = self.fos_api.flavor.get(flavor_id) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) if r is None: raise vimconn.vimconnNotFoundException("Flavor not found at VIM") return {'id': r.get('uuid'), 'name': r.get('name'), 'fos': r}
def get_image_id_from_path(self, path): """Get the image id from image path in the VIM database. Returns the image_id or raises a vimconnNotFoundException """ self.logger.debug('VIM get_image_id_from_path with args: {}'.format( locals())) try: imgs = self.fos_api.image.list() except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) res = [x.get('uuid') for x in imgs if x.get('uri') == path] if len(res) == 0: raise vimconn.vimconnNotFoundException( "Image with this path was not found") return res[0]
def get_image_list(self, filter_dict={}): """Obtain tenant images from VIM Filter_dict can be: name: image name id: image uuid checksum: image checksum location: image path Returns the image list of dictionaries: [{<the fields at Filter_dict plus some VIM specific>}, ...] List can be empty """ self.logger.debug('VIM get_image_list args: {}'.format(locals())) r = [] try: fimgs = self.fos_api.image.list() except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) filters = [ partial(self.__name_filter, filter_name=filter_dict.get('name')), partial(self.__id_filter, filter_id=filter_dict.get('id')), partial(self.__checksum_filter, filter_checksum=filter_dict.get('checksum')) ] r1 = [] for i in fimgs: match = True for f in filters: match = match and f(i) if match: r1.append(i) for i in r1: img_info = { 'name': i.get('name'), 'id': i.get('uuid'), 'checksum': i.get('checksum'), 'location': i.get('uri'), 'fos': i } r.append(img_info) return r
def new_flavor(self, flavor_data): """Adds a tenant flavor to VIM flavor_data contains a dictionary with information, keys: name: flavor name ram: memory (cloud type) in MBytes vpcus: cpus (cloud type) extended: EPA parameters - numas: #items requested in same NUMA memory: number of 1G huge pages memory paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa - name: interface name dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC bandwidth: X Gbps; requested guarantee bandwidth vpci: requested virtual PCI address disk: disk size is_public: #TODO to concrete Returns the flavor identifier""" self.logger.debug('VIM new_flavor with args: {}'.format(locals())) flv_id = '{}'.format(uuid.uuid4()) desc = { 'uuid': flv_id, 'name': flavor_data.get('name'), 'cpu_arch': self.arch, 'cpu_min_count': flavor_data.get('vcpus'), 'cpu_min_freq': 0.0, 'ram_size_mb': float(flavor_data.get('ram')), 'storage_size_gb': float(flavor_data.get('disk')) } try: self.fos_api.flavor.add(desc) except fimerrors.FIMAResouceExistingException as free: raise vimconn.vimconnConflictException( "Flavor {} already exist at VIM. Error {}".format( flv_id, free)) except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e)) return flv_id
def action_vminstance(self, vm_id, action_dict, created_items={}): """ Send and action over a VM instance. Returns created_items if the action was successfully sent to the VIM. created_items is a dictionary with items that :param vm_id: VIM identifier of the VM, provided by method new_vminstance :param action_dict: dictionary with the action to perform :param created_items: provided by method new_vminstance is a dictionary with key-values that will be passed to the method delete_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same as not present. This method can modify this value :return: None, or a console dict """ self.logger.debug('VIM action_vminstance with args: {}'.format( locals())) nid = self.fdu_node_map.get(vm_id) if nid is None: raise vimconn.vimconnNotFoundException('No node for this VM') try: fdu_info = self.fos_api.fdu.instance_info(vm_id) if "start" in action_dict: if fdu_info.get('status') == 'CONFIGURE': self.fos_api.fdu.start(vm_id) elif fdu_info.get('status') == 'PAUSE': self.fos_api.fdu.resume(vm_id) else: raise vimconn.vimconnConflictException( "Cannot start from this state") elif "pause" in action_dict: if fdu_info.get('status') == 'RUN': self.fos_api.fdu.pause(vm_id) else: raise vimconn.vimconnConflictException( "Cannot pause from this state") elif "resume" in action_dict: if fdu_info.get('status') == 'PAUSE': self.fos_api.fdu.resume(vm_id) else: raise vimconn.vimconnConflictException( "Cannot resume from this state") elif "shutoff" in action_dict or "shutdown" or "forceOff" in action_dict: if fdu_info.get('status') == 'RUN': self.fos_api.fdu.stop(vm_id) else: raise vimconn.vimconnConflictException( "Cannot shutoff from this state") elif "terminate" in action_dict: if fdu_info.get('status') == 'RUN': self.fos_api.fdu.stop(vm_id) self.fos_api.fdu.clean(vm_id) self.fos_api.fdu.undefine(vm_id) # self.fos_api.fdu.offload(vm_id) elif fdu_info.get('status') == 'CONFIGURE': self.fos_api.fdu.clean(vm_id) self.fos_api.fdu.undefine(vm_id) # self.fos_api.fdu.offload(vm_id) elif fdu_info.get('status') == 'PAUSE': self.fos_api.fdu.resume(vm_id) self.fos_api.fdu.stop(vm_id) self.fos_api.fdu.clean(vm_id) self.fos_api.fdu.undefine(vm_id) # self.fos_api.fdu.offload(vm_id) else: raise vimconn.vimconnConflictException( "Cannot terminate from this state") elif "rebuild" in action_dict: raise vimconnNotImplemented("Rebuild not implememnted") elif "reboot" in action_dict: if fdu_info.get('status') == 'RUN': self.fos_api.fdu.stop(vm_id) self.fos_api.fdu.start(vm_id) else: raise vimconn.vimconnConflictException( "Cannot reboot from this state") except Exception as e: raise vimconn.vimconnConnectionException( "VIM not reachable. Error {}".format(e))
def new_image(self,image_dict): ''' Adds a tenant image to VIM. imge_dict is a dictionary with: name: name disk_format: qcow2, vhd, vmdk, raw (by default), ... location: path or URI public: "yes" or "no" metadata: metadata of the image Returns the image_id ''' #using version 1 of glance client glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds) #TODO check k_creds vs n_creds retry=0 max_retries=3 while retry<max_retries: retry+=1 try: self._reload_connection() #determine format http://docs.openstack.org/developer/glance/formats.html if "disk_format" in image_dict: disk_format=image_dict["disk_format"] else: #autodiscover base on extention if image_dict['location'][-6:]==".qcow2": disk_format="qcow2" elif image_dict['location'][-4:]==".vhd": disk_format="vhd" elif image_dict['location'][-5:]==".vmdk": disk_format="vmdk" elif image_dict['location'][-4:]==".vdi": disk_format="vdi" elif image_dict['location'][-4:]==".iso": disk_format="iso" elif image_dict['location'][-4:]==".aki": disk_format="aki" elif image_dict['location'][-4:]==".ari": disk_format="ari" elif image_dict['location'][-4:]==".ami": disk_format="ami" else: disk_format="raw" self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location']) if image_dict['location'][0:4]=="http": new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes", container_format="bare", location=image_dict['location'], disk_format=disk_format) else: #local path with open(image_dict['location']) as fimage: new_image = glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes", container_format="bare", data=fimage, disk_format=disk_format) #insert metadata. We cannot use 'new_image.properties.setdefault' #because nova and glance are "INDEPENDENT" and we are using nova for reading metadata new_image_nova=self.nova.images.find(id=new_image.id) new_image_nova.metadata.setdefault('location',image_dict['location']) metadata_to_load = image_dict.get('metadata') if metadata_to_load: for k,v in yaml.load(metadata_to_load).iteritems(): new_image_nova.metadata.setdefault(k,v) return new_image.id except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e: self._format_exception(e) except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError) as e: if retry==max_retries: continue self._format_exception(e) except IOError as e: #can not open the file raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'], http_code=vimconn.HTTP_Bad_Request)