From f755d686b7dab54ed4e9df69aaea1e4e2f936a67 Mon Sep 17 00:00:00 2001 From: Antoine Millet Date: Thu, 4 Jun 2015 15:53:39 +0200 Subject: [PATCH] Implemented allocation job and handler --- cloudcontrol/server/clients/cli.py | 25 +++++++- cloudcontrol/server/jobs/__init__.py | 3 +- cloudcontrol/server/jobs/allocation.py | 86 ++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 cloudcontrol/server/jobs/allocation.py diff --git a/cloudcontrol/server/clients/cli.py b/cloudcontrol/server/clients/cli.py index aef2636..a59484e 100644 --- a/cloudcontrol/server/clients/cli.py +++ b/cloudcontrol/server/clients/cli.py @@ -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: # diff --git a/cloudcontrol/server/jobs/__init__.py b/cloudcontrol/server/jobs/__init__.py index e76a0cc..6a75dd6 100644 --- a/cloudcontrol/server/jobs/__init__.py +++ b/cloudcontrol/server/jobs/__init__.py @@ -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 diff --git a/cloudcontrol/server/jobs/allocation.py b/cloudcontrol/server/jobs/allocation.py new file mode 100644 index 0000000..0241574 --- /dev/null +++ b/cloudcontrol/server/jobs/allocation.py @@ -0,0 +1,86 @@ +# 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 . + + +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') -- GitLab