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

Implemented allocation job and handler

parent cc47bce9
Loading
Loading
Loading
Loading
+23 −2
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@
from sjrpc.core import RpcError
from sjrpc.core import RpcError


from cloudcontrol.common.datastructures.orderedset import OrderedSet
from cloudcontrol.common.datastructures.orderedset import OrderedSet
from cloudcontrol.common.allocation.vmspec import expand_vmspec

from cloudcontrol.server.conf import CCConf
from cloudcontrol.server.conf import CCConf
from cloudcontrol.server.exceptions import (ReservedTagError, BadRoleError,
from cloudcontrol.server.exceptions import (ReservedTagError, BadRoleError,
                                            NotConnectedAccountError, CloneError)
                                            NotConnectedAccountError, CloneError)
@@ -26,7 +28,7 @@ from cloudcontrol.server.repository import RepositoryOperationError
from cloudcontrol.server.handlers import listed, Reporter
from cloudcontrol.server.handlers import listed, Reporter
from cloudcontrol.server.clients import Client, RegisteredCCHandler
from cloudcontrol.server.clients import Client, RegisteredCCHandler
from cloudcontrol.server.jobs import (ColdMigrationJob, HotMigrationJob,
from cloudcontrol.server.jobs import (ColdMigrationJob, HotMigrationJob,
                                      CloneJob, KillClientJob)
                                      CloneJob, KillClientJob, AllocationJob)
from cloudcontrol.common.tql.db.tag import StaticTag
from cloudcontrol.common.tql.db.tag import StaticTag


MIGRATION_TYPES = {'cold': ColdMigrationJob,
MIGRATION_TYPES = {'cold': ColdMigrationJob,
@@ -1012,7 +1014,7 @@ class CliHandler(RegisteredCCHandler):
        return errs.get_dict()
        return errs.get_dict()


    #
    #
    # Election / Migration / Cloning:
    # Election / Migration / Cloning / Allocation
    #
    #


    @listed
    @listed
@@ -1102,6 +1104,25 @@ class CliHandler(RegisteredCCHandler):
                                                        'hv_dest': dest['id']})
                                                        'hv_dest': dest['id']})
        return job.id
        return job.id


    @listed
    def allocate(self, vmspec, tql_target):
        """ Allocate new VMs.

        :param vmspec: a vmspec structure
        :param tql_target: a TQL used as target restriction
        """

        self.client.check('allocate')

        # Check and expand vmspec input:
        expanded_vmspec = expand_vmspec(vmspec)

        job = self.client.spawn_job(AllocationJob, settings={'server': self.server,
                                                             'client': self.client,
                                                             'expanded_vmspec': expanded_vmspec,
                                                             'tql_target': tql_target})
        return job.id

    #
    #
    # Remote console:
    # Remote console:
    #
    #
+2 −1
Original line number Original line Diff line number Diff line
@@ -22,7 +22,8 @@ from cloudcontrol.server.jobs.hotmigration import HotMigrationJob
from cloudcontrol.server.jobs.clone import CloneJob
from cloudcontrol.server.jobs.clone import CloneJob
from cloudcontrol.server.jobs.killoldcli import KillOldCliJob
from cloudcontrol.server.jobs.killoldcli import KillOldCliJob
from cloudcontrol.server.jobs.killclient import KillClientJob
from cloudcontrol.server.jobs.killclient import KillClientJob
from cloudcontrol.server.jobs.allocation import AllocationJob




__all__ = ('ColdMigrationJob', 'HotMigrationJob', 'CloneJob', 'KillOldCliJob',
__all__ = ('ColdMigrationJob', 'HotMigrationJob', 'CloneJob', 'KillOldCliJob',
           'KillClientJob')
           'KillClientJob', 'AllocationJob')
 No newline at end of file
 No newline at end of file
+86 −0
Original line number Original line Diff line number Diff line
# This file is part of CloudControl.
#
# CloudControl is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# CloudControl is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with CloudControl.  If not, see <http://www.gnu.org/licenses/>.


import threading

from cloudcontrol.common.jobs import Job
from cloudcontrol.server.allocator import Allocator, AllocationError


class DeployError(Exception):

    """ Exception raised when an error occurs while deploying a
        virtual machine on an hypervisor.
    """


class AllocationJob(Job):

    """ Allocate a set of VM on hypervisors.
    """

    # Global allocation lock. Must be locked before to do allocation for a
    # single virtual machine:
    allocation_lock = threading.Lock()

    def job(self, server, client, expanded_vmspec, tql_target=None):
        allocator = Allocator(self.logger.getChild('allocator'), server, client)

        results_by_vm = {}
        total = len(expanded_vmspec)
        errors = 0

        for i, vmspec in enumerate(expanded_vmspec):
            self.title = 'Virtual machines allocation (%d/%d, %d errors)' % (i + 1, total, errors)
            with AllocationJob.allocation_lock:
                self.checkpoint()
                try:
                    target_hv_name = allocator.allocate(vmspec, tql_target)[0]
                except AllocationError, err:
                    self.logger.warn('VM %s: allocation error: %s. Skipping...' % (vmspec['title'], err))
                    results_by_vm[vmspec['title']] = 'allocation error, %s' % err
                    errors += 1
                except Exception, err:
                    self.logger.exception('VM %s: unknow error while allocation: %s. Skipping...' % (vmspec['title'], err))
                    results_by_vm[vmspec['title']] = 'unknown error (see logs)'
                    errors += 1
                else:
                    # Keep the VM target in a tag for future migrations
                    if 'tags' not in vmspec:
                        vmspec['tags'] = {}
                    vmspec['tags'].setdefault('target', vmspec['target'])
                    try:
                        vm_uuid = self._deploy(vmspec, server.get_client(target_hv_name))
                    except Exception as err:
                        results_by_vm[vmspec['title']] = 'Error: %s' % err
                        errors += 1
                    else:
                        self.logger.info('VM %s: spawned on %s' % (vmspec['title'], target_hv_name))
                        results_by_vm[vmspec['title']] = 'spawned %s on %s' % (vm_uuid, target_hv_name)

        # Write a summary of the Allocation job in the results attachment:
        results = self.attachment('results')
        for vm_name, result in results_by_vm.iteritems():
            results.write('%s: %s\n' % (vm_name, result))

    def _deploy(self, vmspec, target_hv):
        # Delete keys which are not recognized by subsequent components:
        for key in ('target', 'flags', 'riskgroup'):
            try:
                del vmspec[key]
            except KeyError:
                pass
        return target_hv.define(vmspec, format='vmspec')