Ejemplo n.º 1
0
    def revert_to_snapshot(self, instance, name=None, wait_for_ip=True):
        """
        Perform a quick snapshot revert on a VM instance

        This is a generator function which is used in a co-operative
        multitasking manner. Typically this would be used through run_on_instances().

        @param instance: dict of the VM instance to create
        @param name: name of snapshot, revert to current snapshot if None

        @return: generator function
        """
        def done(task):
            return (hasattr(task, 'info') and (task.info.state == 'success'
                                               or task.info.state == 'error'))

        def got_ip(task):
            return (hasattr(task, 'summary')
                    and getattr(task.summary.guest, 'ipAddress', None))

        vm_name = instance['vm_name']
        vm = instance['vm']
        if not vm:
            vm = self.vim.find_vm_by_name(vm_name)
        if not vm:
            raise InvalidParameterError(
                'VM %s not found in vSphere, something is terribly wrong here'
                % vm_name)

        self.log.debug('REVERT(%s) STARTING' % vm_name)
        if name:
            snapshots = vm.find_snapshots_by_name(name)
            if len(snapshots) != 1:
                raise InvalidParameterError(
                    'there must be one, and only one, snapshot with the name %r'
                    % name)
            task = snapshots[0].snapshot.revert_to_snapshot_task()
        else:
            task = vm.revert_to_current_snapshot_task()
        while not done(task):
            task = (yield task)
        self.log.debug('REVERT(%s) DONE' % vm_name)

        if wait_for_ip:
            self.log.debug('REVERT(%s) WAITING FOR IP' % (vm_name))
            task = vm
            while not got_ip(task):
                task = (yield task)
            self.log.debug('REVERT(%s) GOT IP: %s' %
                           (vm_name, task.summary.guest.ipAddress))
            instance['ipv4'] = task.summary.guest.ipAddress
Ejemplo n.º 2
0
    def update_vm(self, instance):
        """
        Get updated info from the VM instance

        This is a generator function which is used in a co-operative
        multitasking manner. Typically this would be used through run_on_instances().

        @param instance: dict of the VM instance to update

        @return: generator function
        """
        def done(task):
            return (hasattr(task, 'summary')
                    and getattr(task.summary.guest, 'ipAddress', None))

        vm_name = instance['vm_name']
        vm = instance.get('vm')
        if not vm:
            vm = self.vim.find_vm_by_name(vm_name)
        if not vm:
            raise InvalidParameterError(
                'VM %s not found in vSphere, something is terribly wrong here'
                % vm_name)

        self.log.debug("UPDATE-VM(%s) WAITING FOR IP" % (vm_name))
        task = vm
        while not done(task):
            task = (yield task)
        self.log.debug("UPDATE-VM(%s) GOT IP: %s" %
                       (vm_name, task.summary.guest.ipAddress))
        instance['ipv4'] = task.summary.guest.ipAddress
Ejemplo n.º 3
0
 def revert_to_snapshot(self, options):
     vm = self.vim.find_vm_by_name(options.vm_name)
     snapshotinfos = vm.find_snapshots_by_name(options.revert_to_snapshot)
     if len(snapshotinfos) != 1:
         raise InvalidParameterError(
             'there are multiple snapshots with the name %r' %
             options.revert_to_snapshot)
     snapshotinfos[0].snapshot.revert_to_snapshot()
Ejemplo n.º 4
0
    def remove_snapshot(self, instance, name=None):
        def done(task):
            return (hasattr(task, 'info') and (task.info.state == 'success'
                                               or task.info.state == 'error'))

        vm_name = instance['vm_name']
        vm = instance['vm']
        if not vm:
            vm = self.vim.find_vm_by_name(vm_name, ['snapshot'])
        if not vm:
            raise InvalidParameterError(
                'VM %s not found in vSphere, something is terribly wrong here'
                % vm_name)

        self.log.debug('REMOVE-SNAPSHOT(%s) STARTING' % vm_name)
        snapshots = vm.find_snapshots_by_name(name)
        if len(snapshots) != 1:
            raise InvalidParameterError(
                'there must be one, and only one, snapshot with the name %r' %
                name)
        task = snapshots[0].snapshot.remove_snapshot_task(remove_children=True)
        while not done(task):
            task = (yield task)
        self.log.debug('REMOVE-SNAPSHOT(%s) DONE' % vm_name)
Ejemplo n.º 5
0
 def _datastores_in_cluster(self, clustername):
     """ Find and return the list of available datastores for a ClusterComputeResource """
     if clustername not in self._cluster_datastore_cache:
         ccr = self.vim.find_entity_by_name('ClusterComputeResource',
                                            clustername,
                                            ['name', 'datastore'])
         if not ccr:
             raise InvalidParameterError(
                 'specified ClusterComputeResource %r not found' %
                 clustername)
         datastores = [
             ManagedObject(x, self.vim, ['name', 'summary', 'info'])
             for x in ccr.datastore
         ]
         self._cluster_datastore_cache[clustername] = datastores
     return self._cluster_datastore_cache.get(clustername, [])
Ejemplo n.º 6
0
    def power_on_off_vm(self, instance, off=False):
        """Power on/off a VM

        This is a generator function which is used in a co-operative
        multitasking manner. Typically this would be used through run_on_instances().

        @param instance: dict of the VM instance to power on/off

        @return: generator function
        """
        def done(task):
            return (hasattr(task, 'info') and (task.info.state == 'success'
                                               or task.info.state == 'error'))

        vm_name = instance['vm_name']
        vm = instance['vm']
        if not vm:
            vm = self.vim.find_vm_by_name(vm_name, ['summary'])
        if not vm:
            raise InvalidParameterError(
                'VM %s not found in vSphere, something is terribly wrong here'
                % vm_name)

        if vm.power_state() == 'poweredOff' and not off:
            self.log.debug('POWERON(%s) STARTING', vm_name)
            task = vm.power_on_task()
            while not done(task):
                task = (yield task)
            vm.update_local_view(['summary'])
            if vm.power_state() != 'poweredOn':
                raise TaskFailedError('%s was not successfully powered on',
                                      vm_name)
            self.log.debug('POWERON(%s) DONE', vm_name)
        elif vm.power_state() == 'poweredOn' and off:
            self.log.debug('POWEROFF(%s) STARTING', vm_name)
            task = vm.power_off_task()
            while not done(task):
                task = (yield task)
            vm.update_local_view(['summary'])
            if vm.power_state() != 'poweredOff':
                raise TaskFailedError('%s was not successfully powered off',
                                      vm_name)
            self.log.debug('POWEROFF(%s) DONE', vm_name)
        else:
            self.log.debug('VM %s is already %s', vm_name,
                           'off' if off else 'on')
Ejemplo n.º 7
0
 def place_vm(base_vm, placement_strategy='random'):
     """ Place the VM to the available datastores either randomly or wherever there is most space """
     assert placement_strategy in [
         'random', 'most-space'
     ], 'unknown placement strategy, must be either \'random\' or \'most-space\''
     # Make a list of datastores that have enough space and sort it by free space
     possible_targets = sorted([
         x for x in base_vm.available_datastores
         if x.summary.freeSpace > base_vm.size
     ],
                               key=lambda x: x.summary.freeSpace,
                               reverse=True)
     if len(possible_targets) == 0:
         raise InvalidParameterError(
             'no suitable datastore found. Are they all low on space?')
     if placement_strategy == 'random':
         target = random.choice(possible_targets)
     if placement_strategy == 'most-space':
         target = possible_targets[0]
     target.summary.freeSpace -= base_vm.size
     return target
Ejemplo n.º 8
0
    def create_snapshot(self,
                        instance,
                        name=None,
                        description=None,
                        memory=False):
        def done(task):
            return (hasattr(task, 'info') and (task.info.state == 'success'
                                               or task.info.state == 'error'))

        vm_name = instance['vm_name']
        vm = instance['vm']
        if not vm:
            vm = self.vim.find_vm_by_name(vm_name, ['snapshot'])
        if not vm:
            raise InvalidParameterError(
                'VM %s not found in vSphere, something is terribly wrong here'
                % vm_name)

        self.log.debug('CREATE-SNAPSHOT(%s) STARTING' % vm_name)
        task = vm.create_snapshot_task(name, description, memory)
        while not done(task):
            task = (yield task)
        self.log.debug('CREATE-SNAPSHOT(%s) DONE' % vm_name)
Ejemplo n.º 9
0
    def clone_vm(self, instance, nuke_old=False):
        """
        Perform a full clone-poweron-snapshot cycle on the instance

        This is a generator function which is used in a co-operative
        multitasking manner. Typically this would be used through run_on_instances().

        @param instance: dict of the VM instance to create
        @param nuke_old: should an existing VM with the same be nuked

        @return: generator function
        """
        def done(task):
            return (hasattr(task, 'info') and (task.info.state == 'success'
                                               or task.info.state == 'error'))

        def got_ip(task):
            return (hasattr(task, 'summary')
                    and getattr(task.summary.guest, 'ipAddress', None))

        def guest_tool_running(task):
            return (hasattr(task, 'summary')
                    and getattr(task.summary.guest, 'toolsRunningStatus',
                                None) == 'guestToolsRunning')

        def place_vm(base_vm, placement_strategy='random'):
            """ Place the VM to the available datastores either randomly or wherever there is most space """
            assert placement_strategy in [
                'random', 'most-space'
            ], 'unknown placement strategy, must be either \'random\' or \'most-space\''
            # Make a list of datastores that have enough space and sort it by free space
            possible_targets = sorted([
                x for x in base_vm.available_datastores
                if x.summary.freeSpace > base_vm.size
            ],
                                      key=lambda x: x.summary.freeSpace,
                                      reverse=True)
            if len(possible_targets) == 0:
                raise InvalidParameterError(
                    'no suitable datastore found. Are they all low on space?')
            if placement_strategy == 'random':
                target = random.choice(possible_targets)
            if placement_strategy == 'most-space':
                target = possible_targets[0]
            target.summary.freeSpace -= base_vm.size
            return target

        vm_name = instance['vm_name']
        base_vm = self._get_base_vm(instance)
        if not base_vm:
            raise InvalidParameterError(
                'base VM %s not found, check the cloud.base_vm_name property for %s'
                % (instance['base_vm_name'], vm_name))

        if nuke_old:
            clone = self.vim.find_vm_by_name(vm_name, ['summary'])
            if clone:
                if clone.power_state() == 'poweredOn':
                    self.log.debug('CLONE(%s) POWEROFF STARTING' % vm_name)
                    task = clone.power_off_task()
                    while not done(task):
                        task = (yield task)
                    self.log.debug('CLONE(%s) POWEOFF DONE' % vm_name)
                self.log.debug('CLONE(%s) DELETE STARTING' % vm_name)
                task = clone.delete_vm_task()
                while not done(task):
                    task = (yield task)
                self.log.debug('CLONE(%s) DELETE DONE' % vm_name)

        # Use the specified target datastore or pick one automagically based on the placement strategy
        datastore = instance.get('datastore', None)
        if not datastore:
            placement_strategy = instance.get('placement', 'random')
            datastore = place_vm(base_vm,
                                 placement_strategy=placement_strategy)

        cluster = instance.get('cluster', None)

        self.log.debug('CLONE(%s) CLONE STARTING' % vm_name)
        task = base_vm.clone_vm_task(vm_name,
                                     linked_clone=False,
                                     datastore=datastore,
                                     resource_pool=instance.get(
                                         'resource_pool', None),
                                     folder=instance.get('folder'),
                                     cluster=cluster)
        while not done(task):
            task = (yield task)
        if task.info.state != 'success':
            raise TaskFailedError(
                'CLONE(%s) failed with error: %r Details: %r' %
                (vm_name, task.info.error.localizedMessage,
                 task.info.error.fault))
        self.log.debug('CLONE(%s) CLONE DONE' % vm_name)

        clone = self.vim.find_vm_by_name(vm_name)
        assert clone, 'Could not find vm %s after cloning. Must not happen. Ever.' % (
            vm_name)

        # Reconfigure the VM hardware as specified
        hardware = instance.get('hardware', None)
        if hardware:
            # Find if any new disks or NICs need to be added to the VM
            disks = [
                hardware.get('disk%d' % x) for x in xrange(10)
                if hardware.get('disk%d' % x)
            ]
            nics = [
                hardware.get('nic%d' % x) for x in xrange(10)
                if hardware.get('nic%d' % x)
            ]
            spec = self.vim.create_object('VirtualMachineConfigSpec')
            if hardware.get('ram', None):
                spec.memoryMB = int(hardware['ram'])
            if hardware.get('cpus', None):
                spec.numCPUs = int(hardware['cpus'])
            for disk in disks:
                provisioning = disk.get('provisioning', 'thin')
                assert provisioning in [
                    'thin', 'thick'
                ], 'disk provisioning must be on of %r, not %r' % (
                    ['thin', 'thick'], provisioning)
                disk_mode = disk.get('mode', 'persistent')
                disk_spec = clone.spec_new_disk(size=int(disk['size']),
                                                thin=provisioning == 'thin',
                                                disk_mode=disk_mode)
                spec.deviceChange.append(disk_spec)
            for nic in nics:
                network = nic.get('network')
                assert network, 'network name must be specified for NICs'
                nic_type = nic.get('nic_type', 'vmxnet3')
                nic_spec = clone.spec_new_nic(network=network,
                                              nic_type=nic_type)
                spec.deviceChange.append(nic_spec)

            self.log.debug('CLONE(%s) RECONFIG_VM STARTING' % vm_name)
            task = clone.reconfig_vm_task(spec=spec)
            while not done(task):
                task = (yield task)
            self.log.debug('CLONE(%s) RECONFIG_VM DONE' % vm_name)

        self.log.debug('CLONE(%s) POWERON STARTING' % vm_name)
        task = clone.power_on_task()
        while not done(task):
            task = (yield task)
        clone.update_local_view(['summary'])
        if clone.power_state() != 'poweredOn':
            raise TaskFailedError('%s was not successfully powered on' %
                                  vm_name)
        self.log.debug('CLONE(%s) POWERON DONE' % vm_name)

        # Static IPV4 addresses can be specified for the interfaces in the VM.
        # The configuration dictionary has the following structure:
        #
        # {'eth0': {'address': '192.168.2.2',
        #           'netmask': '255.255.255.0'},
        #  'gateway': '192.168.2.1',
        #  'username': '******',
        #  'password': '******'}
        #
        network_config = instance.get('network')
        if network_config:
            self.log.debug('CLONE(%s) SETTING UP IP INTERFACES' % (vm_name))
            username = instance.get('username')
            password = instance.get('password')
            if not username or not password:
                raise InvalidParameterError(
                    "'cloud.username' and 'cloud.password' need to be specified for network interface setup"
                )
            script = ''
            for interface, parameters in [
                (k, v) for k, v in network_config.iteritems()
                    if k not in ['gateway', 'username', 'password']
            ]:
                address = parameters.get('address')
                netmask = parameters.get('netmask')
                if not address or not netmask:
                    raise InvalidParameterError(
                        "'address' and 'netmask' need to be specified for network interface configurations"
                    )
                script += 'ifconfig %s %s netmask %s' % (interface, address,
                                                         netmask)
            gateway = network_config.get('gateway')
            if gateway:
                script += 'route add default gw %s' % gateway
            self.log.debug('CLONE(%s) WAITING FOR GUEST TOOLS TO START' %
                           (vm_name))
            task = clone
            tool_wait_started = time.time()
            while not guest_tool_running(task):
                task = (yield task)
                if time.time() - tool_wait_started > 60.0:
                    raise TimeoutError(
                        'guest tools have not started in 60 seconds')
            self.log.debug('CLONE(%s) RUNNING INTERFACE SETUP SCRIPT IN VM' %
                           (vm_name))
            clone.run_script_in_guest(script, username, password)

        self.log.debug('CLONE(%s) WAITING FOR IP' % (vm_name))
        task = clone
        while not got_ip(task):
            task = (yield task)
        self.log.debug('CLONE(%s) GOT IP: %s' %
                       (vm_name, task.summary.guest.ipAddress))
        instance['ipv4'] = task.summary.guest.ipAddress

        self.log.debug('CLONE(%s) SNAPSHOT STARTING' % vm_name)
        task = clone.create_snapshot_task('pristine', memory=True)
        while not done(task):
            task = (yield task)
        self.log.debug('CLONE(%s) SNAPSHOT DONE' % vm_name)