# Gerer le cas ou le mdp n'est pa valable #TODO: catch livbirt disconnection errors and handle it from __init__ import __version__ from interface import Host from kvm import * from sjrpc.utils import RpcHandler from sjrpc.utils import pure from sjrpc.core import RpcError from exceptions import VMError import logging ############ ## Constants ############ # Logging errors TAG_METHOD_ERROR = 'No method found in hypervisor to handle tag %s' TAG_NOT_FOUND_ERROR = 'Tag %s requested but unknown in node handler' # Tags to method mappings # Hypervisor HV_TAG_WRAP_MAP = { 'h' : 'get_name', 'hv' : 'get_name', 'hvtype' : 'get_hv_type', 'libvirtver': 'get_hv_version', 'arch' : 'get_arch_type', 'uname' : 'get_uname', 'uptime' : 'get_uptime', 'load' : 'get_loadavg', 'cpu' : 'get_cpu', 'cpufreq' : 'get_cpu_frequency', 'cpuuse' : 'get_cpu_percent', 'status' : 'get_status', 'mem' : 'get_mem', 'memused' : 'get_mem_used', 'memfree' : 'get_mem_free', 'nvm' : 'get_vm_count', } HV_TAG_HELPER_LIST = [ 'disk', 'sto', ] # Vm VM_TAG_WRAP_MAP = { 'h' : 'get_name', 'vm' : 'get_name', 'hv' : 'get_hv_name', 'cpu' : 'get_cpu', 'status' : 'get_status', 'mem' : 'get_mem', 'memused' : 'get_mem_used', 'memfree' : 'get_mem_free', } VM_TAG_HELPER_LIST = [ 'disk', ] class NodeHandler(RpcHandler): ''' This is the main node handler that exports the hypervisor capabilities to any connected server. In other terms this is the node interface to the server. ''' def __init__(self, connection, hypervisor): super(RpcHandler, self).__init__() self._connection = connection #do not detect hypervisor if connection is disable in configuration if not hypervisor: logging.debug('Hypervisor detection disabled, running as regular' ' node') self.hv_handle = Host() else: #we set our hypervisor handle #FIXME: must be aware of hypervisor type logging.debug('Initializing a connection on the hypervisor') self.hv_handle = KvmHypervisor() logging.debug('Initialized connection to a KVM hypervisor') def _call_hv_method(self, method): ''' Calls the given method in the :class:`Hypervisor` instance Returns the result of the method called :param method: the method to be called :type method: :class:`str` ''' logging.debug('called _call_hv_method(%s)' % method) return getattr(self.hv_handle, HV_TAG_WRAP_MAP[method])() def _call_hv_helper(self, method): logging.debug('called _call_hv_helper(%s)' % method) return getattr(self, "_helper_hv_" + method)() def _call_vm_method(self, vm_name, tag): ''' Calls the given method in vm instance Returns the result of the method called :param vm: The vm object in which call the method :vm type: :class:`VM` :param method: the method to be called :type method: :class:`str` ''' logging.debug('called _call_vm_method(%s,%s)' % (vm_name, tag)) vm_obj = self.hv_handle.get_vm(vm_name) return getattr(vm_obj, VM_TAG_WRAP_MAP[tag])() def _call_vm_helper(self, vm_name, helper): logging.debug('called _call_vm_helper(%s,%s)' % (vm_name, helper)) vm_obj = self.hv_handle.get_vm(vm_name) return getattr(self, "_helper_vm_" + helper)(vm_obj) def _helper_hv_sto(self): logging.debug('called _helper_hv_sto()') result = {} hv = self.hv_handle # fetch pool list pools = hv.get_storage_pools() result['sto'] = " ".join(pools) # get pool info for pool in pools: # storage type result['sto%s_type' % pool] = hv.get_storage_type(pool) # path to the pool base result['sto%s_path' % pool] = hv.get_storage_path(pool) # pool size result['sto%s_size' % pool] = str(hv.get_storage_capacity(pool)) # pool used space result['sto%s_used' % pool] = str(hv.get_storage_used(pool)) # pool free space result['sto%s_free' % pool] = str(hv.get_storage_free(pool)) # pool volume list result['sto%s_vol' % pool] = hv.get_storage_volumes(pool) return result def _helper_hv_disk(self): logging.debug('called _helper_hv_disk()') result = {} disks = self.hv_handle.get_disks() result['disk'] = " ".join(disks.keys()) for disk, size in disks.iteritems(): result['disk%s_size' % disk] = str(size) return result def _helper_vm_disk(self, vm): logging.debug('called _helper_vm_disk(%s)' % vm.get_name()) result = {} # fetch disk list disks = vm.get_disks() logging.debug('_helper_vm_disk: disk list "%s"' % disks) if len(disks): result['disk'] = " ".join(str(id) for id in range(0, len(disks))) # add disk info for n, disk in enumerate(disks): # disk path to real storage result['disk%i_path' % n] = disk['path'] # disk size result['disk%i_size' % n] = str(disk['size']) # disk pool result['disk%i_pool' % n] = disk['pool'] # disk volume result['disk%i_vol' % n] = disk['vol'] logging.debug('_helper_vm_disk returns "%s"' % result) return result @pure def get_tags(self, tags=None): ''' Return tags bound to hypervisor specified in tags parameter otherwise returns all :param tags: list of tags or None :type tags: :class:`list` ''' result = {} if tags is not None: tags = set(tags) tags |= set(('h', 'hv')) hv_tags = HV_TAG_WRAP_MAP.keys() if tags is None else tags # static tags result['version'] = str(__version__) # direct wrapping for tag in hv_tags: try: result[tag] = str(self._call_hv_method(tag)) except AttributeError: logging.warning(TAG_METHOD_ERROR % tag) except KeyError: logging.warning(TAG_NOT_FOUND_ERROR % tag) except NotImplementedError: logging.debug('get_tags: no method in hypervisor to handle "%s"' % tag) # tag aggregations from wrapping helpers for helper in HV_TAG_HELPER_LIST: htags = self._call_hv_helper(helper) for tag, val in htags.iteritems(): if tags is None or tag in tags: result.update({tag:val}) return result @pure def list_vm(self, vm_names=None, tags=None): ''' Return a list of vm tags If vm_names is None or [] returns all vms If tags is None or [] returns only vm name tag :param vm_names: vm names :type vm_names: :class:`list` of strings :param tags: tags to be returned :type tags: :class:`list` of strings ''' vms_info = [] vm_info = {} tags = [] if tags is None else tags logging.debug('list_vm: processing tags %s' % tags) # ensure that vm name is always returned bu default if not tags.__contains__('vm'): tags.insert(0, 'vm') logging.debug('list_vm: fetching tag list %s' % tags) for vm in self.hv_handle.get_vms(): for tag in tags: try: vm_info[tag] = self._call_vm_method(vm, tag) except AttributeError: logging.warning(TAG_METHOD_ERROR % tag) except KeyError: logging.warning(TAG_NOT_FOUND_ERROR % tag) except NotImplementedError: logging.debug('list_vm: method in vm to handle %s' % tag) vms_info.append(vm_info) vm_info = {} if vm_names: logging.debug('list_vm: filtering tag results for vms %s' % vm_names) filterd_vms = [vm for vm in vms_info if vm['vm'] in vm_names] logging.debug('list_vm: returning -> %s' % filterd_vms) return filterd_vms else: logging.debug('list_vm: returning -> %s' % vms_info) return vms_info @pure def stop_vm(self, vm_names=None, force=False): ''' Stop the specified list of vm names This method do a soft shutdown on the Virtual Machine If vm_names is None all vms in hypervisor will be stopped :param vm_names: the list of vm names to stop :vm_names type: :class:`list` of strings :param force: soft shutown or force poweroff :type force: either True or False ''' if vm_names is None: #fetch all vm names in hypervisor vm_names = gen_vm_names(self.hv_handle) logging.debug('stop_vm: stopping vms %s' % vm_names) for vm in vm_names: try: self.hv_handle.stop_vm(vm, force) except VMError as err: logging.warning('Error while stopping %s: %s' % (vm, err)) else: logging.info('stop_vm: vm %s stopping' % vm) @pure def start_vm(self, vm_names=None): ''' Starts the specified list of vms If vm_names is None all vms in the hypervisor will be started :param vm_names: the list of vms to start :type vm_names: :class:`list` of strings ''' if vm_names is None: vm_names = gen_vm_names(self.hv_handle) logging.debug('start_vm: starting vms %s' % vm_names) for vm in vm_names: try: self.hv_handle.start_vm(vm) except VMError as err: logging.warning('Error while starting %s: %s' % (vm ,err)) else: logging.info('start_vm: vm %s starting' % vm) @pure def suspend_vm(self, vm_names=None): ''' Suspends the specifed list of vms If vm_names is None all vms in hypervisor will be suspended :param vm_names: the list of vms to suspend :type vm_names: :class:`list` of strings ''' if vm_names is None: vm_names = gen_vm_names(self.hv_handle) logging.debug('suspend_vm: suspending vms %s' % vm_names) for vm in vm_names: try: self.hv_handle.suspend_vm(vm) except VMError as err: logging.info('Error while suspending %s: %s' % (vm, err)) else: logging.info('suspend_vm: vm %s suspended' % vm) @pure def resume_vm(self, vm_names=None): ''' Resumes the specified list of vms If vm_names is None all vms in hypervisor will be resumed :param vm_names: the list of vms to resume :type vm_names: :class:`str` ''' if vm_names is None: vm_names = gen_vm_names(self.hv_handle) logging.debug('resume_vm: resuming vms %s' % vm_names) for vm in vm_names: try: self.hv_handle.resume_vm(vm) except VMError as err: logging.info('Error while resuming %s: %s' % (vm, err)) else: logging.info('resume_vm: vm %s resumed' % vm) @pure def execute_command(self, command): ''' Executes the given command on the local hypervisor :param command: the command to execute as it would be typed on a shell prompt :type command: :class:`str` ''' result = self.hv_handle.local_execute(command) return result ### Helper Functions def gen_vm_names(hypervisor): ''' generates a list of vm names defined in hypervisor ''' return [vm.get_name() for vm in hypervisor._vm_list]