Loading cloudcontrol/server/clients/cli.py +23 −2 Original line number Original line Diff line number Diff line Loading @@ -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) Loading @@ -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, Loading Loading @@ -1012,7 +1014,7 @@ class CliHandler(RegisteredCCHandler): return errs.get_dict() return errs.get_dict() # # # Election / Migration / Cloning: # Election / Migration / Cloning / Allocation # # @listed @listed Loading Loading @@ -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: # # Loading cloudcontrol/server/jobs/__init__.py +2 −1 Original line number Original line Diff line number Diff line Loading @@ -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 cloudcontrol/server/jobs/allocation.py 0 → 100644 +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') Loading
cloudcontrol/server/clients/cli.py +23 −2 Original line number Original line Diff line number Diff line Loading @@ -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) Loading @@ -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, Loading Loading @@ -1012,7 +1014,7 @@ class CliHandler(RegisteredCCHandler): return errs.get_dict() return errs.get_dict() # # # Election / Migration / Cloning: # Election / Migration / Cloning / Allocation # # @listed @listed Loading Loading @@ -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: # # Loading
cloudcontrol/server/jobs/__init__.py +2 −1 Original line number Original line Diff line number Diff line Loading @@ -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
cloudcontrol/server/jobs/allocation.py 0 → 100644 +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')