Skip to content
Snippets Groups Projects
ccnodehandlers.py 12 KiB
Newer Older
Benziane Chakib's avatar
Benziane Chakib committed
# Gerer le cas ou le mdp n'est pa valable
#TODO: catch livbirt disconnection errors and handle it 


from interface import Host
Benziane Chakib's avatar
Benziane Chakib committed
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_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',
]

Benziane Chakib's avatar
Benziane Chakib committed

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):
Benziane Chakib's avatar
Benziane Chakib committed
        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')
Benziane Chakib's avatar
Benziane Chakib committed
    
    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):
Benziane Chakib's avatar
Benziane Chakib committed
        '''
        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)
Benziane Chakib's avatar
Benziane Chakib committed
        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)
Benziane Chakib's avatar
Benziane Chakib committed
    @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:
Benziane Chakib's avatar
Benziane Chakib committed
            try:
                result[tag] = str(self._call_hv_method(tag))
Benziane Chakib's avatar
Benziane Chakib committed
            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})
Benziane Chakib's avatar
Benziane Chakib committed
        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
Benziane Chakib's avatar
Benziane Chakib committed

        :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 
Benziane Chakib's avatar
Benziane Chakib committed
        logging.debug('list_vm: processing tags %s' % tags)
        # ensure that vm name is always returned bu default
Benziane Chakib's avatar
Benziane Chakib committed
        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):
Benziane Chakib's avatar
Benziane Chakib committed
        '''
        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
Benziane Chakib's avatar
Benziane Chakib committed
        '''
        if vm_names is None:
            #fetch all vm names in hypervisor
            vm_names = gen_vm_names(self.hv_handle)
Benziane Chakib's avatar
Benziane Chakib committed
        logging.debug('stop_vm: stopping vms %s' % vm_names)
        for vm in vm_names:
            try:
                self.hv_handle.stop_vm(vm, force)
Benziane Chakib's avatar
Benziane Chakib committed
            except VMError as err:
                logging.warning('Error while stopping %s: %s' % (vm, err))
            else:
                logging.info('stop_vm: vm %s stopping' % vm)
Benziane Chakib's avatar
Benziane Chakib committed

    @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)
Benziane Chakib's avatar
Benziane Chakib committed
        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):
        '''
Thibault VINCENT's avatar
Thibault VINCENT committed
        Executes the given command on the local hypervisor
Thibault VINCENT's avatar
Thibault VINCENT committed
        :param command: the command to execute as it would be typed on a shell
            prompt
        :type command: :class:`str`
        '''
Thibault VINCENT's avatar
Thibault VINCENT committed
        result = self.hv_handle.local_execute(command)


### 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]