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

from __future__ import absolute_import

Antoine Millet's avatar
Antoine Millet committed
import inspect
import logging
Antoine Millet's avatar
Antoine Millet committed
from sjrpc.utils import RpcHandler
from sjrpc.core import RpcError
from ccserver.orderedset import OrderedSet
from ccserver.conf import CCConf
from ccserver.exceptions import (AlreadyRegistered, AuthenticationError,
                                 RightError, ReservedTagError, BadObjectError,
                                 BadRoleError, NotConnectedAccountError,
                                 CloneError)
from ccserver.election import Elector
from ccserver import __version__
Antoine Millet's avatar
Antoine Millet committed


MIGRATION_TYPES = {'cold': 'cold_migrate'}


Antoine Millet's avatar
Antoine Millet committed
def listed(func):
    func.__listed__ = True
    return func


class Reporter(object):
    '''
    Simple class used to report error, warning and success of command execution.
    '''

    def __init__(self):
        self._reports = []
        return {'objects': self._reports,
Antoine Millet's avatar
Antoine Millet committed
                'order': ['id', 'status', 'message', 'output']}
    def success(self, oid, message, output=None):
        self._reports.append({'id': oid, 'status': 'success',
                              'message': message, 'output': output})
    def warn(self, oid, message, output=None):
        self._reports.append({'id': oid, 'status': 'warn',
                              'message': message, 'output': output})
    def error(self, oid, message, output=None):
        self._reports.append({'id': oid, 'status': 'error',
                              'message': message, 'output': output})
Antoine Millet's avatar
Antoine Millet committed
class CCHandler(RpcHandler):
    '''
    Base class for handlers of CloudControl server.
    '''
    
    def __init__(self, server):
        self._server = server

    def __getitem__(self, name):
        if name.startswith('_'):
            # Filter the private members access:
            raise KeyError('Remote name %s is private.' % repr(name))
        else:
            logging.debug('Called %s.%s', self.__class__.__name__, name)
            return super(CCHandler, self).__getitem__(name)
Antoine Millet's avatar
Antoine Millet committed

    def functions(self, conn):
Antoine Millet's avatar
Antoine Millet committed
        '''
        Show the list of functions available to the peer.

        :return: list of dict with keys name and description.
        '''

Antoine Millet's avatar
Antoine Millet committed
        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

    def version(self, conn):
Antoine Millet's avatar
Antoine Millet committed
        '''
        Return the current server version.

        :return: the version
        '''
Antoine Millet's avatar
Antoine Millet committed

class OnlineCCHandler(CCHandler):

    def on_disconnect(self, conn):
        client = self._server.search_client_by_connection(conn)
        logging.info('Client %s disconnected', client.login)
        self._server.unregister(conn)
    def _check(self, conn, method, tql=None):
Antoine Millet's avatar
Antoine Millet committed
        client = self._server.search_client_by_connection(conn)
        allow = self._server.check(client.login, method, tql)
Antoine Millet's avatar
Antoine Millet committed
        if not allow:
            raise RightError('You are not allowed to do this action.')

Antoine Millet's avatar
Antoine Millet committed

class HypervisorHandler(OnlineCCHandler):
Antoine Millet's avatar
Antoine Millet committed
    '''
Antoine Millet's avatar
Antoine Millet committed
    Handler binded to 'hv' role.
Antoine Millet's avatar
Antoine Millet committed
    '''

    @listed
    def register(self, conn, obj_id, role):
        '''
        Register an object managed by the calling node.

        .. note:
           the obj_id argument passed to this handler is the object id of the
           registered object (not the fully qualified id, the server will
           preprend the id by "node_id." itself).

        :param obj_id: the id of the object to register
        :param role: the role of the object to register
        '''

        client = self._server.search_client_by_connection(conn)
        self._server.sub_register(client.login, obj_id, role)

    @listed
    def unregister(self, conn, obj_id):
        '''
        Unregister an object managed by the calling node.

        .. note:
           the obj_id argument passed to this handler is the object id of the
           unregistered object (not the fully qualified id, the server will
           preprend the id by "node_id." itself).

        :param obj_id: the id of the object to unregister
        '''

        client = self._server.search_client_by_connection(conn)
        self._server.sub_unregister(client.login, obj_id)
Antoine Millet's avatar
Antoine Millet committed

Antoine Millet's avatar
Antoine Millet committed

class CliHandler(OnlineCCHandler):
Antoine Millet's avatar
Antoine Millet committed
    '''
    Handler binded to 'cli' role.

    Summary of methods:

    ================ ================================ =============
    Method name      Description                      Right(s)
    ================ ================================ =============
    list             list objects                     list
    start            start a vm                       start
    stop             stop a vm                        stop
    destroy          destroy a vm                     destroy
    pause            suspend a vm                     pause  
    resume           resume a paused vm               resume
    passwd           change password of accounts      passwd
    addaccount       add a new account                addaccount
    copyaccount      copy an account                  addaccount
    addtag           add a tag to accounts            addtag
    deltag           remove a tag from accounts       deltag
    tags             show tags of accounts            tags
    delaccount       delete an account                delaccount
    close            close an account                 close
    declose          declose an account               declose
    kill             kill a connected account         kill
    rights           show rights of accounts          rights
    addright         add right rules to accounts      addright
    delright         remove right rules from accounts delright
    execute          execute remote command on hosts  execute
    shutdown         shutdown a connected client      shutdown
    jobs             show jobs                        jobs
    cancel           cancel a running job             cancel
    jobspurge        remove done jobs from jobs list  jobspurge
    dbstats          show stats from cache            OPEN FOR ALL
    ================ ================================ =============
Antoine Millet's avatar
Antoine Millet committed
    '''

    def list(self, conn, query):
        '''
        List all objects registered on this instance.

        :param query: the query to select objects to show
        self._check(conn, 'list', query)
        logging.debug('Executed list function with query %s', query)
        objects, tags = self._server.list(query, return_toshow=True)
        order = OrderedSet(['id'])
        if tags is not None:
            order |= OrderedSet(tags)
        return {'objects': objects, 'order': list(order)}
    def _vm_action(self, query, method, *args, **kwargs):
        vms = self._server.list(query, show=set(('r', 'h')))
        hypervisors = list(self._server.iter_connected_role('hv'))
        errs = Reporter()
            vm_to_start = []
            for vm in vms:
                if vm['r'] != 'vm':
                    errs.error(vm['id'], 'not a vm')
                elif vm['id'].split('.')[0] == hv.login:
                    vm_to_start.append(vm['h'])
                    errs.success(vm['id'], 'ok')
            if vm_to_start:
                hv.connection.call(method, vm_to_start, *args, **kwargs)
        return errs.get_dict()
    def start(self, conn, query):
        self._check(conn, 'start', query)
        return self._vm_action(query, 'vm_start')
    def stop(self, conn, query):
        self._check(conn, 'stop', query)
        return self._vm_action(query, 'vm_stop', force=False)
    def destroy(self, conn, query):
        self._check(conn, 'destroy', query)
        return self._vm_action(query, 'vm_stop', force=True)
    def pause(self, conn, query):
        self._check(conn, 'pause', query)
        return self._vm_action(query, 'vm_suspend')
    def resume(self, conn, query):
        self._check(conn, 'resume', query)
        return self._vm_action(query, 'vm_resume')
    @listed
    def undefine(self, conn, query, delete_storage=True):
        '''
        Undefine selected virtual machines.

        :param query: the tql query to select objects.
        :param delete_storage: delete storage of vm.
        :return: a dict where key is the id of a selected object, and the value
            a tuple (errcode, message) where errcode is (success|error|warn) and
            message an error message or the output of the command in case of
            success.
        '''

        self._check(conn, 'undefine', query)

        #FIXME: When tag globbing will be implemented, the list of tags to
        #       show will be: r, p, h, disk*
        #       I ask "all tags" pending implementation.
        objects = self._server.list(query, show=set(('*',)))
        errs = Reporter()
        for obj in objects:
            if obj['r'] != 'vm':
                errs.error(obj['id'], 'bad role')
                continue
            try:
                hvcon = self._server.get_connection(obj['p'])
            except KeyError:
                errs.error(obj['id'], 'hypervisor not connected')
            else:
                if delete_storage:
                    for disk in obj.get('disk', '').split():
                        pool = obj.get('disk%s_pool' % disk)
                        name = obj.get('disk%s_vol' % disk)
                        hvcon.proxy.vol_delete(pool, name)
                hvcon.proxy.vm_undefine(obj['h'])
                errs.success(obj['id'], 'vm undefined')
                
        return errs.get_dict()

    def passwd(self, conn, query, password, method='ssha'):
        Define a new password for selected user.

        :param query: the query to select the objects to change
        :param password: the password to set (None to remove password)
        :param method: the hash method (sha, ssha, md5, smd5 or plain)
        :return: a standard report output
        self._check(conn, 'passwd', query)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue

                self._server.conf.set_password(obj['a'], password, method)
                errs.success(obj['id'], 'password updated')
    def addaccount(self, conn, login, role, password=None):
        '''
        Create a new account with specified login.

        :param login: the login of the new account
        :param role: the role of the new account
        :param password: the password of the new account (None = not set)

        self._check(conn, 'addaccount')
        
        if role in WelcomeHandler.ROLES:
            self._server.conf.create_account(login, role, password)
        else:
            raise BadRoleError('%r is not a legal role.' % role)
    @listed
    def copyaccount(self, conn, copy_login, login, password=None):
        '''
        Create a new account with specified login.

        :param copy_login: the login of the account to copy
        :param login: the login of the new account
        :param password: the password of the new account (default None)
        '''

        self._check(conn, 'addaccount')
        self._server.conf.copy_account(copy_login, login, password)

    def addtag(self, conn, query, tag_name, tag_value):
        Add a tag to the accounts which match the specified query.

        :param query: the query to select objects
        :param tag_name: the name of the tag to add
        :param tag_value: the value of the tag

        self._check(conn, 'addtag', query)

        if tag_name in self._server.RESERVED_TAGS:
            raise ReservedTagError('Tag %r is read-only' % tag_name)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                tags = self._server.conf.show(obj['a'])['tags']
                if tag_name in tags:
                    errs.warn(obj['id'], 'tag already exists, changed from %s'
                                         ' to %s' % (tags[tag_name], tag_value))
                else:
                    errs.success(obj['id'], 'tag created')
                self._server.conf.add_tag(obj['a'], tag_name, tag_value)

        return errs.get_dict()
    def deltag(self, conn, query, tag_name):
        Remove a tag of the selected accounts.

        :param query: the query to select objects
        :param tag_name: the name of the tag to remove

        self._check(conn, 'deltag', query)

        if tag_name in self._server.RESERVED_TAGS:
            raise ReservedTagError('Tag %r is read-only' % tag_name)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                tags = self._server.conf.show(obj['a'])['tags']
                if tag_name in tags:
                    errs.success(obj['id'], 'tag deleted')
                else:
                    errs.warn(obj['id'], 'unknown tag')
                self._server.conf.remove_tag(obj['a'], tag_name)

        return errs.get_dict()
    def tags(self, conn, query):
        Return all static tags attached to the selected accounts.

        :param query: the query to select objects

        self._check(conn, 'tags', query)
        objects = self._server.list(query, show=set(('a',)))
Antoine Millet's avatar
Antoine Millet committed
            o = {'id': obj['id']}
            if 'a' in obj:
                otags = self._server.conf.show(obj['a'])['tags']
                otags.update({'id': obj['id']})
                o.update(otags)
            tags.append(o)
        return {'objects': tags, 'order': ['id']}
    def delaccount(self, conn, query):
        Delete the accounts selected by query.

        :param query: the query to select objects

        self._check(conn, 'delaccount', query)
        objects = self._server.list(query, show=set(('a',)))
        client = self._server.search_client_by_connection(conn)
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                try:
                    self._server.conf.remove_account(obj['a'])
                except CCConf.UnknownAccount:
                    errs.error(obj['id'], 'unknown account')
                else:
                    errs.success(obj['id'], 'account deleted')
                
                self._server.jobs.create('kill', author=client.login,
                                         account=obj['a'], gracetime=1)
                
    @listed
    def close(self, conn, query):
        '''
        Close selected account an account without deleting them.

        :param query: the query to select objects

        self._check(conn, 'close', query)
        objects = self._server.list(query, show=set(('a',)))
        client = self._server.search_client_by_connection(conn)
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                tags = self._server.conf.show(obj['a'])['tags']
                if 'close' in tags:
                    errs.warn(obj['id'], 'account already closed')
                    continue

                errs.success(obj['id'], 'closed')
                self._server.conf.add_tag(obj['a'], 'close', 'yes')

                self._server.jobs.create('kill', author=client.login,
                                         account=obj['a'], gracetime=1)

        return errs.get_dict()

    @listed
    def declose(self, conn, query):
        '''
        Re-open selected closed accounts.

        :param query: the query to select objects

        self._check(conn, 'declose', query)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                tags = self._server.conf.show(obj['a'])['tags']
                if 'close' in tags:
                    errs.success(obj['id'], 'account declosed')
                else:
                    errs.warn(obj['id'], 'account not closed')
                self._server.conf.remove_tag(obj['a'], 'close')

        return errs.get_dict()
    @listed
    def kill(self, conn, query):
        '''
        Disconnect all connected accounts selected by query.

        :param query: the query to select objects
        self._check(conn, 'kill', query)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                try:
                    self._server.kill(obj['a'])
                except NotConnectedAccountError:
                    errs.error(obj['id'], 'account is not connected')
                else:
                    errs.success(obj['id'], 'account killed')

        return errs.get_dict()
    @listed
    def rights(self, conn, query):
        '''
        Get the rights of selected accounts.

        :param query: the query to select objects

        self._check(conn, 'rights', query)
        objects = self._server.list(query, show=set(('a',)))
        rules = {}
        for obj in objects:
            if 'a' in obj:
                rules[obj['a']] = self._server.conf.show(obj['a'])['rights']
            else:
                raise BadObjectError('All objects must have the "a" tag.')
Antoine Millet's avatar
Antoine Millet committed

    def addright(self, conn, query, tql, method=None, allow=True, index=None):
        '''
        Add a right rule to the selected accounts.

        :param query: the query to select objects
        :param tql: the TQL of the right rule
        :param method: the method of the right rule
        :param allow: target = allow if True, else False
        :param index: the index of the rule in list (can be negative to index
            from the end, if the index is out of range, the rule is added to
            the end.

        self._check(conn, 'addright', query)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                try:
                    self._server.conf.add_right(obj['a'], tql, method,
                                                allow, index)
                except self._server.conf.UnknownAccount:
                    errs.error(obj['id'], 'unknown account')
                else:
                    errs.success(obj['id'], 'right rule added')
                
        return errs.get_dict()
    def delright(self, conn, query, index):
        '''
        Remove a right rule from the selected objects.

        :param query: the query to select objects
        :param index: the index of the right rule to remove

        self._check(conn, 'delright', query)
        objects = self._server.list(query, show=set(('a',)))
        errs = Reporter()
        with self._server.conf:
            for obj in objects:
                if 'a' not in obj:
                    errs.error(obj['id'], 'not an account')
                    continue
                try:
                    self._server.conf.remove_right(obj['a'], index)
                except self._server.conf.UnknownAccount:
                    errs.error(obj['id'], 'unknown account')
                except self._server.conf.OutOfRangeIndexError:
                    errs.error(obj['id'], 'index out of range')
                else:
                    errs.success(obj['id'], 'right rule deleted')
                
        return errs.get_dict()
    @listed
    def execute(self, conn, query, command):
        '''
        Execute command on matched objects (must be roles hv or host).

        :param query: the tql query to select objects.
        :param command: the command to execute on each object
        :return: a dict where key is the id of a selected object, and the value
            a tuple (errcode, message) where errcode is (success|error|warn) and
            message an error message or the output of the command in case of
            success.
        '''

        self._check(conn, 'execute', query)
        objects = self._server.list(query, show=set(('r',)))
        errs = Reporter()
        for obj in objects:
            if obj['r'] not in ('hv', 'host'):
                errs.error(obj['id'], 'bad role')
                continue
            try:
                objcon = self._server.get_connection(obj['id'])
            except KeyError:
                errs.error(obj['id'], 'node not connected')
            else:
                returned = objcon.connection.call('execute_command', command)
                errs.success(obj['id'], 'command executed', output=returned)
    @listed
    def shutdown(self, conn, query, reboot=True, gracefull=True):
        '''
        Execute a shutdown on selected objects (must be roles hv or host).

        :param query: the tql query to select objects.
        :param reboot: reboot the host instead of just shut it off
        :param gracefull: properly shutdown the host
        :return: a dict where key is the id of a selected object, and the value
            a tuple (errcode, message) where errcode is (success|error|warn) and
            message an error message.
        '''

        self._check(conn, 'execute', query)
        objects = self._server.list(query, show=set(('r',)))
        errs = Reporter()
        for obj in objects:
            if obj['r'] not in ('hv', 'host'):
                errs.error(obj['id'], 'bad role')
                continue
            try:
                objcon = self._server.get_connection(obj['id'])
            except KeyError:
                errs.error(obj['id'], 'node not connected')
            else:
                try:
                    objcon.connection.call('node_shutdown',
                                                      reboot, gracefull)
                except RpcError as err:
                    errs.error(obj['id'], '%s (exc: %s)' % (err.message,
                                                            err.exception))
                else:
                    errs.success(obj['id'], 'ok')
                
        return errs.get_dict()

    def jobs(self, conn, query, show_done=True, show_running=True):
        '''
        Return all jobs.
        :param show_done: show done jobs
        :param show_running: show running jobs

        if query:
            raise NotImplementedError('Tql in jobs is not yet supported.')
        props = ('id', 'author', 'created', 'ended',
                 'duration', 'done', 'title', 'status')
        jobs = []
        for job in self._server.jobs.iterjobs(show_done, show_running):
            
            jobs.append(job.export(props))

    @listed
    def cancel(self, conn, jobid):
        '''
        Cancel a job.

        :param jobid: the id of the job to cancel.
        '''

        self._server.jobs.cancel(jobid)

    @listed
    def jobspurge(self, conn):
        '''
        Purge all done jobs from the job list.
        '''

        self._server.jobs.purge()

    @listed
    def electiontypes(self, conn):
        return Elector.ALGO_BY_TYPES

    @listed
    def election(self, conn, query_vm, query_dest, mtype='cold', algo='fair',
                 **kwargs):
        '''
        Consult the server for the migration of specified vm on an hypervisor
        pool.

        :param query_vm: the tql query to select VMs to migrate
        :param query_dest: the tql query to select destination hypervisors
            candidates
        :param mtype: type of migration
        :param algo: algo used for distribution
        '''
        client = self._server.search_client_by_connection(conn)
        elector = Elector(self._server, query_vm, query_dest, client.login)
        return elector.election(mtype, algo, **kwargs)

    @listed
    def migrate(self, conn, migration_plan):
        '''
        Launch the provided migration plan.

        :param migration_plan: the plan of the migration.
        :return: a standard error report
        '''
        client = self._server.search_client_by_connection(conn)
        errs = Reporter()
        for migration in migration_plan:
            # Check if the migration type is know:
            if migration['type'] in MIGRATION_TYPES:
                mtype = MIGRATION_TYPES[migration['type']]
            else:
                errmsg = '%r unknown migration type' % migration['type']
                errs.error(migration['sid'], errmsg)
            self._server.objects.update(ids=(migration['sid'],))
            vm = self._server.objects.get_by_id(migration['sid'])

            # Construct the migration properties:
            migration_properties = {
                'author': client.login,
                'vm_name': vm['h'],
                'hv_source': vm['p'],
                'hv_dest': migration['did']
            }

            # Create the job:
            self._server.jobs.create(mtype, **migration_properties)
            errs.success(migration['sid'], 'migration launched')
        
        return errs.get_dict()
    @listed
    def clone(self, conn, tql_vm, tql_dest, name):
        '''
        Create and launch a clone job.

        :param tql_vm: a tql matching one vm object (the cloned vm)
        :param tql_dest: a tql matching one hypervisor object (the destination
                         hypervisor)
        :param name: the new name of the VM
        '''

        client = self._server.search_client_by_connection(conn)

        vm = self._server.list(tql_vm, show=('r', 'h', 'p'))

        if len(vm) != 1:
            raise CloneError('VM Tql must select ONE vm')
        elif vm[0]['r'] != 'vm':
            raise CloneError('Destination Tql must select a vm')
        else:
            vm = vm[0]

        dest = self._server.list(tql_dest, show=('r',))
        if len(dest) != 1:
            raise CloneError('Destination Tql must select ONE hypervisor')
        elif dest[0]['r'] != 'hv':
            raise CloneError('Destination Tql must select an hypervisor')
        else:
            dest = dest[0]

        self._server.jobs.create('clone', **{'vm_name': vm['h'],
                                           'new_vm_name': name,
                                           'hv_source': vm['p'],
                                           'hv_dest': dest['id'],
                                           'author': client.login})

    def dbstats(self, conn):
        '''
        Return statistics about current database status.
        '''
        return self._server.objects.stats()

    def proxy_client(self, conn, login, command, *args, **kwargs):
        client = self._server.get_connection(login)
        return client.connection.call(command, *args, **kwargs)
Antoine Millet's avatar
Antoine Millet committed

Antoine Millet's avatar
Antoine Millet committed

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 = {
        'cli': CliHandler,
        'hv': HypervisorHandler,
        'host': None,
Antoine Millet's avatar
Antoine Millet committed
    }

    @listed
    def authentify(self, conn, login, password):
Antoine Millet's avatar
Antoine Millet committed
        '''
        Authenticate the client.
Antoine Millet's avatar
Antoine Millet committed

        :param login: the login of the account
        :param password: the password of the account (not hashed)
        :return: role affected to the client on success
        :thrown AuthenticationError: when authentication fail
Antoine Millet's avatar
Antoine Millet committed
        '''
Antoine Millet's avatar
Antoine Millet committed

        logmsg = 'Authentication error from %s: '
        conf = self._server.conf
        with conf:
            try:
                role = self._server.conf.authentify(login, password)
            except CCConf.UnknownAccount:
                raise AuthenticationError('Unknown login')
            else:
                if 'close' in self._server.conf.show(login)['tags']:
                    logging.warning(logmsg + 'account closed (%s)',
                                    conn.getpeername(), login)
                    raise AuthenticationError('Account is closed')
Antoine Millet's avatar
Antoine Millet committed

        if role is None:
            logging.warning(logmsg + 'bad login/password (%s)',
                            conn.getpeername(), login)
            raise AuthenticationError('Bad login/password')
Antoine Millet's avatar
Antoine Millet committed
        else:
            if role not in WelcomeHandler.ROLES:
                logging.warning(logmsg + 'bad role in account config (%s)',
                                conn.getpeername(), login)
                raise BadRoleError('%r is not a legal role' % role)
            
            # If authentication is a success, try to register the client:
            try:
                self._server.register(login, role, conn)
                logging.warning(logmsg + 'already connected (%s)',
                                conn.getpeername(), login)
                raise AuthenticationError('Already connected')
            conn.set_handler(WelcomeHandler.ROLES.get(role)(self._server))

            logging.info('Authentication success from %s with login %s',
                         conn.getpeername(), login)
Antoine Millet's avatar
Antoine Millet committed
            return role