def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None, availability_zone_index=None, availability_zone_list=None): """Adds a VM instance to VIM Params: start: indicates if VM must start or boot in pause mode. Ignored image_id,flavor_id: image and flavor uuid net_list: list of interfaces, each one is a dictionary with: name: net_id: network uuid to connect vpci: virtual vcpi to assign model: interface model, virtio, e2000, ... mac_address: use: 'data', 'bridge', 'mgmt' type: 'virtual', 'PF', 'VF', 'VFnotShared' vim_id: filled/added by this function #TODO ip, security groups Returns the instance identifier """ self.logger.debug( "new_vminstance input: image='{}' flavor='{}' nics='{}'".format(image_id, flavor_id, str(net_list))) try: client = oca.Client(self.user + ':' + self.passwd, self.url) listaTemplate = oca.VmTemplatePool(client) listaTemplate.info() for template in listaTemplate: if str(template.id) == str(flavor_id): cpu = ' CPU = "{}"'.format(template.template.cpu) memory = ' MEMORY = "{}"'.format(template.template.memory) context = ' CONTEXT = [NETWORK = "YES",SSH_PUBLIC_KEY = "$USER[SSH_PUBLIC_KEY]" ]' graphics = ' GRAPHICS = [ LISTEN = "0.0.0.0", TYPE = "VNC" ]' disk = ' DISK = [ IMAGE_ID = {}]'.format(image_id) sched_requeriments = ' SCHED_REQUIREMENTS = "CLUSTER_ID={}"'.format(self.config["cluster"]["id"]) template_updated = cpu + memory + context + graphics + disk + sched_requeriments networkListVim = oca.VirtualNetworkPool(client) networkListVim.info() network = "" for net in net_list: network_found = False for network_existingInVim in networkListVim: if str(net["net_id"]) == str(network_existingInVim.id): net["vim_id"] = network_existingInVim["id"] network = 'NIC = [NETWORK = "{}",NETWORK_UNAME = "{}" ]'.format( network_existingInVim.name, network_existingInVim.uname) network_found = True break if not network_found: raise vimconn.vimconnNotFoundException("Network {} not found".format(net["net_id"])) template_updated += network oca.VmTemplate.update(template, template_updated) self.logger.info( "Instanciating in OpenNebula a new VM name:{} id:{}".format(template.name, template.id)) vminstance_id = template.instantiate(name=name) return str(vminstance_id), None raise vimconn.vimconnNotFoundException("Flavor {} not found".format(flavor_id)) except Exception as e: self.logger.error("Create new vm instance error: " + str(e)) raise vimconn.vimconnException(e)
def subnet_sizes(self, availability_zones, cidr): """Calcualtes possible subnets given CIDR value of VPC """ if availability_zones != 2 and availability_zones != 3: self.logger.debug("Number of AZs should be 2 or 3") raise vimconn.vimconnNotSupportedException("Number of AZs should be 2 or 3") netmasks = ('255.255.252.0', '255.255.254.0', '255.255.255.0', '255.255.255.128') ip = netaddr.IPNetwork(cidr) mask = ip.netmask if str(mask) not in netmasks: self.logger.debug("Netmask " + str(mask) + " not found") raise vimconn.vimconnNotFoundException("Netmask " + str(mask) + " not found") if availability_zones == 2: for n, netmask in enumerate(netmasks): if str(mask) == netmask: subnets = list(ip.subnet(n + 24)) else: for n, netmask in enumerate(netmasks): if str(mask) == netmask: pub_net = list(ip.subnet(n + 24)) pri_subs = pub_net[1:] pub_mask = pub_net[0].netmask pub_split = list(ip.subnet(26)) if (str(pub_mask) == '255.255.255.0') else list(ip.subnet(27)) pub_subs = pub_split[:3] subnets = pub_subs + pri_subs return map(str, subnets)
def get_network(self, net_id): """Obtain network details from the 'net_id' VIM network Return a dict that contains: 'id': (mandatory) VIM network id, that is, net_id 'name': (mandatory) VIM network name 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER' 'error_msg': (optional) text that explains the ERROR status other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param Raises an exception upon error or when network is not found """ try: one = self._new_one_connection() net_pool = one.vnpool.info(-2, -1, -1).VNET net = {} for network in net_pool: if str(network.ID) == str(net_id): net['id'] = network.ID net['name'] = network.NAME net['status'] = "ACTIVE" break if net: return net else: raise vimconn.vimconnNotFoundException( "Network {} not found".format(net_id)) except Exception as e: self.logger.error("Get network " + str(net_id) + " error): " + str(e)) raise vimconn.vimconnException(e)
def delete_vminstance(self, vm_id, created_items=None): """ Removes a VM instance from VIM and its associated elements :param vm_id: VIM identifier of the VM, provided by method new_vminstance :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method action_vminstance :return: None or the same vm_id. Raises an exception on fail """ try: one = self._new_one_connection() one.vm.recover(int(vm_id), 3) vm = None while True: if vm is not None and vm.LCM_STATE == 0: break else: vm = one.vm.info(int(vm_id)) except pyone.OneNoExistsException as e: self.logger.info("The vm " + str(vm_id) + " does not exist or is already deleted") raise vimconn.vimconnNotFoundException( "The vm {} does not exist or is already deleted".format(vm_id)) except Exception as e: self.logger.error("Delete vm instance " + str(vm_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
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 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': memory in MB 'vcpus': number of virtual cpus #todo: complete parameters for EPA Returns the flavor_id or raises a vimconnNotFoundException """ self.logger.debug("Getting flavor id from data") try: flavor = None for key, values in self.flavor_info.iteritems(): if (values["ram"], values["cpus"], values["disk"]) == ( flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"]): flavor = (key, values) break elif (values["ram"], values["cpus"], values["disk"]) >= ( flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"]): if not flavor: flavor = (key, values) else: if (flavor[1]["ram"], flavor[1]["cpus"], flavor[1]["disk"]) >= ( values["ram"], values["cpus"], values["disk"]): flavor = (key, values) if flavor: return flavor[0] raise vimconn.vimconnNotFoundException("Cannot find flavor with this flavor ID/Name") except Exception as e: self.format_vimconn_exception(e)
def get_image_id_from_path(self, path): '''Get the image id from image path in the VIM database. Returns the image_id ''' try: self._reload_connection() images = self.nova.images.list() for image in images: if image.metadata.get("location")==path: return image.id raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path)) except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: self._format_exception(e)
def get_flavor(self, flavor_id): # Esta correcto """Obtain flavor details from the VIM""" try: client = oca.Client(self.user + ':' + self.passwd, self.url) listaTemplate = oca.VmTemplatePool(client) listaTemplate.info() for template in listaTemplate: if str(template.id) == str(flavor_id): return {'id': template.id, 'name': template.name} raise vimconn.vimconnNotFoundException("Flavor {} not found".format(flavor_id)) except Exception as e: self.logger.error("get flavor " + str(flavor_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
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 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 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_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("Getting instance type") try: if flavor_id in self.flavor_info: return self.flavor_info[flavor_id] else: raise vimconn.vimconnNotFoundException("Cannot find flavor with this flavor ID/Name") except Exception as e: self.format_vimconn_exception(e)
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 get_network(self, net_id): """Obtain network details from the 'net_id' VIM network Return a dict that contains: 'id': (mandatory) VIM network id, that is, net_id 'name': (mandatory) VIM network name 'status': (mandatory) can be 'ACTIVE', 'INACTIVE', 'DOWN', 'BUILD', 'ERROR', 'VIM_ERROR', 'OTHER' 'error_msg': (optional) text that explains the ERROR status other VIM specific fields: (optional) whenever possible using the same naming of filter_dict param Raises an exception upon error or when network is not found """ self.logger.debug('get_network: {}'.format(net_id)) res = self.get_network_list(filter_dict={'id': net_id}) if len(res) == 0: raise vimconn.vimconnNotFoundException( "Network {} not found at VIM".format(net_id)) return res[0]
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 delete_network(self, net_id): """Deletes a tenant network from VIM Returns the network identifier or raises an exception upon error or when network is not found """ self.logger.debug('delete_network: {}'.format(net_id)) try: self.fos_api.network.remove_network(net_id) except fimerrors.FIMNotFoundException as fnfe: raise vimconn.vimconnNotFoundException( "Network {} not found at VIM (already deleted?). Error {}". format(net_id, fnfe)) except Exception as e: raise vimconn.vimconnException( "Cannot delete network {} from VIM. Error {}".format( net_id, e)) return net_id
def delete_flavor(self, flavor_id): """ Deletes a tenant flavor from VIM Returns the old flavor_id """ try: client = oca.Client(self.user + ':' + self.passwd, self.url) listaTemplate = oca.VmTemplatePool(client) listaTemplate.info() self.logger.info("Deleting VIM flavor DELETE {}".format(self.url)) for template in listaTemplate: if str(template.id) == str(flavor_id): template.delete() return template.id raise vimconn.vimconnNotFoundException("Flavor {} not found".format(flavor_id)) except Exception as e: self.logger.error("Delete flavor " + str(flavor_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
def get_flavor(self, flavor_id): # Esta correcto """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 """ try: one = self._new_one_connection() template = one.template.info(int(flavor_id)) if template is not None: return {'id': template.ID, 'name': template.NAME} raise vimconn.vimconnNotFoundException( "Flavor {} not found".format(flavor_id)) except Exception as e: self.logger.error("get flavor " + str(flavor_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
def delete_tenant(self, tenant_id): """Delete a tenant from VIM. Returns the old tenant identifier""" try: client = oca.Client(self.user + ':' + self.passwd, self.url) group_list = oca.GroupPool(client) user_list = oca.UserPool(client) group_list.info() user_list.info() for group in group_list: if str(group.id) == str(tenant_id): for user in user_list: if str(user.name) == str(self.user): self._delete_secondarygroup(user.id, group.id) group.delete(client) return None raise vimconn.vimconnNotFoundException("Group {} not found".format(tenant_id)) except Exception as e: self.logger.error("Delete tenant " + str(tenant_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
def delete_vminstance(self, vm_id, created_items=None): """Removes a VM instance from VIM, returns the deleted vm_id""" try: client = oca.Client(self.user + ':' + self.passwd, self.url) vm_pool = oca.VirtualMachinePool(client) vm_pool.info() vm_exist = False for i in vm_pool: if str(i.id) == str(vm_id): vm_exist = True break if not vm_exist: self.logger.info("The vm " + str(vm_id) + " does not exist or is already deleted") raise vimconn.vimconnNotFoundException("The vm {} does not exist or is already deleted".format(vm_id)) params = '<?xml version="1.0"?> \ <methodCall>\ <methodName>one.vm.recover</methodName>\ <params>\ <param>\ <value><string>{}:{}</string></value>\ </param>\ <param>\ <value><int>{}</int></value>\ </param>\ <param>\ <value><int>{}</int></value>\ </param>\ </params>\ </methodCall>'.format(self.user, self.passwd, str(vm_id), str(3)) r = requests.post(self.url, params) obj = untangle.parse(str(r.content)) response_success = obj.methodResponse.params.param.value.array.data.value[0].boolean.cdata.encode('utf-8') response = obj.methodResponse.params.param.value.array.data.value[1].i4.cdata.encode('utf-8') # response can be the resource ID on success or the error string on failure. response_error_code = obj.methodResponse.params.param.value.array.data.value[2].i4.cdata.encode('utf-8') if response_success.lower() == "true": return response else: raise vimconn.vimconnException("vm {} cannot be deleted with error_code {}: {}".format(vm_id, response_error_code, response)) except Exception as e: self.logger.error("Delete vm instance " + str(vm_id) + " error: " + str(e)) raise vimconn.vimconnException(e)
def get_network(self, net_id): """Obtain network details of network id""" try: client = oca.Client(self.user + ':' + self.passwd, self.url) networkList = oca.VirtualNetworkPool(client) networkList.info() net = {} for network in networkList: if str(network.id) == str(net_id): net['id'] = net_id net['name'] = network.name net['status'] = "ACTIVE" break if net: return net else: raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id)) except Exception as e: self.logger.error("Get network " + str(net_id) + " error): " + str(e)) raise vimconn.vimconnException(e)
def delete_network(self, net_id): """Deletes a tenant network from VIM Returns the network identifier """ try: # self.delete_bridge_host() client = oca.Client(self.user + ':' + self.passwd, self.url) networkList = oca.VirtualNetworkPool(client) networkList.info() network_deleted = False for network in networkList: if str(network.id) == str(net_id): oca.VirtualNetwork.delete(network) network_deleted = True if network_deleted: return net_id else: raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id)) except Exception as e: self.logger.error("Delete network " + str(net_id) + "error: " + str(e)) raise vimconn.vimconnException(e)
def get_network(self, net_id): '''Obtain details of network from VIM Returns the network information from a network id''' self.logger.debug(" Getting tenant network %s from VIM", net_id) filter_dict={"id": net_id} net_list = self.get_network_list(filter_dict) if len(net_list)==0: raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id)) elif len(net_list)>1: raise vimconn.vimconnConflictException("Found more than one network with this criteria") net = net_list[0] subnets=[] for subnet_id in net.get("subnets", () ): try: subnet = self.neutron.show_subnet(subnet_id) except Exception as e: self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e))) subnet = {"id": subnet_id, "fault": str(e)} subnets.append(subnet) net["subnets"] = subnets return net
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_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None, availability_zone_index=None, availability_zone_list=None): """Adds a VM instance to VIM Params: 'start': (boolean) indicates if VM must start or created in pause mode. 'image_id','flavor_id': image and flavor VIM id to use for the VM 'net_list': list of interfaces, each one is a dictionary with: 'name': (optional) name for the interface. 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ... 'mac_address': (optional) mac address to assign to this interface 'ip_address': (optional) IP address to assign to this interface #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF 'type': (mandatory) can be one of: 'virtual', in this case always connected to a network of type 'net_type=bridge' 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it can created unconnected 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity. 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs are allocated on the same physical NIC 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing or True, it must apply the default VIM behaviour After execution the method will add the key: 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this interface. 'net_list' is modified 'cloud_config': (optional) dictionary with: 'key-pairs': (optional) list of strings with the public key to be inserted to the default user 'users': (optional) list of users to be inserted, each item is a dict with: 'name': (mandatory) user name, 'key-pairs': (optional) list of strings with the public key to be inserted to the user 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init, or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file 'config-files': (optional). List of files to be transferred. Each item is a dict with: 'dest': (mandatory) string with the destination absolute path 'encoding': (optional, by default text). Can be one of: 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64' 'content' (mandatory): string with the content of the file 'permissions': (optional) string with file permissions, typically octal notation '0644' 'owner': (optional) file owner, string with the format 'owner:group' 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk) 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with: 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted 'size': (mandatory) string with the size of the disk in GB availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if availability_zone_index is None Returns a tuple with the instance identifier and created_items or raises an exception on error created_items can be None or a dictionary where this method can include key-values that will be passed to the method delete_vminstance and action_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. """ self.logger.debug('new_vminstance with rgs: {}'.format(locals())) fdu_uuid = '{}'.format(uuid.uuid4()) flv = self.fos_api.flavor.get(flavor_id) img = self.fos_api.image.get(image_id) if flv is None: raise vimconn.vimconnNotFoundException( "Flavor {} not found at VIM".format(flavor_id)) if img is None: raise vimconn.vimconnNotFoundException( "Image {} not found at VIM".format(image_id)) created_items = {'fdu_id': '', 'node_id': '', 'connection_points': []} fdu_desc = { 'name': name, 'uuid': fdu_uuid, 'computation_requirements': flv, 'image': img, 'hypervisor': self.hv, 'migration_kind': 'LIVE', 'interfaces': [], 'io_ports': [], 'connection_points': [], 'depends_on': [] } nets = [] cps = [] intf_id = 0 for n in net_list: cp_id = '{}'.format(uuid.uuid4()) n.update({'vim_id': cp_id}) pair_id = n.get('net_id') cp_d = {'uuid': cp_id, 'pair_id': pair_id} intf_d = { 'name': n.get('name', 'eth{}'.format(intf_id)), 'is_mgmt': False, 'if_type': 'INTERNAL', 'virtual_interface': { 'intf_type': n.get('model', 'VIRTIO'), 'vpci': n.get('vpci', '0:0:0'), 'bandwidth': int(n.get('bw', 100)) } } if n.get('mac_address', None) is not None: intf_d['mac_address'] = n['mac_address'] created_items['connection_points'].append(cp_id) fdu_desc['connection_points'].append(cp_d) fdu_desc['interfaces'].append(intf_d) intf_id = intf_id + 1 if cloud_config is not None: configuration = {'conf_type': 'CLOUD_INIT'} if cloud_config.get('user-data') is not None: configuration.update({'script': cloud_config.get('user-data')}) if cloud_config.get('key-pairs') is not None: configuration.update( {'ssh_keys': cloud_config.get('key-pairs')}) if 'script' in configuration: fdu_desc.update({'configuration': configuration}) ### NODE Selection ### # Infrastructure info # nodes dict with # uuid -> node uuid # computational capabilities -> cpu, ram, and disk available # hypervisors -> list of available hypervisors (eg. KVM, LXD, BARE) # # # UPDATING AVAILABLE INFRASTRUCTURE if len(self.nodes) == 0: nodes_id = self.fos_api.node.list() else: nodes_id = self.nodes nodes = [] for n in nodes_id: n_info = self.fos_api.node.info(n) if n_info is None: continue n_plugs = [] for p in self.fos_api.node.plugins(n): n_plugs.append(self.fos_api.plugin.info(n, p)) n_cpu_number = len(n_info.get('cpu')) n_cpu_arch = n_info.get('cpu')[0].get('arch') n_cpu_freq = n_info.get('cpu')[0].get('frequency') n_ram = n_info.get('ram').get('size') n_disk_size = sorted( list( filter(lambda x: 'sda' in x['local_address'], n_info.get('disks'))), key=lambda k: k['dimension'])[-1].get('dimension') hvs = [] for p in n_plugs: if p.get('type') == 'runtime': hvs.append(p.get('name')) ni = { 'uuid': n, 'computational_capabilities': { 'cpu_count': n_cpu_number, 'cpu_arch': n_cpu_arch, 'cpu_freq': n_cpu_freq, 'ram_size': n_ram, 'disk_size': n_disk_size }, 'hypervisors': hvs } nodes.append(ni) # NODE SELECTION compatible_nodes = [] for n in nodes: if fdu_desc.get('hypervisor') in n.get('hypervisors'): n_comp = n.get('computational_capabilities') f_comp = fdu_desc.get('computation_requirements') if f_comp.get('cpu_arch') == n_comp.get('cpu_arch'): if f_comp.get('cpu_min_count') <= n_comp.get( 'cpu_count') and f_comp.get( 'ram_size_mb') <= n_comp.get('ram_size'): if f_comp.get('disk_size_gb') <= n_comp.get( 'disk_size'): compatible_nodes.append(n) if len(compatible_nodes) == 0: raise vimconn.vimconnConflictException("No available nodes at VIM") selected_node = random.choice(compatible_nodes) created_items.update({ 'fdu_id': fdu_uuid, 'node_id': selected_node.get('uuid') }) self.logger.debug('FOS Node {} FDU Descriptor: {}'.format( selected_node.get('uuid'), fdu_desc)) try: self.fos_api.fdu.onboard(fdu_desc) instanceid = self.fos_api.fdu.instantiate( fdu_uuid, selected_node.get('uuid')) created_items.update({'instance_id': instanceid}) self.fdu_node_map.update({instanceid: selected_node.get('uuid')}) self.logger.debug('new_vminstance return: {}'.format( (fdu_uuid, created_items))) return (instanceid, created_items) except fimerrors.FIMAResouceExistingException as free: raise vimconn.vimconnConflictException( "VM already exists at VIM. Error {}".format(free)) except Exception as e: raise vimconn.vimconnException( "Error while instantiating VM {}. Error {}".format(name, e))
def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None, availability_zone_index=None, availability_zone_list=None): """Adds a VM instance to VIM Params: 'start': (boolean) indicates if VM must start or created in pause mode. 'image_id','flavor_id': image and flavor VIM id to use for the VM 'net_list': list of interfaces, each one is a dictionary with: 'name': (optional) name for the interface. 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ... 'mac_address': (optional) mac address to assign to this interface 'ip_address': (optional) IP address to assign to this interface #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF 'type': (mandatory) can be one of: 'virtual', in this case always connected to a network of type 'net_type=bridge' 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it can created unconnected 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity. 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs are allocated on the same physical NIC 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing or True, it must apply the default VIM behaviour After execution the method will add the key: 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this interface. 'net_list' is modified 'cloud_config': (optional) dictionary with: 'key-pairs': (optional) list of strings with the public key to be inserted to the default user 'users': (optional) list of users to be inserted, each item is a dict with: 'name': (mandatory) user name, 'key-pairs': (optional) list of strings with the public key to be inserted to the user 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init, or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file 'config-files': (optional). List of files to be transferred. Each item is a dict with: 'dest': (mandatory) string with the destination absolute path 'encoding': (optional, by default text). Can be one of: 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64' 'content' (mandatory): string with the content of the file 'permissions': (optional) string with file permissions, typically octal notation '0644' 'owner': (optional) file owner, string with the format 'owner:group' 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk) 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with: 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted 'size': (mandatory) string with the size of the disk in GB availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if availability_zone_index is None Returns a tuple with the instance identifier and created_items or raises an exception on error created_items can be None or a dictionary where this method can include key-values that will be passed to the method delete_vminstance and action_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. """ self.logger.debug( "new_vminstance input: image='{}' flavor='{}' nics='{}'".format( image_id, flavor_id, str(net_list))) try: one = self._new_one_connection() template_vim = one.template.info(int(flavor_id), True) disk_size = str(template_vim.TEMPLATE["DISK"]["SIZE"]) one = self._new_one_connection() template_updated = "" for net in net_list: net_in_vim = one.vn.info(int(net["net_id"])) net["vim_id"] = str(net_in_vim.ID) network = 'NIC = [NETWORK = "{}",NETWORK_UNAME = "{}" ]'.format( net_in_vim.NAME, net_in_vim.UNAME) template_updated += network template_updated += "DISK = [ IMAGE_ID = {},\n SIZE = {}]".format( image_id, disk_size) if isinstance(cloud_config, dict): if cloud_config.get("key-pairs"): context = 'CONTEXT = [\n NETWORK = "YES",\n SSH_PUBLIC_KEY = "' for key in cloud_config["key-pairs"]: context += key + '\n' # if False: # context += '"\n USERNAME = '******'"]' template_updated += context vm_instance_id = one.template.instantiate(int(flavor_id), name, False, template_updated) self.logger.info( "Instanciating in OpenNebula a new VM name:{} id:{}".format( name, flavor_id)) return str(vm_instance_id), None except pyone.OneNoExistsException as e: self.logger.error("Network with id " + str(e) + " not found: " + str(e)) raise vimconn.vimconnNotFoundException(e) except Exception as e: self.logger.error("Create new vm instance error: " + str(e)) raise vimconn.vimconnException(e)
def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}): """ Params: uuid - id asigned to this VIM name - name assigned to this VIM, can be used for logging tenant_id - ID to be used for tenant tenant_name - name of tenant to be used VIM tenant to be used url_admin - optional, url used for administrative tasks user - credentials of the VIM user passwd - credentials of the VIM user log_level - if must use a different log_level than the general one config - dictionary with misc VIM information region_name - name of region to deploy the instances vpc_cidr_block - default CIDR block for VPC security_groups - default security group to specify this instance persistent_info - dict where the class can store information that will be available among class destroy/creation cycles. This info is unique per VIM/credential. At first call it will contain an empty dict. Useful to store login/tokens information for speed up communication """ vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config, persistent_info) self.persistent_info = persistent_info self.a_creds = {} if user: self.a_creds['aws_access_key_id'] = user else: raise vimconn.vimconnAuthException("Username is not specified") if passwd: self.a_creds['aws_secret_access_key'] = passwd else: raise vimconn.vimconnAuthException("Password is not specified") if 'region_name' in config: self.region = config.get('region_name') else: raise vimconn.vimconnNotFoundException( "AWS region_name is not specified at config") self.vpc_data = {} self.subnet_data = {} self.conn = None self.conn_vpc = None self.account_id = None self.vpc_id = self.get_tenant_list()[0]['id'] # we take VPC CIDR block if specified, otherwise we use the default CIDR # block suggested by AWS while creating instance self.vpc_cidr_block = '10.0.0.0/24' if tenant_id: self.vpc_id = tenant_id if 'vpc_cidr_block' in config: self.vpc_cidr_block = config['vpc_cidr_block'] self.security_groups = None if 'security_groups' in config: self.security_groups = config['security_groups'] self.key_pair = None if 'key_pair' in config: self.key_pair = config['key_pair'] self.flavor_info = None if 'flavor_info' in config: flavor_data = config.get('flavor_info') if isinstance(flavor_data, str): try: with open(flavor_data[1:], 'r') as stream: self.flavor_info = yaml.load(stream) except yaml.YAMLError as e: self.flavor_info = None raise vimconn.vimconnException( "Bad format at file '{}': {}".format( flavor_data[1:], e)) except IOError as e: raise vimconn.vimconnException( "Error reading file '{}': {}".format( flavor_data[1:], e)) elif isinstance(flavor_data, dict): self.flavor_info = flavor_data self.logger = logging.getLogger('openmano.vim.aws') if log_level: self.logger.setLevel(getattr(logging, log_level))