def change_address( vm_hostname, new_address, offline=False, migrate=False, allow_reserved_hv=False, offline_transport='drbd', ): """Change VMs IP address This is done by changing data in Serveradmin, running Puppet in VM and rebooting it. """ if not offline: raise IGVMError('IP address change can be only performed offline') with _get_vm(vm_hostname) as vm: new_address = ip_address(new_address) if vm.dataset_obj['intern_ip'] == new_address: raise ConfigError('New IP address is the same as the old one!') if not vm.hypervisor.get_vlan_network(new_address) and not migrate: err = 'Current hypervisor does not support new subnet!' raise ConfigError(err) new_network = Query( { 'servertype': 'route_network', 'state': 'online', 'network_type': 'internal', 'intern_ip': Contains(new_address), } ).get()['hostname'] vm_was_running = vm.is_running() with Transaction() as transaction: if vm_was_running: vm.shutdown( transaction=transaction, check_vm_up_on_transaction=False, ) vm.change_address( new_address, new_network, transaction=transaction, ) if migrate: vm_migrate( vm_object=vm, run_puppet=True, offline=True, no_shutdown=True, allow_reserved_hv=allow_reserved_hv, offline_transport=offline_transport, ) else: vm.hypervisor.mount_vm_storage(vm, transaction=transaction) vm.run_puppet() vm.hypervisor.redefine_vm(vm) vm.hypervisor.umount_vm_storage(vm) if vm_was_running: vm.start()
def check_serveradmin_config(self): """Validate relevant Serveradmin attributes""" mul_numa_nodes = 128 * self.hypervisor.num_numa_nodes() validations = [ ( 'hostname', re_compile(r'\A[a-z][a-z0-9\.\-]+\Z').match, 'invalid hostname', ), ('memory', lambda v: v > 0, 'memory must be > 0'), # https://medium.com/@juergen_thomann/memory-hotplug-with-qemu-kvm-and-libvirt-558f1c635972#.sytig6o9h ( 'memory', lambda v: v % mul_numa_nodes == 0, 'memory must be multiple of {}MiB'.format(mul_numa_nodes), ), ('num_cpu', lambda v: v > 0, 'num_cpu must be > 0'), ('os', lambda v: True, 'os must be set'), ( 'disk_size_gib', lambda v: v > 0, 'disk_size_gib must be > 0', ), ('puppet_ca', lambda v: True, 'puppet_ca must be set'), ('puppet_master', lambda v: True, 'puppet_master must be set'), ] for attr, check, err in validations: value = self.dataset_obj[attr] if not value: raise ConfigError('"{}" attribute is not set'.format(attr)) if not check(value): raise ConfigError(err)
def vm_rename(vm_hostname, new_hostname, offline=False): """Redefine the VM on the same hypervisor with a different name We can only do this operation offline. If the VM is online, it needs to be shut down. No data will be lost. """ with _get_vm(vm_hostname) as vm: if vm.dataset_obj['puppet_disabled']: raise ConfigError( 'Rename command only works with Puppet enabled' ) _check_defined(vm) if not offline: raise NotImplementedError( 'Rename command only works with --offline at the moment.' ) if not vm.is_running(): raise NotImplementedError( 'Rename command only works online at the moment.' ) vm.rename(new_hostname)
def vm_rename(vm_hostname, new_hostname, offline=False): """Redefine the VM on the same hypervisor with a different name We can only do this operation offline. If the VM is online, it needs to be shut down. No data will be lost. """ with _get_vm(vm_hostname) as vm: if vm.dataset_obj['datacenter_type'] not in ['aws.dct', 'kvm.dct']: raise NotImplementedError( 'This operation is not yet supported for {}'.format( vm.dataset_obj['datacenter_type'])) if vm.dataset_obj['puppet_disabled']: raise ConfigError('Rename command only works with Puppet enabled') if vm.dataset_obj['datacenter_type'] == 'kvm.dct': _check_defined(vm) if not offline: raise NotImplementedError( 'Rename command only works with --offline at the moment.') if not vm.is_running(): raise NotImplementedError( 'Rename command only works online at the moment.') vm.rename(new_hostname) elif vm.dataset_obj['datacenter_type'] == 'aws.dct': vm.aws_rename(new_hostname)
def vm_set_num_cpu(self, vm, num_cpu): """Change the number of CPUs of a VM""" self._check_committed(vm) self._check_attribute_synced(vm, 'num_cpu') if num_cpu < 1: raise ConfigError('Invalid num_cpu value: {}'.format(num_cpu)) log.info('Changing #CPUs of "{}" on "{}" from {} to {}...'.format( vm.fqdn, self.fqdn, vm.dataset_obj['num_cpu'], num_cpu)) # If VM is offline, we can just rebuild the domain if not self.vm_running(vm): log.info('VM is offline, rebuilding domain with new settings') vm.dataset_obj['num_cpu'] = num_cpu self.redefine_vm(vm) else: set_vcpus(self, vm, self._get_domain(vm), num_cpu) # Validate changes # We can't rely on the hypervisor to provide data on VMs all the time. updated_dataset_obj = self.vm_sync_from_hypervisor(vm) current_num_cpu = updated_dataset_obj['num_cpu'] if current_num_cpu != num_cpu: raise HypervisorError( 'New CPUs are not visible to hypervisor, changes will not be ' 'committed.') vm.dataset_obj['num_cpu'] = num_cpu vm.dataset_obj.commit()
def get_puppet_ca(vm): puppet_ca_type = Query( { 'hostname': vm['puppet_ca'] }, ['servertype'], ).get()['servertype'] if puppet_ca_type not in ['vm', 'public_domain']: raise ConfigError( 'Servertype {} not supported for puppet_ca'.format( puppet_ca_type, ), ) if puppet_ca_type == 'vm': return vm['puppet_ca'] ca_query = Query( {'domain': vm['puppet_ca']}, [{ 'lb_nodes': ['hostname', 'state'] }], ) ca_hosts = [ lb_node['hostname'] for res in ca_query for lb_node in res['lb_nodes'] if lb_node['state'] in ['online', 'deploy_online'] ] random.shuffle(ca_hosts) return ca_hosts[0]
def format_vm_storage(self, vm, transaction=None): """Create new filesystem for VM and mount it. Returns mount path.""" if self.vm_defined(vm): raise InvalidStateError( 'Refusing to format storage of defined VM "{}".'.format( vm.fqdn)) mkfs_options = XFS_CONFIG.get(vm.dataset_obj['os']) if not mkfs_options: raise ConfigError('No mkfs options defined for OS {}'.format( vm.dataset_obj['os'])) self.format_storage(self.get_volume_by_vm(vm).path(), mkfs_options) return self.mount_vm_storage(vm, transaction)
def change_address(vm_hostname, new_address, offline=False): """Change VMs IP address This is done by changing data in Serveradmin, running Puppet in VM and rebooting it. """ if not offline: raise IGVMError('IP address change can be only performed offline') with _get_vm(vm_hostname) as vm: if vm.dataset_obj['igvm_operation_mode'] != 'kvm': raise NotImplementedError( 'This operation is not yet supported for {}'.format( vm.dataset_obj['igvm_operation_mode'])) old_address = vm.dataset_obj['intern_ip'] new_address = ip_address(new_address) if old_address == new_address: raise ConfigError('New IP address is the same as the old one!') vm_was_running = vm.is_running() vm.dataset_obj['intern_ip'] = new_address vm.dataset_obj.commit() if vm_was_running: vm.shutdown() try: with Transaction() as transaction: vm.hypervisor.mount_vm_storage(vm, transaction) vm.run_puppet() vm.hypervisor.redefine_vm(vm) vm.hypervisor.umount_vm_storage(vm) if vm_was_running: vm.start() except BaseException: vm.dataset_obj['intern_ip'] = old_address vm.dataset_obj.commit() raise
def _check_committed(self, vm): """Check that the given VM has no uncommitted changes""" if vm.dataset_obj.is_dirty(): raise ConfigError( 'VM object has uncommitted changes, commit them first!')