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
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
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()
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)
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, [])
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')
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
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)
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)