# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Hyper-V classes to be used in testing. """ import sys import time from nova import exception from nova.virt.hyperv import constants from nova.virt.hyperv import volumeutilsV2 from xml.etree import ElementTree # Check needed for unit testing on Unix if sys.platform == 'win32': import wmi class HyperVUtils(object): def __init__(self): self.__conn = None self.__conn_v2 = None self.__conn_cimv2 = None self.__conn_wmi = None self.__conn_storage = None self._volumeutils = volumeutilsV2.VolumeUtilsV2( self._conn_storage, self._conn_wmi) @property def _conn(self): if self.__conn is None: self.__conn = wmi.WMI(moniker='//./root/virtualization') return self.__conn @property def _conn_v2(self): if self.__conn_v2 is None: self.__conn_v2 = wmi.WMI(moniker='//./root/virtualization/v2') return self.__conn_v2 @property def _conn_cimv2(self): if self.__conn_cimv2 is None: self.__conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') return self.__conn_cimv2 @property def _conn_wmi(self): if self.__conn_wmi is None: self.__conn_wmi = wmi.WMI(moniker='//./root/wmi') return self.__conn_wmi @property def _conn_storage(self): if self.__conn_storage is None: storage_namespace = '//./Root/Microsoft/Windows/Storage' self.__conn_storage = wmi.WMI(moniker=storage_namespace) return self.__conn_storage def create_vhd(self, path): image_service = self._conn.query( "Select * from Msvm_ImageManagementService")[0] (job, ret_val) = image_service.CreateDynamicVirtualHardDisk( Path=path, MaxInternalSize=3 * 1024 * 1024) if ret_val == constants.WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) else: success = (ret_val == 0) if not success: raise Exception('Failed to create Dynamic disk %s with error %d' % (path, ret_val)) def _check_job_status(self, jobpath): """Poll WMI job state for completion.""" job_wmi_path = jobpath.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) return job.JobState == constants.WMI_JOB_STATE_COMPLETED def _get_vm(self, vm_name, conn=None): if conn is None: conn = self._conn vml = conn.Msvm_ComputerSystem(ElementName=vm_name) if not len(vml): raise exception.InstanceNotFound(instance=vm_name) return vml[0] def remote_vm_exists(self, server, vm_name): conn = wmi.WMI(moniker='//' + server + '/root/virtualization') return self._vm_exists(conn, vm_name) def vm_exists(self, vm_name): return self._vm_exists(self._conn, vm_name) def _vm_exists(self, conn, vm_name): return len(conn.Msvm_ComputerSystem(ElementName=vm_name)) > 0 def _get_vm_summary(self, vm_name): vm = self._get_vm(vm_name) vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = vm.associators( wmi_association_class='Msvm_SettingsDefineState', wmi_result_class='Msvm_VirtualSystemSettingData') settings_paths = [v.path_() for v in vmsettings] return vs_man_svc.GetSummaryInformation([100, 105], settings_paths)[1][0] def get_vm_uptime(self, vm_name): return self._get_vm_summary(vm_name).UpTime def get_vm_state(self, vm_name): return self._get_vm_summary(vm_name).EnabledState def set_vm_state(self, vm_name, req_state): self._set_vm_state(self._conn, vm_name, req_state) def _set_vm_state(self, conn, vm_name, req_state): vm = self._get_vm(vm_name, conn) (job, ret_val) = vm.RequestStateChange(req_state) success = False if ret_val == constants.WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) elif ret_val == 0: success = True elif ret_val == 32775: #Invalid state for current operation. Typically means it is #already in the state requested success = True if not success: raise Exception(_("Failed to change vm state of %(vm_name)s" " to %(req_state)s") % locals()) def get_vm_disks(self, vm_name): return self._get_vm_disks(self._conn, vm_name) def _get_vm_disks(self, conn, vm_name): vm = self._get_vm(vm_name, conn) vmsettings = vm.associators( wmi_result_class='Msvm_VirtualSystemSettingData') rasds = vmsettings[0].associators( wmi_result_class='MSVM_ResourceAllocationSettingData') disks = [r for r in rasds if r.ResourceSubType == 'Microsoft Virtual Hard Disk'] disk_files = [] for disk in disks: disk_files.extend([c for c in disk.Connection]) volumes = [r for r in rasds if r.ResourceSubType == 'Microsoft Physical Disk Drive'] volume_drives = [] for volume in volumes: hostResources = volume.HostResource drive_path = hostResources[0] volume_drives.append(drive_path) dvds = [r for r in rasds if r.ResourceSubType == 'Microsoft Virtual CD/DVD Disk'] dvd_files = [] for dvd in dvds: dvd_files.extend([c for c in dvd.Connection]) return (disk_files, volume_drives, dvd_files) def remove_remote_vm(self, server, vm_name): conn = wmi.WMI(moniker='//' + server + '/root/virtualization') conn_cimv2 = wmi.WMI(moniker='//' + server + '/root/cimv2') self._remove_vm(vm_name, conn, conn_cimv2) def remove_vm(self, vm_name): self._remove_vm(vm_name, self._conn, self._conn_cimv2) def _remove_vm(self, vm_name, conn, conn_cimv2): vm = self._get_vm(vm_name, conn) vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0] #Stop the VM first. self._set_vm_state(conn, vm_name, 3) (disk_files, volume_drives, dvd_files) = self._get_vm_disks(conn, vm_name) (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) if ret_val == constants.WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) elif ret_val == 0: success = True if not success: raise Exception(_('Failed to destroy vm %s') % vm_name) #Delete associated vhd disk files. for disk in disk_files + dvd_files: vhd_file = conn_cimv2.query( "Select * from CIM_DataFile where Name = '" + disk.replace("'", "''") + "'")[0] vhd_file.Delete() def _get_target_iqn(self, volume_id): return 'iqn.2010-10.org.openstack:volume-' + volume_id def logout_iscsi_volume_sessions(self, volume_id): target_iqn = self._get_target_iqn(volume_id) if (self.iscsi_volume_sessions_exist(volume_id)): self._volumeutils.logout_storage_target(target_iqn) def iscsi_volume_sessions_exist(self, volume_id): target_iqn = self._get_target_iqn(volume_id) return len(self._conn_wmi.query( "SELECT * FROM MSiSCSIInitiator_SessionClass \ WHERE TargetName='" + target_iqn + "'")) > 0 def get_vm_count(self): return len(self._conn.query( "Select * from Msvm_ComputerSystem where Description " "<> 'Microsoft Hosting Computer System'")) def get_vm_snapshots_count(self, vm_name): return len(self._conn.query( "Select * from Msvm_VirtualSystemSettingData where \ SettingType = 5 and SystemName = '" + vm_name + "'")) def get_vhd_parent_path(self, vhd_path): image_man_svc = self._conn.Msvm_ImageManagementService()[0] (vhd_info, job_path, ret_val) = \ image_man_svc.GetVirtualHardDiskInfo(vhd_path) if ret_val == constants.WMI_JOB_STATUS_STARTED: success = self._check_job_status(job_path) else: success = (ret_val == 0) if not success: raise Exception(_("Failed to get info for disk %s") % (vhd_path)) base_disk_path = None et = ElementTree.fromstring(vhd_info) for item in et.findall("PROPERTY"): if item.attrib["NAME"] == "ParentPath": base_disk_path = item.find("VALUE").text break return base_disk_path