Commit d1b1b91e authored by Antoine Millet's avatar Antoine Millet
Browse files

Updated handlers and jobs to use the new rights manager API

parent 239c63b4
Loading
Loading
Loading
Loading
+36 −121
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ from cloudcontrol.common.datastructures.orderedset import OrderedSet
from cloudcontrol.server.conf import CCConf
from cloudcontrol.server.exceptions import (ReservedTagError, BadObjectError,
                                            BadRoleError, NotConnectedAccountError,
                                            CloneError)
                                            CloneError, RightError)
from cloudcontrol.server.election import Elector

from cloudcontrol.server.handlers import listed, Reporter
@@ -66,9 +66,8 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects to show
        """

        self.check('list', query)
        self.logger.debug('Executed list function with query %s', query)
        objects = self.server.list(query)
        objects = self.client.list(query, method='list')
        order = OrderedSet(['id'])
        #if tags is not None:
        #    order |= OrderedSet(tags)
@@ -79,7 +78,7 @@ class CliHandler(RegisteredCCHandler):
        """
        errs = Reporter()
        # Search all hypervisors of selected vms:
        for vm in self.server.list(query, show=('r', 'h', 'p')):
        for vm in self.client.list(query, show=('r', 'h', 'p'), method=method):
            if vm['r'] != 'vm':
                errs.error(vm['id'], 'not a vm')
            else:
@@ -99,35 +98,30 @@ class CliHandler(RegisteredCCHandler):
    def start(self, query):
        """ Start a virtual machine.
        """
        self.check('start', query)
        return self._vm_action(query, 'vm_start')

    @listed
    def stop(self, query):
        """ Stop a virtual machine.
        """
        self.check('stop', query)
        return self._vm_action(query, 'vm_stop')

    @listed
    def destroy(self, query):
        """ Destroy (hard shutdown) a virtual machine.
        """
        self.check('destroy', query)
        return self._vm_action(query, 'vm_destroy')

    @listed
    def pause(self, query):
        """ Pause a virtual machine.
        """
        self.check('pause', query)
        return self._vm_action(query, 'vm_suspend')

    @listed
    def resume(self, query):
        """ Resume a virtual machine.
        """
        self.check('resume', query)
        return self._vm_action(query, 'vm_resume')

    @listed
@@ -136,7 +130,6 @@ class CliHandler(RegisteredCCHandler):

        :param query: tql query
        """
        self.check('disablevirtiocache', query)
        return self._vm_action(query, 'vm_disable_virtio_cache')

    @listed
@@ -160,9 +153,7 @@ class CliHandler(RegisteredCCHandler):
            success.
        """

        self.check('undefine', query)

        objects = self.server.list(query, show=('r', 'p', 'h', 'disk*',))
        objects = self.client.list(query, show=('r', 'p', 'h', 'disk*',), method='undefine')
        errs = Reporter()
        for obj in objects:
            if obj['r'] != 'vm':
@@ -193,8 +184,7 @@ class CliHandler(RegisteredCCHandler):
        :return: a standard report output
        """

        self.check('passwd', query)
        objects = self.server.list(query, show=('a',))
        objects = self.client.list(query, show=('a',), method='passwd')
        errs = Reporter()
        with self.conf:
            for obj in objects:
@@ -216,7 +206,7 @@ class CliHandler(RegisteredCCHandler):
        :param password: the password of the new account (None = not set)
        """

        self.check('addaccount')
        self.client.check('addaccount')

        if role in Client.roles:
            self.conf.create_account(login, role, password)
@@ -232,7 +222,7 @@ class CliHandler(RegisteredCCHandler):
        :param password: the password of the new account (default None)
        """

        self.check('addaccount')
        self.client.check('addaccount')
        self.conf.copy_account(copy_login, login, password)

    @listed
@@ -244,12 +234,10 @@ class CliHandler(RegisteredCCHandler):
        :param tag_value: the value of the tag
        """

        self.check('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=('a',))
        objects = self.client.list(query, show=('a',), method='addtag')
        errs = Reporter()
        with self.conf:
            for obj in objects:
@@ -282,12 +270,10 @@ class CliHandler(RegisteredCCHandler):
        :param tag_name: the name of the tag to remove
        """

        self.check('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=('a',))
        objects = self.client.list(query, show=('a',), method='deltag')
        errs = Reporter()
        with self.conf:
            for obj in objects:
@@ -312,8 +298,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects
        """

        self.check('tags', query)
        objects = self.server.list(query, show=('a',))
        objects = self.client.list(query, show=('a',), method='tags')
        tags = []
        for obj in objects:
            o = {'id': obj['id']}
@@ -331,8 +316,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects
        """

        self.check('delaccount', query)
        objects = self.server.list(query, show=('a',))
        objects = self.client.list(query, show=('a',), method='delaccount')
        errs = Reporter()
        with self.server.conf:
            for obj in objects:
@@ -358,8 +342,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects
        """

        self.check('close', query)
        objects = self.server.list(query, show=('a',))
        objects = self.client.list(query, show=('a',), method='close')
        errs = Reporter()
        with self.server.conf:
            for obj in objects:
@@ -388,8 +371,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects
        """

        self.check('declose', query)
        objects = self.server.list(query, show=('a',))
        objects = self.client.list(query, show=('a',), method='declose')
        errs = Reporter()
        with self.server.conf:
            for obj in objects:
@@ -414,8 +396,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the query to select objects
        """

        self.check('kill', query)
        objects = self.server.list(query, show=set(('a',)))
        objects = self.client.list(query, show=set(('a',)), method='kill')
        errs = Reporter()
        with self.server.conf:
            for obj in objects:
@@ -432,80 +413,22 @@ class CliHandler(RegisteredCCHandler):
        return errs.get_dict()

    @listed
    def rights(self, query):
        """ Get the rights of selected accounts.

        :param query: the query to select objects
    def loadrights(self):
        """ Get the current ruleset.
        """

        self.check('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.')

        return rules

    @listed
    def addright(self, 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('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()
        self.client.check('rights')
        return self.server.rights.export()

    @listed
    def delright(self, query, index):
        """ Remove a right rule from the selected objects.
    def saverights(self, ruleset):
        """ Set the current ruleset.

        :param query: the query to select objects
        :param index: the index of the right rule to remove
        :param ruleset: the ruleset to load.
        """

        self.check('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()
        self.client.check('rights')
        self.server.rights.load(ruleset)

    @listed
    def execute(self, query, command):
@@ -519,8 +442,7 @@ class CliHandler(RegisteredCCHandler):
            success.
        """

        self.check('execute', query)
        objects = self.server.list(query, show=('r',))
        objects = self.client.list(query, show=('r',), method='execute')
        errs = Reporter()
        for obj in objects:
            if obj['r'] not in ('hv', 'host'):
@@ -548,8 +470,7 @@ class CliHandler(RegisteredCCHandler):
            message an error message.
        """

        self.check('execute', query)
        objects = self.server.list(query, show=set(('r',)))
        objects = self.client.list(query, show=set(('r',)), method='execute')
        errs = Reporter()
        for obj in objects:
            if obj['r'] not in ('hv', 'host'):
@@ -577,8 +498,7 @@ class CliHandler(RegisteredCCHandler):
        :param query: the tql query used to select jobs to cancel
        """

        self.check('cancel', query)
        objects = self.server.list(query, show=('r', 'p'))
        objects = self.client.list(query, show=('r', 'p'), method='cancel')
        errs = Reporter()
        for obj in objects:
            if obj['r'] != 'job':
@@ -604,8 +524,7 @@ class CliHandler(RegisteredCCHandler):
        .. note::
           Purge only work for job with state done.
        """
        self.check('purge', query)
        objects = self.server.list(query, show=('r', 'p', 'state'))
        objects = self.client.list(query, show=('r', 'p', 'state'), method='purge')
        errs = Reporter()
        for obj in objects:
            if obj['r'] != 'job':
@@ -631,8 +550,7 @@ class CliHandler(RegisteredCCHandler):

        :param query: the tql query used to select jobs
        """
        self.check('attachment', query)
        objects = self.server.list(query, show=('r', 'p'))
        objects = self.server.list(query, show=('r', 'p'), method='attachment')
        errs = Reporter()
        for obj in objects:
            if obj['r'] != 'job':
@@ -664,7 +582,7 @@ class CliHandler(RegisteredCCHandler):
        :param mtype: type of migration
        :param algo: algo used for distribution
        """
        elector = Elector(self.server, query_vm, query_dest, self.client.login)
        elector = Elector(self.server, query_vm, query_dest, self.client)
        return elector.election(mtype, algo, **kwargs)

    @listed
@@ -688,6 +606,7 @@ class CliHandler(RegisteredCCHandler):

            # Construct the migration properties:
            migration_properties = {
                'client': self.client,
                'server': self.server,
                'vm_name': vm['h'],
                'hv_source': vm['p'],
@@ -710,7 +629,7 @@ class CliHandler(RegisteredCCHandler):
        :param name: the new name of the VM
        """

        vm = self.server.list(tql_vm, show=('r', 'h', 'p'))
        vm = self.clone.list(tql_vm, show=('r', 'h', 'p'), method='clone')

        if len(vm) != 1:
            raise CloneError('VM Tql must select ONE vm')
@@ -719,7 +638,7 @@ class CliHandler(RegisteredCCHandler):
        else:
            vm = vm[0]

        dest = self.server.list(tql_dest, show=('r',))
        dest = self.clone.list(tql_dest, show=('r',), method='clone')
        if len(dest) != 1:
            raise CloneError('Destination Tql must select ONE hypervisor')
        elif dest[0]['r'] != 'hv':
@@ -740,8 +659,7 @@ class CliHandler(RegisteredCCHandler):
        :param tql: tql matching only one object on which start the console
        :return: the label of the created tunnel
        """
        self.check('console', tql)
        objects = self.server.list(tql, show=('r', 'p', 'h'))
        objects = self.server.list(tql, show=('r', 'p', 'h'), method='console')
        if len(objects) != 1:
            raise NotImplementedError('Console only support one tunnel at time for now')
        errs = Reporter()
@@ -762,8 +680,7 @@ class CliHandler(RegisteredCCHandler):
        :param tql: tql matching only one object on which start the rshell
        :return: the label of the created tunnel
        """
        self.check('rshell', tql)
        objects = self.server.list(tql, show=('r', 'p'))
        objects = self.server.list(tql, show=('r', 'p'), method='rshell')
        if len(objects) != 1:
            raise NotImplementedError('Rshell only support one tunnel at time for now')
        errs = Reporter()
@@ -787,7 +704,6 @@ class CliHandler(RegisteredCCHandler):
        :param xpixel: unused
        :param ypixel: unused
        """
        self.check('rshell')
        ttype, client, ctun, stun = self.client.get_tunnel(label)
        if ttype != 'rshell':
            raise ValueError('Label does not refers on a rshell')
@@ -797,7 +713,6 @@ class CliHandler(RegisteredCCHandler):
    def rshell_wait(self, label):
        """ Wait for a remote shell termination.
        """
        self.check('rshell')
        ttype, client, ctun, stun = self.client.get_tunnel(label)
        if ttype != 'rshell':
            raise ValueError('Label does not refers on a rshell')
@@ -820,7 +735,7 @@ class CliHandler(RegisteredCCHandler):
        :param port: port on which establish the tunnel on destination
        :param destination: tunnel destination (from the remote client side)
        """
        self.check('forward', 'id=%s' % login)
        self.client.check('forward', query='id=%s' % login)
        # Create the tunnel to the node:
        try:
            host_client = self.server.get_client(login)
@@ -844,7 +759,7 @@ class CliHandler(RegisteredCCHandler):
        :param func: function to execute on the client
        :param \*args, \*\*kwargs: arguments of the call
        """
        self.check('forward_call')
        self.client.check('forward_call')
        client = self.server.get_client(login)
        return client.conn.call(func, *args, **kwargs)

+5 −18
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ class Elector(object):
                 ('not_source_hv', 'filter source hv'),
                 ('is_connected', 'filter connected hv'),
                 ('vm_htype_eq_hv', 'filter bad hv types'),
                 ('has_rights', 'filter rights'),
                 ('has_alloc', 'filter allocatable hv'),
                 ('duplicate_name', 'filter vm duplicate names'),
                 ('enough_disk', 'filter hv with not enough disk'),),
@@ -33,7 +32,6 @@ class Elector(object):
                ('not_source_hv', 'filter source hv'),
                ('is_connected', 'filter connected hv'),
                ('vm_htype_eq_hv', 'filter bad hv types'),
                ('has_rights', 'filter rights'),
                ('has_alloc', 'filter allocatable hv'),
                ('duplicate_name', 'filter vm duplicate names'),
                #('enough_ram', 'filter hv with not enough ram'),
@@ -44,7 +42,7 @@ class Elector(object):
    ALGO_BY_TYPES = {'cold': ('fair', ),
                     'hot': ('fair', )}

    def __init__(self, server, query_vm, query_dest, login):
    def __init__(self, server, query_vm, query_dest, client):
        # The server instance for this election:
        self._server = server

@@ -54,8 +52,8 @@ class Elector(object):
        # The TQL query to select destination hypervisor:
        self._query_dest = query_dest

        # The login of the initiator of the election:
        self._login = login
        # The client who requested the election:
        self._client = client

    def election(self, mtype, algo):
        """ Generate a new migration plan for this election. You must specify
@@ -108,8 +106,8 @@ class Elector(object):
            hv_tags |= getattr(filterfunc, '__tags__', set())

        # Get the selected vms and hvs:
        vms = self._server.list(self._query_vm, show=('*',))
        hvs = self._server.list(self._query_dest, show=hv_tags)
        vms = self._client.list(self._query_vm, show=('*',), method='migrate')
        hvs = self._client.list(self._query_dest, show=hv_tags, method='migrate')

        candidates = []
        errors = []
@@ -118,9 +116,6 @@ class Elector(object):
        for vm in vms:
            if vm['r'] != 'vm':
                continue
            tql = 'id=%s' % vm['id']
            if not self._server.check(self._login, 'coldmigrate', tql):
                continue
            vm_dest = copy(hvs)
            for func, desc in filterfuncs:
                vm_dest = func(vm, vm_dest)
@@ -202,14 +197,6 @@ class Elector(object):
                returned.append(hv)
        return returned

    def _filter_has_rights(self, vm, hvs):
        returned = []
        for hv in hvs:
            tql = 'id=%s' % hv['id']
            if self._server.check(self._login, 'coldmigrate_dest', tql):
                returned.append(hv)
        return returned

    @tags('alloc')
    def _filter_has_alloc(self, vm, hvs):
        returned = []
+10 −9
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from sjrpc.core import AsyncWatcher
from cloudcontrol.common.jobs import Job, JobCancelError

from cloudcontrol.server.utils import AcquiresAllOrNone
from cloudcontrol.server.exceptions import RightError


class CloneJob(Job):
@@ -19,7 +20,7 @@ class CloneJob(Job):
     * author: login of the author cli
    """

    def job(self, server, hv_source, vm_name, hv_dest, new_vm_name):
    def job(self, server, client, hv_source, vm_name, hv_dest, new_vm_name):
        self._func_cancel_xfer = None  # Callback to a function used to cancel
                                       # a disk transfert
        vm_id = '%s.%s' % (hv_source, vm_name)
@@ -29,15 +30,15 @@ class CloneJob(Job):

        # Cancel the job if the user has not the right to clone the vm or to
        # select an hypervisor as destination:
        right_check = server.check

        tql = 'id=%s' % vm_id
        if not right_check(self.state.owner, 'clone', tql):
            raise JobCancelError('author have no rights to migrate this VM')
        try:
            client.check('clone', 'id=%s' % vm_id)
        except RightError:
            raise JobCancelError('author have no rights to clone this VM')

        tql = 'id=%s' % hv_dest
        if not right_check(self.state.owner, 'clone_dest', tql):
            raise JobCancelError('author have no right to migrate to this hv')
        try:
            client.check('clone', 'id=%s' % hv_dest)
        except RightError:
            raise JobCancelError('author have no right to clone to this hv')

        # Update the VM object:
        vm = server.db.get_by_id(vm_id)
+8 −7
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ from sjrpc.core import AsyncWatcher
from cloudcontrol.common.jobs import Job, JobCancelError

from cloudcontrol.server.utils import AcquiresAllOrNone
from cloudcontrol.server.exceptions import RightError


class ColdMigrationJob(Job):
@@ -16,7 +17,7 @@ class ColdMigrationJob(Job):
     * author: login of the author cli
    """

    def job(self, server, hv_source, vm_name, hv_dest):
    def job(self, server, client, hv_source, vm_name, hv_dest):
        self._func_cancel_xfer = None  # Callback to a function used to cancel
                                       # a disk transfert
        vm_id = '%s.%s' % (hv_source, vm_name)
@@ -26,14 +27,14 @@ class ColdMigrationJob(Job):

        # Cancel the job if the user has not the right to migrate the vm or to
        # select an hypervisor as destination:
        right_check = server.check

        tql = 'id=%s' % vm_id
        if not right_check(self.state.owner, 'coldmigrate', tql):
        try:
            client.check('migrate', 'id=%s' % vm_id)
        except RightError:
            raise JobCancelError('author have no rights to migrate this VM')

        tql = 'id=%s' % hv_dest
        if not right_check(self.state.owner, 'coldmigrate_dest', tql):
        try:
            client.check('migrate', 'id=%s' % hv_dest)
        except RightError:
            raise JobCancelError('author have no right to migrate to this hv')

        # Update the VM object:
+8 −7
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from sjrpc.core import AsyncWatcher
from cloudcontrol.common.jobs import Job, JobCancelError

from cloudcontrol.server.utils import AcquiresAllOrNone
from cloudcontrol.server.exceptions import RightError


class HotMigrationJob(Job):
@@ -18,7 +19,7 @@ class HotMigrationJob(Job):
     * author: login of the author cli
    """

    def job(self, server, hv_source, vm_name, hv_dest):
    def job(self, server, client, hv_source, vm_name, hv_dest):
        vm_id = '%s.%s' % (hv_source, vm_name)

        self.title = 'Hot migration %s --> %s' % (vm_id, hv_dest)
@@ -26,14 +27,14 @@ class HotMigrationJob(Job):

        # Cancel the job if the user has not the right to migrate the vm or to
        # select an hypervisor as destination:
        right_check = server.check

        tql = 'id=%s' % vm_id
        if not right_check(self.state.owner, 'hotmigrate', tql):
        try:
            client.check('migrate', 'id=%s' % vm_id)
        except RightError:
            raise JobCancelError('author have no rights to migrate this VM')

        tql = 'id=%s' % hv_dest
        if not right_check(self.state.owner, 'hotmigrate_dest', tql):
        try:
            client.check('migrate', 'id=%s' % hv_dest)
        except RightError:
            raise JobCancelError('author have no right to migrate to this hv')

        # Update the VM object:
Loading