os-win/os_win/utils/compute/vmutils10.py

291 lines
11 KiB
Python

# Copyright 2015 Cloudbase Solutions Srl
# All Rights Reserved.
#
# 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.
import re
from oslo_log import log as logging
import six
from os_win._i18n import _
from os_win import constants
from os_win import exceptions
from os_win.utils import _wqlutils
from os_win.utils.compute import vmutils
from oslo_utils import units
LOG = logging.getLogger(__name__)
class VMUtils10(vmutils.VMUtils):
_UEFI_CERTIFICATE_AUTH = 'MicrosoftUEFICertificateAuthority'
_SERIAL_PORT_SETTING_DATA_CLASS = "Msvm_SerialPortSettingData"
_SECURITY_SETTING_DATA = 'Msvm_SecuritySettingData'
_PCI_EXPRESS_SETTING_DATA = 'Msvm_PciExpressSettingData'
_MSPS_NAMESPACE = '//%s/root/msps'
_remote_fx_res_map = {
constants.REMOTEFX_MAX_RES_1024x768: 0,
constants.REMOTEFX_MAX_RES_1280x1024: 1,
constants.REMOTEFX_MAX_RES_1600x1200: 2,
constants.REMOTEFX_MAX_RES_1920x1200: 3,
constants.REMOTEFX_MAX_RES_2560x1600: 4,
constants.REMOTEFX_MAX_RES_3840x2160: 5
}
_remotefx_max_monitors_map = {
# defines the maximum number of monitors for a given
# resolution
constants.REMOTEFX_MAX_RES_1024x768: 8,
constants.REMOTEFX_MAX_RES_1280x1024: 8,
constants.REMOTEFX_MAX_RES_1600x1200: 4,
constants.REMOTEFX_MAX_RES_1920x1200: 4,
constants.REMOTEFX_MAX_RES_2560x1600: 2,
constants.REMOTEFX_MAX_RES_3840x2160: 1
}
_remotefx_vram_vals = [64 * units.Mi, 128 * units.Mi, 256 * units.Mi,
512 * units.Mi, 1024 * units.Mi]
def __init__(self, host='.'):
super(VMUtils10, self).__init__(host)
self._conn_msps_attr = None
self._sec_svc_attr = None
@property
def _conn_msps(self):
if not self._conn_msps_attr:
try:
namespace = self._MSPS_NAMESPACE % self._host
self._conn_msps_attr = self._get_wmi_conn(namespace)
except Exception:
raise exceptions.OSWinException(
_("Namespace %(namespace)s not found. Make sure "
"FabricShieldedTools feature is installed.") %
{'namespace': namespace})
return self._conn_msps_attr
@property
def _sec_svc(self):
if not self._sec_svc_attr:
self._sec_svc_attr = self._conn.Msvm_SecurityService()[0]
return self._sec_svc_attr
def set_nested_virtualization(self, vm_name, state):
"""Enables nested virtualization for the given VM.
:param vm_name: the name of the VM.
:param state: boolean, if True, nested virtualization will be enabled,
disabled otherwise.
"""
vmsettings = self._lookup_vm_check(vm_name)
procsettings = _wqlutils.get_element_associated_class(
self._conn, self._PROCESSOR_SETTING_DATA_CLASS,
element_instance_id=vmsettings.InstanceID)[0]
procsettings.ExposeVirtualizationExtensions = state
self._jobutils.modify_virt_resource(procsettings)
def vm_gen_supports_remotefx(self, vm_gen):
"""RemoteFX is supported on both generation 1 and 2 virtual
machines for Windows 10 / Windows Server 2016.
:returns: True
"""
return True
def _validate_remotefx_params(self, monitor_count, max_resolution,
vram_bytes=None):
super(VMUtils10, self)._validate_remotefx_params(monitor_count,
max_resolution)
if vram_bytes and vram_bytes not in self._remotefx_vram_vals:
raise exceptions.HyperVRemoteFXException(
_("Unsuported RemoteFX VRAM value: %(requested_value)s."
"The supported VRAM values are: %(supported_values)s") %
{'requested_value': vram_bytes,
'supported_values': self._remotefx_vram_vals})
def _set_remotefx_vram(self, remotefx_disp_ctrl_res, vram_bytes):
if vram_bytes:
remotefx_disp_ctrl_res.VRAMSizeBytes = six.text_type(vram_bytes)
def _vm_has_s3_controller(self, vm_name):
return self.get_vm_generation(vm_name) == constants.VM_GEN_1
def _set_secure_boot(self, vs_data, msft_ca_required):
vs_data.SecureBootEnabled = True
if msft_ca_required:
uefi_data = self._conn.Msvm_VirtualSystemSettingData(
ElementName=self._UEFI_CERTIFICATE_AUTH)[0]
vs_data.SecureBootTemplateId = uefi_data.SecureBootTemplateId
def populate_fsk(self, fsk_filepath, fsk_pairs):
"""Writes in the fsk file all the substitution strings and their
values which will populate the unattended file used when
creating the pdk.
"""
fabric_data_pairs = []
for fsk_key, fsk_value in fsk_pairs.items():
fabricdata = self._conn_msps.Msps_FabricData.new()
fabricdata.key = fsk_key
fabricdata.Value = fsk_value
fabric_data_pairs.append(fabricdata)
fsk = self._conn_msps.Msps_FSK.new()
fsk.FabricDataPairs = fabric_data_pairs
msps_pfp = self._conn_msps.Msps_ProvisioningFileProcessor
msps_pfp.SerializeToFile(fsk_filepath, fsk)
def add_vtpm(self, vm_name, pdk_filepath, shielded):
"""Adds a vtpm and enables it with encryption or shielded option."""
vm = self._lookup_vm_check(vm_name)
msps_pfp = self._conn_msps.Msps_ProvisioningFileProcessor
provisioning_file = msps_pfp.PopulateFromFile(pdk_filepath)[0]
# key_protector: array of bytes
key_protector = provisioning_file.KeyProtector
# policy_data: array of bytes
policy_data = provisioning_file.PolicyData
security_profile = _wqlutils.get_element_associated_class(
self._conn, self._SECURITY_SETTING_DATA,
element_uuid=vm.ConfigurationID)[0]
security_profile.EncryptStateAndVmMigrationTraffic = True
security_profile.TpmEnabled = True
security_profile.ShieldingRequested = shielded
sec_profile_serialized = security_profile.GetText_(1)
(job_path, ret_val) = self._sec_svc.SetKeyProtector(
key_protector, sec_profile_serialized)
self._jobutils.check_ret_val(ret_val, job_path)
(job_path, ret_val) = self._sec_svc.SetSecurityPolicy(
policy_data, sec_profile_serialized)
self._jobutils.check_ret_val(ret_val, job_path)
(job_path, ret_val) = self._sec_svc.ModifySecuritySettings(
sec_profile_serialized)
self._jobutils.check_ret_val(ret_val, job_path)
def provision_vm(self, vm_name, fsk_filepath, pdk_filepath):
vm = self._lookup_vm_check(vm_name)
provisioning_service = self._conn_msps.Msps_ProvisioningService
(job_path, ret_val) = provisioning_service.ProvisionMachine(
fsk_filepath, vm.ConfigurationID, pdk_filepath)
self._jobutils.check_ret_val(ret_val, job_path)
def is_secure_vm(self, instance_name):
inst_id = self.get_vm_id(instance_name)
security_profile = _wqlutils.get_element_associated_class(
self._conn, self._SECURITY_SETTING_DATA,
element_uuid=inst_id)
if security_profile:
return security_profile[0].EncryptStateAndVmMigrationTraffic
return False
def add_pci_device(self, vm_name, vendor_id, product_id):
"""Adds the given PCI device to the given VM.
:param vm_name: the name of the VM to which the PCI device will be
attached to.
:param vendor_id: the PCI device's vendor ID.
:param product_id: the PCI device's product ID.
:raises exceptions.PciDeviceNotFound: if there is no PCI device
identifiable by the given vendor_id and product_id, or it was
already assigned.
"""
vmsettings = self._lookup_vm_check(vm_name)
pci_setting_data = self._get_new_setting_data(
self._PCI_EXPRESS_SETTING_DATA)
pci_device = self._get_assignable_pci_device(vendor_id, product_id)
pci_setting_data.HostResource = [pci_device.path_()]
self._jobutils.add_virt_resource(pci_setting_data, vmsettings)
def _get_assignable_pci_device(self, vendor_id, product_id):
pci_devices = self._conn.Msvm_PciExpress()
pattern = re.compile(
"^(.*)VEN_%(vendor_id)s&DEV_%(product_id)s&(.*)$" % {
'vendor_id': vendor_id, 'product_id': product_id})
for dev in pci_devices:
if pattern.match(dev.DeviceID):
# NOTE(claudiub): if the given PCI device is already assigned,
# the pci_devices list will contain PCI device with the same
# LocationPath.
pci_devices_found = [d for d in pci_devices if
d.LocationPath == dev.LocationPath]
LOG.debug('PCI devices found: %s',
[d.DeviceID for d in pci_devices_found])
# device is not in use by other VM
if len(pci_devices_found) == 1:
return pci_devices_found[0]
raise exceptions.PciDeviceNotFound(vendor_id=vendor_id,
product_id=product_id)
def remove_pci_device(self, vm_name, vendor_id, product_id):
"""Removes the given PCI device from the given VM.
:param vm_name: the name of the VM from which the PCI device will be
attached from.
:param vendor_id: the PCI device's vendor ID.
:param product_id: the PCI device's product ID.
"""
vmsettings = self._lookup_vm_check(vm_name)
pattern = re.compile(
"^(.*)VEN_%(vendor_id)s&DEV_%(product_id)s&(.*)$" % {
'vendor_id': vendor_id, 'product_id': product_id})
pci_sds = _wqlutils.get_element_associated_class(
self._conn, self._PCI_EXPRESS_SETTING_DATA,
vmsettings.InstanceID)
pci_sds = [sd for sd in pci_sds if pattern.match(sd.HostResource[0])]
if pci_sds:
self._jobutils.remove_virt_resource(pci_sds[0])
else:
LOG.debug("PCI device with vendor ID %(vendor_id)s and "
"%(product_id)s is not attached to %(vm_name)s",
{'vendor_id': vendor_id, 'product_id': product_id,
'vm_name': vm_name})
def remove_all_pci_devices(self, vm_name):
"""Removes all the PCI devices from the given VM.
:param vm_name: the name of the VM from which all the PCI devices will
be detached from.
"""
vmsettings = self._lookup_vm_check(vm_name)
pci_sds = _wqlutils.get_element_associated_class(
self._conn, self._PCI_EXPRESS_SETTING_DATA,
vmsettings.InstanceID)
if pci_sds:
self._jobutils.remove_multiple_virt_resources(pci_sds)