#TODO: vm informations gathering !!! import libvirt import sys # we use psutils to get host informations we can't get with # libvirt import psutil from interface import * from exceptions import * from time import sleep ### # Defined constants ### ## # Libvirt ## # KVM_LIBVIRT_SESSION = 'qemu:///system' XEN_LIBVIRT_SESSION = 'xen:///' ######### ## States ######### # Virtual Machines VM_STATUS = ( 'No state', 'Running', 'Blocked on resource', 'Paused', 'Shutting down ...', 'Shutdown', 'Crashed' ) VM_START_STATES = { 'running': 1, 'paused' : 0 } SOFT_VM_POWEROFF = True FORCE_VM_POWEROFF = False # Storage Pools POOL_STATE = ( 'Not Running', 'Initializing pool, not available', 'Running normally', 'Running degraded', 'Running, but not accessible' ) ######################### ## Pool informations tags ######################### POOL_NAME = 'pool' POOL_STATUS = 'pstatus' POOL_TOTAL_SIZE = 'psize' POOL_FREE_SPACE = 'pfree_space' POOL_USED_SPACE = 'pused_space' POOL_NUM_VOLUMES = 'num_vols' POOL_BACKEND = 'backend' DEFAULT_VM_START = VM_START_STATES['running'] ## # Pretty size outputs ## MEGABYTE_DIV = 1024 * 1024 GIGABYTE_DIV = 1024 * 1024 * 1024 KILOBYTE_DIV = 1024 class LibvirtVm(VM): ''' Libvirt api access to Virtual Machine actions All libvirt.virDomain calls must stay in this class :param domain: a libvirt domain instance :type domain: :class:`libvirt.virDomain` :param hypervisor: a hypervisor reference object :type hypervisor: :class:`LibvirtHypervisor` ''' _print_header = True def __init__(self, domain, hypervisor): ''' ''' super(LibvirtVm, self).__init__() if isinstance(domain, libvirt.virDomain): self._domain = domain self.vm_info = {} self._find_pid() else: raise TypeError('Need virDomain object given %s' % type(domain)) self.set_hypervisor(hypervisor) def __cmp__(self, other): ''' We overload the compare method so that two virtual machines are identical if they are bound to the same domain object ''' return cmp(self._domain, other._domain) def _find_pid(self): ''' Finds the PID of the current vm ''' result = find_process_id(self._domain.UUIDString()) if result: self._pid = int(result.pop()) else: self._pid = None def get_pid(self): return self._pid def set_hypervisor(self, hypervisor): if isinstance(hypervisor, LibvirtHypervisor): self._hypervisor = hypervisor else: raise TypeError('argument type error') def get_name(self): return self._domain.name() def get_used_mem(self): return self._domain.info()[2] / KILOBYTE_DIV def get_total_mem(self): return self._domain.info()[1] / KILOBYTE_DIV def get_free_mem(self): return (self.get_total_mem() - self.get_used_mem()) / KILOBYTE_DIV def get_vcpu(self): return self._domain.info()[3] def get_cpu_percent(self): self._find_pid() if self._pid: p = psutil.Process(self._pid) sleep(0.7) res = int(p.get_cpu_percent()) res = 100 if res > 100 else res return res else: return 0 def get_status(self): return VM_STATUS[self._domain.info()[0]] def shutdown(self): try: self._domain.shutdown() except libvirt.libvirtError: raise VMError('%s is not running !' % self.get_name()) def force_poweroff(self): try: self._domain.destroy() except libvirt.libvirtError: raise VMError('%s is not running !' % self.get_name()) def start(self): try: self._domain.create() except libvirt.libvirtError: raise VMError('%s is already running !' % self.get_name()) def suspend(self): self._domain.suspend() def resume(self): self._domain.resume() def get_uuid(self): ''' Returns the uuid string of the vm ''' return self._domain.UUIDString() def __str__(self): ''' LibvirtVm object string representation ''' header = '%-10s | %-10s | %-10s | %-15s | %-10s\n\n' %\ ('Name', 'UsedMem', 'VCpu', 'CpuUsage', 'Status') self._fetch_memory_stats() self.get_cpu_stats() stats = '%-10s | %-10s | %-10s | %-15s | %-10s' %\ (self.get_name(), self.used_memory, self.vcpu, self.cpu_use, self.get_status()) return header + stats class LibvirtHypervisor(Hypervisor): ''' Libvirt api access to Hypervisor actions. All libvirt calls to the hypervisor stay in this class :param hv_type: This is the hypervisor type, this parameter is important as it specifies the hypervisor type used when connecting to libvirt. :type hv_type: :class:`str` .. note:: hv_type can be any of kvm, xen, ... , other hypervisors, however only one connection at a time is allowed ''' def __init__(self, hv_type): #FIXME: don't use hard coded hypervisor type in URI try: if hv_type == 'kvm': self._con_handle = libvirt.open(KVM_LIBVIRT_SESSION) else: raise NotImplemented('or unknown hypervisor type') except libvirt.libvirtError as error: raise HypervisorError(error, 'libvirt cannot connect to hypervisor') #build vm objects self._build_vm_list() self.hv_info = {} self.hv_type = hv_type def get_name(self): ''' Returns hypervisor's name ''' return self._con_handle.getHostname() def get_hv_type(self): return self.hv_type def get_hv_version(self): return self._con_handle.getVersion() def _build_vm_list(self): ''' Builds the lists of all defined vms (running and offline) in the current hypervisor ''' self._runing_vm_list = [] self._offline_vm_list = [] self._dom_ids = self._con_handle.listDomainsID() self._defined_doms = self._con_handle.listDefinedDomains() for doms in self._dom_ids: vm = self._con_handle.lookupByID(doms) self._runing_vm_list.append(LibvirtVm(vm, self)) for defined_dom in self._defined_doms: vm = self._con_handle.lookupByName(defined_dom) self._offline_vm_list.append(LibvirtVm(vm, self)) self._vm_list = [] self._vm_list = self._runing_vm_list self._vm_list.extend(self._offline_vm_list) def get_vms(self): ''' Returns a list of :class:`LibvirtVm` objects defining all current defined vms in the hypervisor ''' return self._vm_list def get_arch_type(self): ''' Get archetcture type of hypervisor ''' return self._con_handle.getInfo()[0] def get_nb_cpu(self): ''' Returns number of active cpus in hypervisor ''' return self._con_handle.getInfo()[2] def get_cpu_frequency(self): ''' Returns the frequency in MHZ of cpus in the hypervisor ''' return self._con_handle.getInfo()[3] def get_nb_threads(self): ''' Number of threads per core ''' return self._con_handle.getInfo()[7] def get_free_mem(self): return psutil.avail_phymem() / MEGABYTE_DIV def get_used_mem(self): return psutil.used_phymem() / MEGABYTE_DIV def get_total_mem(self): return ((psutil.avail_phymem() + psutil.used_phymem()) / MEGABYTE_DIV) def get_cpu_percent(self): return '%2.0f' % psutil.cpu_percent() def get_status(self): raise NotImplementedError() #TODO: finish storage class, make tests, class LibvirtHVStorage(HVStorage): ''' Base storage class using libvirt api for storage managment Storage pools here are libvirt.virStoragePool instances ''' def __init__(self, hypervisor): if isinstance(hypervisor, LibvirtHypervisor): self.hv_handle = hypervisor self._fetch_st_pools() else: raise TypeError('Excpected %s given %s' % (LibvirtHypervisor, hypervisor)) def _fetch_st_pools(self): ''' Sets an attribute self._pool_objs containging a list of libvirt pool objects bound to the hypervisor ''' pools = [] pools.extend(self.hv_handle._con_handle.listDefinedStoragePools()) pools.extend(self.hv_handle._con_handle.listStoragePools()) self._pools = [] self._pools.extend(pool_obj(self.hv_handle._con_handle, pool) for pool in pools) def get_storage_pools(self): return [pool.name() for pool in self._pools] def get_pool_info(self, pool): pool_info = {} pool_info[POOL_NAME] = pool.name() pool_info[POOL_STATUS] = self.get_pool_state(pool) pool_info[POOL_TOTAL_SIZE] = self.get_pool_size(pool) / GIGABYTE_DIV return pool_info def get_pool_size(self, pool): ''' Returns total logical size of the pool :param pool: the storage pool :type pool: :class:`libvirt.virStoragePool` ''' return pool.info()[1] def get_pool_state(self, pool): ''' Returns the running state of the pool :param pool: the storage pool name :type pool: libvirt.`virStoragePool` ''' return POOL_STATE[pool.info()[0]] #### Helper functions def map_process(process): ''' map each process object with it's pid and command line options ''' return (process.pid, process.cmdline) def find_process_id(uuid): ''' Find the process id of a kvm process gevin an UUID of a virDomain object Returns a list of one process id ''' return [p.pid for p in psutil.get_process_list() if p.cmdline.__contains__(uuid)] def pool_obj(con_handle, pool_name): ''' Creates a libvirt pool object given the name of the pool :param con_handle: libvirt connection handle :type con_handle: :class:`libvirt.virConnect` ''' return con_handle.storagePoolLookupByName(pool_name)