Skip to content
allocation.py 3.54 KiB
Newer Older
# 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')