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 Diff line number Diff line
@@ -17,6 +17,8 @@
from sjrpc.core import RpcError

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

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

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

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

    @listed
@@ -1102,6 +1104,25 @@ class CliHandler(RegisteredCCHandler):
                                                        'hv_dest': dest['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:
    #
+2 −1
Original line number 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.killoldcli import KillOldCliJob
from cloudcontrol.server.jobs.killclient import KillClientJob
from cloudcontrol.server.jobs.allocation import AllocationJob


__all__ = ('ColdMigrationJob', 'HotMigrationJob', 'CloneJob', 'KillOldCliJob',
           'KillClientJob')
 No newline at end of file
           'KillClientJob', 'AllocationJob')
 No newline at end of file
+86 −0
Original line number 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')