
Supports iso9660 ConfigDriveV2 drives, which can be attached to the instance on boot as VHD or ISO. Admin password injection can be selectively enabled for security concerns. Updates also the hyperv tests. Change-Id: I22d1ab57bd2f75ddf8758b2ea1a958d7bbe35731
253 lines
8.9 KiB
Python
253 lines
8.9 KiB
Python
# 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 volumeutils
|
|
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._volumeutils = volumeutils.VolumeUtils()
|
|
|
|
@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
|
|
|
|
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)
|
|
self._volumeutils.logout_storage_target(self._conn_wmi, 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
|