Skip to content
handlers.py 6.18 KiB
Newer Older
Antoine Millet's avatar
Antoine Millet committed
#!/usr/bin/env python
#coding=utf8

import inspect
import logging
from sjrpc.utils import RpcHandler, pure
from tql import TqlQuery
Antoine Millet's avatar
Antoine Millet committed

def listed(func):
    func.__listed__ = True
    return func

class CCHandler(RpcHandler):
    '''
    Base class for handlers of CloudControl server.

    :cvar role_name: the name of the role binded to the handler
    :type role_name: str or None is not applicable
    '''
    
    role_name = None

    def __init__(self, server):
        self._server = server

    def __getitem__(self, name):
        logging.debug('Called method %s.%s' % (self.__class__.__name__, name))
        return super(CCHandler, self).__getitem__(name)

    @pure
    def list_commands(self):
        cmd_list = []

        for attr in dir(self):
            attr = getattr(self, attr, None)
            if getattr(attr, '__listed__', False):
                cmd = {}
                cmd['name'] = attr.__name__
                doc = inspect.getdoc(attr)
                if doc:
                    cmd['description'] = inspect.cleandoc(doc)
Antoine Millet's avatar
Antoine Millet committed
                cmd_list.append(cmd)

        return cmd_list

class OnlineCCHandler(CCHandler):

    def on_disconnect(self, connection):
        self._server.unregister(connection)

class HypervisorHandler(OnlineCCHandler):
Antoine Millet's avatar
Antoine Millet committed
    '''
    Handler binded to 'node' role.
    '''
    role_name = 'hypervisor'


class ClientHandler(OnlineCCHandler):
Antoine Millet's avatar
Antoine Millet committed
    '''
    Handler binded to 'cli' role.
    '''
    
    role_name = 'client'

Antoine Millet's avatar
Antoine Millet committed

        objects = []
Antoine Millet's avatar
Antoine Millet committed
        query = TqlQuery(query)

        for condition in query.iterconditions():
            tag_name = condition.name
            
            if tag_name in ('a', 'hv', 'h', 'id'):
                # Append all accounts:
                for login in self._server.conf.list_accounts():
                    tags = self._server.resolve_tags(login, query.tags)
                    objects.append(tags)

            if tag_name in ('vm', 'h', 'id'):
                hvs = [o for o in objects if o['role'] == 'hypervisor'
                               and o['status'] == 'online']
                if not hvs:
                    for hy in self._server.iter_connected_role('hypervisor'):
                        tags = self._server.resolve_tags(hy.login, query.tags)
                        hvs.append(tags)

                # Query the selected hypervisors:
                logging.debug('Querying %d hypervisors: '
                              '%s' % (len(hvs), hvs))
                async_calls = {}
                tags = tuple(query.tags)
                for hy_tags in hvs:
                    hy = self._server.get_connection(hy_tags['a'])
                    cid = hy.connection.async_call('list_vm', tags=tags)
                    async_calls[cid] = hy_tags

                logging.debug('Waiting for the response of hypervisors...')
                calls = frozenset(async_calls)
                responses = self._server.manager.wait(calls, timeout=5)
                logging.debug('Responses received from hypervisors.')

                for resp in responses:
                    if resp['error'] is None:
                        hy = async_calls[resp['id']]
                        for vm_tags in resp['return']:
                            vm_tags['role'] = 'vm'
                            vm_tags['id'] = '%s.%s' % (hy['a'], vm_tags['vm'])
                            vm_tags.update(hy['__static_user'])
                            objects.append(vm_tags)

            # Filter the tag
            objects = [o for o in objects if condition.check(o.get(tag_name))]

        # Before to return, filter with the asked tags for each objects:
        tags = query.tags
        tags.add('id')
        tags.add('role')
        final_objects = {}
        for obj in objects:
            for key in obj.keys():
                if key not in tags:
                    del obj[key]
            final_objects[obj['id']] = obj

        return final_objects.values()
    @pure
    @listed
    def list(self, query):
        '''
        List all objects registered on this instance.
        '''

        logging.debug('Executed list function with query %s' % query)
        return self._list(query)
    def _vm_action(self, query, method, *args, **kwargs):
        vms = self._list(query)
        hypervisors = list(self._server.iter_connected_role('hypervisor'))
            vm_to_start = []
            for vm in vms:
                if vm['role'] != 'vm':
                    pass
                elif vm['id'].split('.')[0] == hv.login:
                    vm_to_start.append(vm['vm'])
            if vm_to_start:
                hv.connection.call(method, vm_to_start, *args, **kwargs)

    @pure
    @listed
    def start(self, query):
        self._vm_action(query, 'start_vm')

    @pure
    @listed
    def stop(self, query, force=False):
        self._vm_action(query, 'stop_vm', force)

    @pure
    @listed
    def suspend(self, query):
        self._vm_action(query, 'suspend_vm')

    @pure
    @listed
    def resume(self, query):
        self._vm_action(query, 'resume_vm')
Antoine Millet's avatar
Antoine Millet committed

class WelcomeHandler(CCHandler):
    '''
    Default handler used on client connections of the server.

    :cvar ROLES: role name/handler mapping
    '''
    
    ROLES = {
        'client': ClientHandler,
        'hypervisor': HypervisorHandler,
    }

    @listed
    def authentify(self, connection, login, password):
        '''
        Authenticate the client.
        '''
        try:
            role = self._server.conf.authentify(login, password)
        except CCConf.UnknownAccount:
            raise AuthenticationError('Authentication failure (Unknown login)')
Antoine Millet's avatar
Antoine Millet committed

        if role is None:
            logging.info('New authentication from %s: failure' % login)
            raise AuthenticationError('Authentication failure')
Antoine Millet's avatar
Antoine Millet committed
        else:
            # If authentication is a success, ask tags to the server:
            self._server.register(login, role, connection)
            connection.set_handler(WelcomeHandler.ROLES.get(role)(self._server))
            logging.info('New authentication from %s: success' % login)
            
            return role