def disconnect_switch_port(self, switch_port_name, vnic_deleted, delete_port): """Disconnects the switch port.""" switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] switch_port_path = self._get_switch_port_path_by_name( switch_port_name) if not switch_port_path: # Port not found. It happens when the VM was already deleted. return if not vnic_deleted: (ret_val, ) = switch_svc.DisconnectSwitchPort( SwitchPort=switch_port_path) if ret_val != 0: data = {'switch_port_name': switch_port_name, 'ret_val': ret_val} raise exceptions.HyperVException( message=_('Failed to disconnect port %(switch_port_name)s ' 'with error %(ret_val)s') % data) if delete_port: (ret_val, ) = switch_svc.DeleteSwitchPort( SwitchPort=switch_port_path) if ret_val != 0: data = {'switch_port_name': switch_port_name, 'ret_val': ret_val} raise exceptions.HyperVException( message=_('Failed to delete port %(switch_port_name)s ' 'with error %(ret_val)s') % data)
def _get_vm(self, conn_v2, vm_name): vms = conn_v2.Msvm_ComputerSystem(ElementName=vm_name) n = len(vms) if not n: raise exception.NotFound(_('VM not found: %s') % vm_name) elif n > 1: raise exceptions.HyperVException(_('Duplicate VM name found: %s') % vm_name) return vms[0]
def check_live_migration_config(self): conn_v2 = self._get_conn_v2() migration_svc = conn_v2.Msvm_VirtualSystemMigrationService()[0] vsmssds = migration_svc.associators( wmi_association_class='Msvm_ElementSettingData', wmi_result_class='Msvm_VirtualSystemMigrationServiceSettingData') vsmssd = vsmssds[0] if not vsmssd.EnableVirtualSystemMigration: raise exceptions.HyperVException( _('Live migration is not enabled on this host')) if not migration_svc.MigrationServiceListenerIPAddressList: raise exceptions.HyperVException( _('Live migration networks are not configured on this host'))
def _get_conn_v2(self, host='localhost'): try: return wmi.WMI(moniker='//%s/root/virtualization/v2' % host) except wmi.x_wmi as ex: LOG.exception(_LE('Get version 2 connection error')) if ex.com_error.hresult == -2147217394: msg = (_('Live migration is not supported on target host "%s"') % host) elif ex.com_error.hresult == -2147023174: msg = (_('Target live migration host "%s" is unreachable') % host) else: msg = _('Live migration failed: %s') % ex.message raise exceptions.HyperVException(msg)
def _lookup_vm_check(self, vm_name): vm = self._lookup_vm(vm_name) if not vm: raise exceptions.HyperVVMNotFoundException( _('VM not found: %s') % vm_name) return vm
def _get_vswitch(self, vswitch_name): vswitch = self._conn.Msvm_VirtualEthernetSwitch( ElementName=vswitch_name) if not len(vswitch): raise exceptions.HyperVException(_('VSwitch not found: %s') % vswitch_name) return vswitch[0]
def check_ret_val(self, ret_val, job_path, success_values=[0]): if ret_val in [constants.WMI_JOB_STATUS_STARTED, constants.WMI_JOB_STATE_RUNNING]: return self._wait_for_job(job_path) elif ret_val not in success_values: raise exceptions.HyperVException( _('Operation failed with return value: %s') % ret_val)
def execute(self, *args, **kwargs): stdout_value, stderr_value = _utils.execute(*args, **kwargs) if stdout_value.find('The operation completed successfully') == -1: raise exceptions.HyperVException( _('An error has occurred when calling the iscsi initiator: %s') % stdout_value) return stdout_value
def _get_vnic_settings(self, vnic_name): vnic_settings = self._conn.Msvm_SyntheticEthernetPortSettingData( ElementName=vnic_name) if not vnic_settings: raise exceptions.HyperVException( message=_('Vnic not found: %s') % vnic_name) return vnic_settings[0]
def create_dynamic_vhd(self, path, max_internal_size, format): vhd_format = self._vhd_format_map.get(format) if not vhd_format: raise exceptions.HyperVException(_("Unsupported disk format: %s") % format) self._create_vhd(self._VHD_TYPE_DYNAMIC, vhd_format, path, max_internal_size=max_internal_size)
def get_free_controller_slot(self, scsi_controller_path): attached_disks = self.get_attached_disks(scsi_controller_path) used_slots = [int(disk.AddressOnParent) for disk in attached_disks] for slot in range(constants.SCSI_CONTROLLER_SLOTS_NUMBER): if slot not in used_slots: return slot raise exceptions.HyperVException( _("Exceeded the maximum number of slots"))
def _lookup_vm(self, vm_name): vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) n = len(vms) if n == 0: return None elif n > 1: raise exceptions.HyperVException( _('Duplicate VM name found: %s') % vm_name) else: return vms[0]
def create_dynamic_vhd(self, path, max_internal_size, format): if format != constants.DISK_FORMAT_VHD: raise exceptions.HyperVException(_("Unsupported disk format: %s") % format) image_man_svc = self._conn.Msvm_ImageManagementService()[0] (job_path, ret_val) = image_man_svc.CreateDynamicVirtualHardDisk( Path=path, MaxInternalSize=max_internal_size) self._jobutils.check_ret_val(ret_val, job_path)
def host_power_action(self, action): win32_os = self._conn_cimv2.Win32_OperatingSystem()[0] if action == constants.HOST_POWER_ACTION_SHUTDOWN: win32_os.Win32Shutdown(self._HOST_FORCED_SHUTDOWN) elif action == constants.HOST_POWER_ACTION_REBOOT: win32_os.Win32Shutdown(self._HOST_FORCED_REBOOT) else: raise NotImplementedError( _("Host %(action)s is not supported by the Hyper-V driver") % {"action": action})
def _get_vhd_dynamic_blk_size(self, vhd_path): blk_size_offset = VHD_BLK_SIZE_OFFSET try: with open(vhd_path, "rb") as f: f.seek(blk_size_offset) version = f.read(4) except IOError: raise exceptions.HyperVException(_("Unable to obtain block size " "from VHD %(vhd_path)s") % {"vhd_path": vhd_path}) return struct.unpack('>i', version)[0]
def _get_virt_utils_class(v1_class, v2_class): # The "root/virtualization" WMI namespace is no longer supported on # Windows Server / Hyper-V Server 2012 R2 / Windows 8.1 # (kernel version 6.3) or above. if (CONF.hyperv.force_hyperv_utils_v1 and utils.check_min_windows_version(6, 3)): raise exceptions.HyperVException( _('The "force_hyperv_utils_v1" option cannot be set to "True" ' 'on Windows Server / Hyper-V Server 2012 R2 or above as the WMI ' '"root/virtualization" namespace is no longer supported.')) return _get_class(v1_class, v2_class, CONF.hyperv.force_hyperv_utils_v1)
def copy(self, src, dest): # With large files this is 2x-3x faster than shutil.copy(src, dest), # especially when copying to a UNC target. # shutil.copyfileobj(...) with a proper buffer is better than # shutil.copy(...) but still 20% slower than a shell copy. # It can be replaced with Win32 API calls to avoid the process # spawning overhead. LOG.debug('Copying file from %s to %s', src, dest) output, ret = _utils.execute('cmd.exe', '/C', 'copy', '/Y', src, dest) if ret: raise IOError(_('The file copy from %(src)s to %(dest)s failed') % {'src': src, 'dest': dest})
def mount_smb_share(self, smbfs_share, username=None, password=None): try: LOG.debug('Mounting share: %s', smbfs_share) self._smb_conn.Msft_SmbMapping.Create(RemotePath=smbfs_share, UserName=username, Password=password) except wmi.x_wmi as exc: err_msg = (_( 'Unable to mount SMBFS share: %(smbfs_share)s ' 'WMI exception: %(wmi_exc)s'), {'smbfs_share': smbfs_share, 'wmi_exc': exc}) raise exceptions.HyperVException(err_msg)
def _wait_for_job(self, job_path): """Poll WMI job state and wait for completion.""" job_wmi_path = job_path.replace('\\', '/') job = wmi.WMI(moniker=job_wmi_path) while job.JobState == constants.WMI_JOB_STATE_RUNNING: time.sleep(0.1) job = wmi.WMI(moniker=job_wmi_path) if job.JobState != constants.WMI_JOB_STATE_COMPLETED: job_state = job.JobState if job.path().Class == "Msvm_ConcreteJob": err_sum_desc = job.ErrorSummaryDescription err_desc = job.ErrorDescription err_code = job.ErrorCode data = {'job_state': job_state, 'err_sum_desc': err_sum_desc, 'err_desc': err_desc, 'err_code': err_code} raise exceptions.HyperVException( _("WMI job failed with status %(job_state)d. " "Error details: %(err_sum_desc)s - %(err_desc)s - " "Error code: %(err_code)d") % data) else: (error, ret_val) = job.GetError() if not ret_val and error: data = {'job_state': job_state, 'error': error} raise exceptions.HyperVException( _("WMI job failed with status %(job_state)d. " "Error details: %(error)s") % data) else: raise exceptions.HyperVException( _("WMI job failed with status %d. No error " "description available") % job_state) desc = job.Description elap = job.ElapsedTime LOG.debug("WMI job succeeded: %(desc)s, Elapsed=%(elap)s", {'desc': desc, 'elap': elap}) return job
def get_internal_vhd_size_by_file_size(self, vhd_path, new_vhd_file_size): """Get internal size of a VHD according to new VHD file size. VHDX Size = Header (1MB) + Log + Metadata Region + BAT + Payload Blocks The chunk size is the maximum number of bytes described by a SB block. Chunk size = 2^{23} * LogicalSectorSize :param str vhd_path: VHD file path :param new_vhd_file_size: Size of the new VHD file. :return: Internal VHD size according to new VHD file size. """ vhd_format = self.get_vhd_format(vhd_path) if vhd_format == constants.DISK_FORMAT_VHD: return super(VHDUtilsV2, self).get_internal_vhd_size_by_file_size( vhd_path, new_vhd_file_size) else: vhd_info = self.get_vhd_info(vhd_path) vhd_type = vhd_info['Type'] if vhd_type == self._VHD_TYPE_DIFFERENCING: vhd_parent = self.get_vhd_parent_path(vhd_path) return self.get_internal_vhd_size_by_file_size(vhd_parent, new_vhd_file_size) else: try: with open(vhd_path, 'rb') as f: hs = VHDX_HEADER_SECTION_SIZE bes = VHDX_BAT_ENTRY_SIZE lss = vhd_info['LogicalSectorSize'] bs = self._get_vhdx_block_size(f) ls = self._get_vhdx_log_size(f) ms = self._get_vhdx_metadata_size_and_offset(f)[0] chunk_ratio = (1 << 23) * lss / bs size = new_vhd_file_size max_internal_size = (bs * chunk_ratio * (size - hs - ls - ms - bes - bes / chunk_ratio) / (bs * chunk_ratio + bes * chunk_ratio + bes)) return max_internal_size - (max_internal_size % bs) except IOError as ex: raise exceptions.HyperVException( _("Unable to obtain internal size from VHDX: " "%(vhd_path)s. Exception: %(ex)s") % {"vhd_path": vhd_path, "ex": ex})
def get_external_vswitch(self, vswitch_name): if vswitch_name: vswitches = self._conn.Msvm_VirtualSwitch(ElementName=vswitch_name) else: # Find the vswitch that is connected to the first physical nic. ext_port = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0] port = ext_port.associators(wmi_result_class='Msvm_SwitchPort')[0] vswitches = port.associators(wmi_result_class='Msvm_VirtualSwitch') if not len(vswitches): raise exceptions.HyperVException(_('vswitch "%s" not found') % vswitch_name) return vswitches[0].path_()
def _is_port_vm_started(self, port): vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = port.associators( wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA) # See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx (ret_val, summary_info) = vs_man_svc.GetSummaryInformation( [self._VM_SUMMARY_ENABLED_STATE], [v.path_() for v in vmsettings]) if ret_val or not summary_info: raise utils.HyperVException(msg=_('Cannot get VM summary data ' 'for: %s') % port.ElementName) return summary_info[0].EnabledState is self._HYPERV_VM_STATE_ENABLED
def create_vswitch_port(self, vswitch_path, port_name): switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] # Create a port on the vswitch. (new_port, ret_val) = switch_svc.CreateSwitchPort( Name=str(uuid.uuid4()), FriendlyName=port_name, ScopeOfResidence="", VirtualSwitch=vswitch_path) if ret_val != 0: raise exceptions.HyperVException( _("Failed to create vswitch port %(port_name)s on switch " "%(vswitch_path)s") % {'port_name': port_name, 'vswitch_path': vswitch_path}) return new_port
def get_vhd_format(self, path): with open(path, 'rb') as f: # Read header if f.read(8) == VHDX_SIGNATURE: return constants.DISK_FORMAT_VHDX # Read footer f.seek(0, 2) file_size = f.tell() if file_size >= 512: f.seek(-512, 2) if f.read(8) == VHD_SIGNATURE: return constants.DISK_FORMAT_VHD raise exceptions.HyperVException(_('Unsupported virtual disk format'))
def login_storage_target(self, target_lun, target_iqn, target_portal, auth_username=None, auth_password=None): """Ensure that the target is logged in.""" self._login_target_portal(target_portal) retry_count = CONF.hyperv.volume_attach_retry_count # If the target is not connected, at least two iterations are needed: # one for performing the login and another one for checking if the # target was logged in successfully. if retry_count < 2: retry_count = 2 for attempt in range(retry_count): target = self._conn_storage.query("SELECT * FROM MSFT_iSCSITarget " "WHERE NodeAddress='%s' " % target_iqn) if target and target[0].IsConnected: if attempt == 0: # The target was already connected but an update may be # required target[0].Update() return try: target = self._conn_storage.MSFT_iSCSITarget auth = {} if auth_username and auth_password: auth['AuthenticationType'] = self._CHAP_AUTH_TYPE auth['ChapUsername'] = auth_username auth['ChapSecret'] = auth_password target.Connect(NodeAddress=target_iqn, IsPersistent=True, **auth) time.sleep(CONF.hyperv.volume_attach_retry_interval) except wmi.x_wmi as exc: LOG.debug("Attempt %(attempt)d to connect to target " "%(target_iqn)s failed. Retrying. " "WMI exception: %(exc)s " % {'target_iqn': target_iqn, 'exc': exc, 'attempt': attempt}) raise exceptions.HyperVException(_('Failed to login target %s') % target_iqn)
def _get_instances_sub_dir(self, dir_name, remote_server=None, create_dir=True, remove_dir=False): instances_path = self.get_instances_dir(remote_server) path = os.path.join(instances_path, dir_name) try: if remove_dir: self._check_remove_dir(path) if create_dir: self._check_create_dir(path) return path except WindowsError as ex: if ex.winerror == ERROR_INVALID_NAME: raise exceptions.HyperVException(_( "Cannot access \"%(instances_path)s\", make sure the " "path exists and that you have the proper permissions. " "In particular Nova-Compute must not be executed with the " "builtin SYSTEM account or other accounts unable to " "authenticate on a remote host.") % {'instances_path': instances_path}) raise
def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name): port_alloc, found = self._get_switch_port_allocation(switch_port_name) if not found: raise utils.HyperVException( msg=_('Port Allocation not found: %s') % switch_port_name) vlan_settings = self._get_vlan_setting_data_from_port_alloc(port_alloc) if vlan_settings: if (vlan_settings.OperationMode == self._OPERATION_MODE_ACCESS and vlan_settings.AccessVlanId == vlan_id): # VLAN already set to corect value, no need to change it. return # Removing the feature because it cannot be modified # due to a wmi exception. self._jobutils.remove_virt_feature(vlan_settings) (vlan_settings, found) = self._get_vlan_setting_data(switch_port_name) vlan_settings.AccessVlanId = vlan_id vlan_settings.OperationMode = self._OPERATION_MODE_ACCESS self._jobutils.add_virt_feature(vlan_settings, port_alloc)
def unmount_smb_share(self, smbfs_share, force=False): mappings = self._smb_conn.Msft_SmbMapping(RemotePath=smbfs_share) if not mappings: LOG.debug('Share %s is not mounted. Skipping unmount.', smbfs_share) for mapping in mappings: # Due to a bug in the WMI module, getting the output of # methods returning None will raise an AttributeError try: mapping.Remove(Force=force) except AttributeError: pass except wmi.x_wmi: # If this fails, a 'Generic Failure' exception is raised. # This happens even if we unforcefully unmount an in-use # share, for which reason we'll simply ignore it in this # case. if force: raise exceptions.HyperVException( _("Could not unmount share: %s"), smbfs_share)
def get_vm_summary_info(self, vm_name): vm = self._lookup_vm_check(vm_name) vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = vm.associators( wmi_association_class=self._SETTINGS_DEFINE_STATE_CLASS, wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) settings_paths = [v.path_() for v in vmsettings] # See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx (ret_val, summary_info) = vs_man_svc.GetSummaryInformation( [constants.VM_SUMMARY_NUM_PROCS, constants.VM_SUMMARY_ENABLED_STATE, constants.VM_SUMMARY_MEMORY_USAGE, constants.VM_SUMMARY_UPTIME], settings_paths) if ret_val: raise exceptions.HyperVException( _('Cannot get VM summary data for: %s') % vm_name) si = summary_info[0] memory_usage = None if si.MemoryUsage is not None: memory_usage = long(si.MemoryUsage) up_time = None if si.UpTime is not None: up_time = long(si.UpTime) # Nova requires a valid state to be returned. Hyper-V has more # states than Nova, typically intermediate ones and since there is # no direct mapping for those, ENABLED is the only reasonable option # considering that in all the non mappable states the instance # is running. enabled_state = self._enabled_states_map.get(si.EnabledState, constants.HYPERV_VM_STATE_ENABLED) summary_info_dict = {'NumberOfProcessors': si.NumberOfProcessors, 'EnabledState': enabled_state, 'MemoryUsage': memory_usage, 'UpTime': up_time} return summary_info_dict
def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path): image_man_svc = self._conn.Msvm_ImageManagementService()[0] vhd_info_xml = self._get_vhd_info_xml(image_man_svc, child_vhd_path) et = ElementTree.fromstring(vhd_info_xml) item = et.find(".//PROPERTY[@NAME='ParentPath']/VALUE") if item is not None: item.text = parent_vhd_path else: msg = (_("Failed to reconnect image %(child_vhd_path)s to " "parent %(parent_vhd_path)s. The child image has no " "parent path property.") % {'child_vhd_path': child_vhd_path, 'parent_vhd_path': parent_vhd_path}) raise exceptions.HyperVException(msg) vhd_info_xml = ElementTree.tostring(et) (job_path, ret_val) = image_man_svc.SetVirtualHardDiskSettingData( VirtualDiskSettingData=vhd_info_xml) self._jobutils.check_ret_val(ret_val, job_path)