def delete_ip_info_from_database(inst_name, vif_index): """ delete the ip in database :param inst_name: :param vif_index: :return: """ log.info("Delete vif [%s] IP information from database.", vif_index) sync_data = {} if vif_index == "0": sync_data["first_ip"] = None elif vif_index == "1": sync_data["second_ip"] = None else: log.info("No IP with vif index [%s] in database, return.", vif_index) return True db_driver = DbFactory.get_db_driver("VirtHost") try: json_data = json.dumps(sync_data) ret = db_driver.update(hostname=inst_name, data=sync_data) except Exception as error: log.warn("Delete ip information raise error: %s", error) ret = False if not ret: log.warn("Delete IP information from database with ret: [%s], data: %s", ret, sync_data) return ret
def update_ip_infor_to_database(inst_name, vif_index=None, ip=None, host_ip=None): """ As the IP for xenserver'VM is not accessable when it is down, so update it with user's input :param inst_name: :param vif_index: vif index :param ip: the IP on vif :param host_ip: Host server IP :return: """ log.info("Update [%s] IP information [%s, %s] to database.", inst_name, vif_index, ip) sync_data = {} if host_ip: sync_data['vm_host_ip'] = host_ip if vif_index == "0": sync_data["first_ip"] = ip elif vif_index == "1": sync_data["second_ip"] = ip else: log.warn("Database only record the first and second IP for VM.") if not sync_data: return True db_driver = DbFactory.get_db_driver("VirtHost") try: #json_data = json.dumps(sync_data) ret = db_driver.update(hostname=inst_name, data=sync_data) except Exception as error: log.exception("update IP information raise error: %s", error) ret = False if not ret: log.warn("Update IP information to database with ret: [%s], data: %s", ret, sync_data) return ret
def get_host_plat_info(self): """ Return HV platform info This needs root permission to do """ ret_plat_dict = {} hv_root_hdl = self._get_root_handler() try: sys_info_xml = hv_root_hdl.getSysinfo(0) except libvirtError as e: log.debug(str(e)) log.warn("Could not get platform/system info") return ret_plat_dict finally: self._delete_root_handler() # Return as XML format sys_node = xmlEtree.ElementTree(xmlEtree.fromstring(sys_info_xml)).find('system') for item in sys_node: if item.attrib.get('name') == 'manufacturer': ret_plat_dict['vendor_name'] = item.text if item.attrib.get('name') == 'product': ret_plat_dict['product_name'] = item.text if item.attrib.get('name') == 'serial': ret_plat_dict['serial_number'] = item.text return ret_plat_dict
def get_host_cpu_info(self): """ Return HV CPU info """ ret_cpu_dict = {} hv_handler = self.get_handler() try: # https://libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/ch03s04s03.html hv_info = hv_handler.getInfo() except libvirtError as e: log.debug(str(e)) log.warn("Could not get CPU info") return ret_cpu_dict ret_cpu_dict['cpu_model'] = str(hv_info[0]) ret_cpu_dict['cpu_cores'] = hv_info[2] # return MHz ret_cpu_dict['cpu_speed'] = int(hv_info[3]) # number of NUMA nodes * number of sockets per node ret_cpu_dict['cpu_sockets'] = int(hv_info[4]) * int(hv_info[5]) ret_cpu_dict['cores_per_socket'] = int(hv_info[6]) # number of threads per core ret_cpu_dict['thread_per_core'] = int(hv_info[7]) return ret_cpu_dict
def get_all_disk(self, inst_name): """ {'0': {'disk_size': 20.0, 'device_name': 'xvda'}, '3': {'disk_size': 0, 'device_name': 'xvdd'}} :param inst_name: :return: return a dict with infor about all the virtual disk number, eg, 1,2, etc and its name in guest, eg:vda """ disk_list = self.__get_disk_elements_list(inst_name=inst_name) all_disk_info = {} for disk_num, disk_elment in enumerate(disk_list): device_name = disk_elment.find('target').get('dev') file_path = disk_elment.find('source').get('file') log.debug("Disks on domain [%s]: disk path: %s", inst_name, file_path) try: volume_info = self._hypervisor_handler.storageVolLookupByPath(file_path).info() except libvirtError as error: log.warn("Exception raise in get all disk when look up storage vol by path: %s", error) continue disk_dize = volume_info[1] / 1024.0 / 1024.0 / 1024.0 disk_free = (volume_info[1] - volume_info[2]) / 1024.0 / 1024.0 / 1024.0 disk_free = float("%.3f" % disk_free) all_disk_info[disk_num] = {'disk_size': disk_dize, 'device_name': device_name, 'disk_free': disk_free} return all_disk_info
def set_vm_vcpu_live(self, inst_name, vcpu_num): """ set the vcpu numbers for a running VM :param inst_name: :param vcpu_num: should be str of a int number :return: True or False """ if self._hypervisor_handler is None: self._hypervisor_handler = self.get_handler() try: vm_ref = self._hypervisor_handler.xenapi.VM.get_by_name_label( inst_name)[0] cpu_max = self._hypervisor_handler.xenapi.VM.get_VCPUs_max(vm_ref) if int(vcpu_num) > int(cpu_max): log.warn( "VCPU number exceed the max cpu number:%s, will set it to the max instead.", cpu_max) vcpu_num = cpu_max self._hypervisor_handler.xenapi.VM.set_VCPUs_number_live( vm_ref, str(vcpu_num)) return True except Exception, error: log.exception("Raise exceptions: [%s].", error) return False
def get_handler(self): ''' return the handler of the virt_driver ''' if self._hypervisor_handler is not None: return self._hypervisor_handler if self.hostname is None: self._hypervisor_handler = XenAPI.xapi_local() #no __nonzero__, can not use if/not for bool test else: log.debug("connecting to %s with user:%s,passwd:%s", "http://" + str(self.hostname), self.user, self.passwd) self._hypervisor_handler = XenAPI.Session("http://" + str(self.hostname)) old = signal.signal(signal.SIGALRM, self.timeout_handler) signal.alarm(4) # connetctions timeout set to 5 secs try: self._hypervisor_handler.xenapi.login_with_password(self.user, self.passwd, API_VERSION_1_1, 'XenVirtDriver') except Exception as error: log.warn("Exception raised: %s when get handler.", error) log.info("Retry connecting to :%s", "https://" + str(self.hostname)) self._hypervisor_handler = XenAPI.Session("https://" + str(self.hostname)) signal.alarm(4) try: self._hypervisor_handler.xenapi.login_with_password(self.user, self.passwd, API_VERSION_1_1, 'XenVirtDriver') except Exception as errors: log.exception("Exception errors:%s when get handler", errors) return None finally: signal.alarm(0) signal.signal(signal.SIGALRM, old) log.debug("Get handler ID in vnet driver: %s", id(self._hypervisor_handler)) return self._hypervisor_handler
def set_vm_vcpu_max(self, inst_name, vcpu_num): """ set the vcpu numbers for a halted VM :param inst_name: :param vcpu_num: :return: True or False """ if self._hypervisor_handler is None: self._hypervisor_handler = self.get_handler() vcpu_num = int(vcpu_num) try: vm_ref = self._hypervisor_handler.xenapi.VM.get_by_name_label( inst_name)[0] # 0 < VCPUs_at_startup <= VCPUs_max cpu_at_start = self._hypervisor_handler.xenapi.VM.get_VCPUs_at_startup( vm_ref) if vcpu_num < int(cpu_at_start): log.warn( "The max cpu number is smaller than the live number [%s] and will change live cpu to it.", cpu_at_start) self._hypervisor_handler.xenapi.VM.set_VCPUs_at_startup( vm_ref, str(vcpu_num)) self._hypervisor_handler.xenapi.VM.set_VCPUs_max( vm_ref, str(vcpu_num)) return True except Exception, error: log.exception("Raise exceptions: [%s].", error) return False
def find_default_available_ip(server_ip, template_name): """ use the template's network to find the default ip for new vm :param server_ip: host ip :param template_name: template name for new vm :return: """ target_network, target_netmask = None, None vnetDeriver = QemuVnetDriver(server_ip, Libvirtd_User, Libvirtd_Pass) if vnetDeriver: br_name = vnetDeriver.get_vif_bridge_name(template_name, 0) device_info = vnetDeriver.get_bridge_info(bridge_name=br_name) target_network = device_info.get("IP", None) target_netmask = device_info.get("netmask", None) if not target_network: log.error("Can not find the target network for new vm.") return None if not target_netmask: target_netmask = "24" ip_address_netmask = ip_network(unicode(target_network + "/" + target_netmask), strict=False) avaiable_hosts = [str(ip) for ip in ip_address_netmask.hosts()] # just to fetch ip info when find default ip used_ips = fetch_used_ips_from_db() if platform.system() == "Linux": cmd = "nmap -v -sn -n %s -oG - | awk '/Status: Down/{print $2}'" % ip_address_netmask p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) pout, perr = p.communicate() if perr: log.error("subprocess execute return eror: %s", perr) return None for ip in pout.splitlines(): if (ip not in used_ips) and (ip in avaiable_hosts): return ip elif platform.system() == "Darwin": for ip in avaiable_hosts: if ip in used_ips: continue if is_IP_pingable(ip): continue return ip else: for ip in avaiable_hosts: if ip not in used_ips: return ip log.warn("Can not find a available ip for new vm") return None
def get_host_bond_info(self): """ :return: return the bond information """ try: raise NotImplementedError() except NotImplementedError: log.warn("Get host bond info is not supported in KVM by now.") return {}
def is_IP_available(self, vif_ip=None, vif_netmask=None, device=None, network=None, bridge=None): """ check if a IP and Netmask usable """ # No ip , don't need to check if not vif_ip: return True dest_metmask = "" dest_gateway = None if device is not None: try: device_info = self.vnet_driver.get_device_infor( device_name=device) dest_metmask = device_info["netmask"] dest_gateway = device_info['gateway'] except KeyError as error: log.exception(str(error)) elif network is not None or bridge is not None: # TODO: need to add API to get network infor accroding to network or bridge pass if vif_netmask: if dest_metmask and dest_metmask != vif_netmask: log.error( "Netmask [%s] is not corresponding with the target network.", vif_netmask) return False else: # get the netmask on device as the default one vif_netmask = dest_metmask log.debug("VIF IP is: %s, netmask is: %s", vif_ip, vif_netmask) if not vif_netmask: # No default netmask and no given log.warn("No netmask given, the default one is '255.255.255.0'.") else: vif_gateway = dest_gateway if dest_gateway else None if not IpCheck.is_valid_ipv4_parameter( vif_ip, vif_netmask, gateway=vif_gateway): return False # First check it from database if self.check_ip_used(vif_ip): log.error("Ip address [%s] already in used.(Check from database).", vif_ip) return False # This ping test take a second, put it at last. if is_IP_pingable(vif_ip): log.error("Ipaddress [%s] is already be used(Ping test).", vif_ip) return False return True
def set_mac_address(self, inst_name, eth_index, new_mac): """ <mac address='52:54:00:68:43:c2'/> """ domain = self._get_domain_handler(domain_name=inst_name) if not domain: log.error("Domain %s doesn't exist, set mac failed.", inst_name) return False if domain.isActive(): log.warn("New MAC will take effect after domain reboot.") vif_list = self._get_dom_interfaces_elements_list(inst_name) try: interface = vif_list[eth_index] mac_element = interface.find("mac") old_mac = mac_element.get("address") except IndexError: log.exception("No interfaces at index [%s] find in domain [%s]", eth_index, inst_name) return False tree = xmlEtree.fromstring(domain.XMLDesc()) mac_list = tree.findall('devices/interface/mac') try: for mac_element in mac_list: if mac_element.get("address") == old_mac: log.debug( "Change old mac [%s] to new [%s] on interface index %s", old_mac, new_mac, eth_index) mac_element.set("address", new_mac) except ValueError as error: log.exception("Exception when set mac: %s on domain: [%s]", error, inst_name) return False domain_xml = xmlEtree.tostring(tree) # after change the xml, redeine it hv_handler = self.get_handler() if not hv_handler: log.error("Can not connect to host: %s when create domain %s.", self.hostname, inst_name) return False try: # if failed it will raise libvirtError, return value is always a Domain object _ = hv_handler.defineXML(domain_xml) except libvirtError: log.error( "Create domain %s failed when define by xml after set MAC.", inst_name) return False return True
def wrapper(*args, **kwargs): old = signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(time_wait) try: return func(*args, **kwargs) except TimeoutError as error: log.warn(error) return default_ret finally: signal.alarm(0) signal.signal(signal.SIGALRM, old)
def get_host_sw_ver(self, short_name=True): """ Return the HV SW version """ hv_handler = self.get_handler() try: hv_ver = hv_handler.getVersion() lib_ver = hv_handler.getLibVersion() except libvirtError, e: log.debug(str(e)) log.warn("Could not get HV version") return None
def get_host_manage_interface_infor(self): """ The manage interface, or the default interface configured with a managed IP :return: """ try: raise NotImplementedError() except NotImplementedError: log.warn( "get host manage interface is not supported in KVM by now.") return {}
def get_host_plat_info(self): """ Return HV platform info This needs root permission to do """ ret_plat_dict = {} hv_root_hdl = self._get_root_handler() try: sys_info_xml = hv_root_hdl.getSysinfo(0) except libvirtError, e: log.debug(str(e)) log.warn("Could not get platform/system info") return ret_plat_dict
def get_host_os(self, short_name=True): """ :return: the host OS information. As no API to get host OS, return KVM/QEMU instead """ log.warn('QEMU/KVM does not support to get host os, return kvm/qemu instead.') if self._hypervisor_handler is None: self._hypervisor_handler = self.get_handler() xmlfile = self._hypervisor_handler.getCapabilities() if self.__is_kvm_available(xmlfile): return "KVM" else: return "QEMU"
def get_host_cpu_info(self): """ Return HV CPU info """ ret_cpu_dict = {} hv_handler = self.get_handler() try: # https://libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/ch03s04s03.html hv_info = hv_handler.getInfo() except libvirtError, e: log.debug(str(e)) log.warn("Could not get CPU info") return ret_cpu_dict
def get_target_path_via_pool(self, storage_pool_name): """ return the pool's path as base path :param storage_pool_name: name of pool :return: """ try: pool = self._hypervisor_handler.storagePoolLookupByName(storage_pool_name) pool_xml_tree = xmlEtree.fromstring(pool.XMLDesc()) pool_path = pool_xml_tree.find("target/path") return pool_path.text except libvirtError as error: log.warn("Exception when get_target_path_via_pool: error %s", error) return ""
def get_host_mem_info(self): """ Return HV memory info """ ret_mem_dict = {} hv_handler = self.get_handler() try: # https://libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/ch03s04s16.html mem_info = hv_handler.getMemoryStats( libvirt.VIR_NODE_MEMORY_STATS_ALL_CELLS) except libvirtError, e: log.debug(str(e)) log.warn("Could not get memory info") return ret_mem_dict
def get_all_vif_info(self, inst_name): """ :return: return all the VIFs's information: mac and IP """ vif_list = self._get_dom_interfaces_elements_list(inst_name) vifs_info = {} log.warn( "The IP was mapped from MAC, make sure your domain get IP mapped from MAC." ) for index, element in enumerate(vif_list): mac, ip_str = self.__get_mac_and_ip(element) vifs_info[index] = {'mac': mac, 'ip': ip_str} return vifs_info
def update_vm_database_info(inst_name, **kwargs): """ This function is used to sync VM information when config changed, include:cpu_cores, memory_size, disk_num :param inst_name: :param kwargs: :return: """ log.info("Start to update [%s] information to databse.", inst_name) host_name = kwargs['host'] user = kwargs['user'] if kwargs['user'] else "root" passwd = str(kwargs['passwd']).replace('\\', '') if kwargs['passwd'] else "" virt_driver = VirtFactory.get_virt_driver(host_name, user, passwd) db_driver = DbFactory.get_db_driver("VirtHost") vm_record = virt_driver.get_vm_record(inst_name=inst_name) if not vm_record: return False sn = vm_record['uuid'] if not db_driver.query(sn=sn): log.info("No record found with given VM:[%s], don't update database", inst_name) return True cpu_cores = vm_record['VCPUs_live'] memory_size = vm_record['memory_target'] disk_info = virt_driver.get_all_disk(inst_name=inst_name) disk_num = len(disk_info) # TODO: sync disk size # for disk in disk_info: # disk_size += virt_driver.get_disk_size(inst_name, disk) sync_data = {"cpu_cores": cpu_cores, "memory_size": memory_size, "disk_num": disk_num } try: ret = db_driver.update(sn=sn, data=sync_data) except Exception as error: log.debug("Exception raise when update vm database: %s", error) ret = False if not ret: log.warn("Update database information with ret: [%s], data: %s", ret, sync_data) return ret
def get_host_mem_info(self): """ Return HV memory info: free memory include free and cache """ ret_mem_dict = {} hv_handler = self.get_handler() try: # https://libvirt.org/docs/libvirt-appdev-guide-python/en-US/html/ch03s04s16.html mem_info = hv_handler.getMemoryStats(libvirt.VIR_NODE_MEMORY_STATS_ALL_CELLS) except libvirtError as e: log.debug(str(e)) log.warn("Could not get memory info") return ret_mem_dict ret_mem_dict['size_total'] = float("%.3f" % (mem_info['total'] / 1024.0 / 1024.0)) ret_mem_dict['size_free'] = float("%.3F" % ((mem_info['free'] + mem_info['cached']) / 1024.0 / 1024.0)) ret_mem_dict['size_used'] = ret_mem_dict['size_total'] - ret_mem_dict['size_free'] return ret_mem_dict
def delete(self, id=None, sn=None, hostname=None): """ Delete a record from database :param id: pk :param sn: :param hostname: :return: """ if id is None and sn is None and hostname is None: log.error( "Delete record from DB need an ID, a hostname or a SN to identify which record will be deleted." ) return False if id is None: query_data = self.query(sn=sn, hostname=hostname) if not query_data: log.info( "No record found to delete with sn:%s and hostname:%s.", sn, hostname) return True id_list = [record['id'] for record in query_data] else: id_list = [id] for id in id_list: url = self.url + str(id) db_name = self.db_name(url) log.debug("Delete url: %s", url) self.resp = self.session.delete(url) if self.resp.status_code == requests.codes.no_content: log.info("Delete data from database [%s] successfully.", db_name) elif self.resp.status_code == requests.codes.not_found: log.warn("Not found the record when delete from [%s]: 404", db_name) elif self.is_respond_error: log.warn( "Delete data from database: [%s] failed, return code: %s.", db_name, self.resp.status_code) return False return True
def update_storage_to_database(self): """ sync server's storage info to cmdb :return: true or false """ server_name = self.server_name log.info("Start to update [%s] storage information to database.", server_name) sn = self.virt_driver.get_host_plat_info().get('serial_number') if not self.db_driver.query(sn=sn, hostname=server_name): log.info("No record found with server name [%s], don't update.", server_name) return True storage_info = self.get_host_all_storage_info() disk_size, disk_free, disk_num = 0, 0, 0 for sr, disk in storage_info.iteritems(): if int(disk[0]) > 10: disk_num += 1 disk_size += disk[0] disk_free += disk[1] sync_data = {"disk_size": int(disk_size), "disk_free": int(disk_free)} comment = "Update server storage by virtualization API with data: %s" % sync_data sync_data['comment'] = comment try: ret = self.db_driver.update(sn=sn, hostname=server_name, data=sync_data) except Exception as error: log.exception( "Exception raise when update server storage to cmdb: %s", error) ret = False if not ret: log.warn( "Update server storage information return ret: [%s], data: %s", ret, sync_data['comment']) return ret
def get_vif_ip(self, inst_name, vif_index): """ :param inst_name: :param vif_index: :return: """ vif_list = self._get_dom_interfaces_elements_list(inst_name=inst_name) for index, element in enumerate(vif_list): if index == int(vif_index): log.warn( "The ip was mapped from MAC, make sure your domain get IP mapped from MAC." ) _, ip_str = self.__get_mac_and_ip(element) return ip_str else: log.warn("No virtual interface with index [%s] in domain: [%s].", vif_index, inst_name) return None
def get_target_path_via_file(self, source_file): """ return source file's path, if source file in a nfs pool, return the default pool's base path :param source_file: :return: new target path and new pool name """ from lib.Utils.constans import NETFS_POOL_TYPE, DISK_POOL try: vol = self._hypervisor_handler.storageVolLookupByPath(source_file) pool = vol.storagePoolLookupByVolume() pool_xml_tree = xmlEtree.fromstring(pool.XMLDesc()) if pool_xml_tree.attrib.get("type") == NETFS_POOL_TYPE: return self.get_target_path_via_pool(DISK_POOL), DISK_POOL else: pool_path = pool_xml_tree.find("target/path") return pool_path.text, None except libvirtError as error: log.warn("Exception raise when get_target_path_via_file, error: %s", error) return "", None
def query(self, id=None, sn=None, hostname=None): """ query from database :param id: PK id :param sn: UUID of VM or host :param hostname: The name of VM or host :return: the record with Dict """ url = self.url db_name = self.db_name(url) data = {'pagesize': 'max'} url += "?" select_item = ["pagesize=max"] if id: data['id'] = id select_item.append("id=%s" % id) if sn: data['sn'] = sn select_item.append("sn=%s" % sn) if hostname: data['hostname'] = hostname select_item.append("hostname=%s" % hostname) url += "&".join(select_item) log.debug("Query URL: %s", url) self.resp = self.session.get(url) if self.resp.status_code == requests.codes.ok: log.debug( "Query from database: [%s] with record [%s] successfully.", db_name, data) else: log.error( "Query from database: [%s] with record [%s] failed, please check the log at '/var/log/virt.log'.", db_name, data) log.debug(self.resp.content) if not self.respond_data_count: log.warn("No records found with query data: %s.", data) return [] else: return self.respond_data_list
def config_vcpus(self, inst_name, vcpu_nums=None, vcpu_max=None): """ :param inst_name: VM name :param vcpu_nums: the current vcpu number :param vcpu_max: the max vcpu number :return: """ if not vcpu_max and not vcpu_nums: return True log.info("Start to configure the VCPU in VM [%s].", inst_name) if vcpu_max: if self.virt_driver.is_instance_halted(inst_name=inst_name): log.info( "Start to configure max VCPU number in VM [%s], max vcpu num=[%s].", inst_name, str(vcpu_max)) ret = self.virt_driver.set_vm_vcpu_max(inst_name=inst_name, vcpu_num=vcpu_max) if not ret: log.warn( "Failed to set vm max VCPU number to [%s], return ret=%s", str(vcpu_max), str(ret)) else: log.error("Only support set max cpu number on a halted VM.") return False log.info( "Start to configure the live VCPU number in VM [%s], lively vcpu num=[%s].", inst_name, str(vcpu_nums)) if vcpu_nums: ret = self.virt_driver.set_vm_vcpu_live(inst_name=inst_name, vcpu_num=vcpu_nums) if not ret: log.warn( "Failed to set vm lively VCPU number to [%s], return ret=%s", str(vcpu_nums), str(ret)) # set vcpu max will change the start up vcpu when max < live cpu number if ret: # Don't need to check db sync ret, because there is crontab to sync it self.update_database_info(inst_name=inst_name) return ret
def delete_vm(self, vm_name): """ :param vm_name: :return: """ log.info("Start to delete VM [%s].", vm_name) ret = self.virt_driver.delete_instance(vm_name) if not ret: return False db_ret = self.delete_database_info(inst_name=vm_name) if not db_ret: log.warn( "Failed to clear the database information of VM [%s], please do it manually.", vm_name) # No matter delete vm from DB failed or not, return True return True