resync: "Adds RemoteFX support to the Hyper-V driver"
(cherry-picked from commit a39710244a8f26b0d9d80bffa41c04c84b133f17) Change-Id: I873053f7e16941ad7b272693ece8b69e49fc7c18
This commit is contained in:
@@ -69,15 +69,6 @@ IMAGE_PROP_VM_GEN_2 = "hyperv-gen2"
|
|||||||
VM_GEN_1 = 1
|
VM_GEN_1 = 1
|
||||||
VM_GEN_2 = 2
|
VM_GEN_2 = 2
|
||||||
|
|
||||||
REMOTEFX_MAX_RES_1024x768 = "1024x768"
|
|
||||||
REMOTEFX_MAX_RES_1280x1024 = "1280x1024"
|
|
||||||
REMOTEFX_MAX_RES_1600x1200 = "1600x1200"
|
|
||||||
REMOTEFX_MAX_RES_1920x1200 = "1920x1200"
|
|
||||||
REMOTEFX_MAX_RES_2560x1600 = "2560x1600"
|
|
||||||
REMOTEFX_MAX_RES_3840x2160 = "3840x2160"
|
|
||||||
|
|
||||||
|
|
||||||
FLAVOR_REMOTE_FX_EXTRA_SPEC_KEY = "hyperv:remotefx"
|
|
||||||
|
|
||||||
IMAGE_PROP_INTERACTIVE_SERIAL_PORT = "interactive_serial_port"
|
IMAGE_PROP_INTERACTIVE_SERIAL_PORT = "interactive_serial_port"
|
||||||
IMAGE_PROP_LOGGING_SERIAL_PORT = "logging_serial_port"
|
IMAGE_PROP_LOGGING_SERIAL_PORT = "logging_serial_port"
|
||||||
@@ -94,6 +85,10 @@ SERIAL_PORT_TYPES = {
|
|||||||
# logging and interactive sessions.
|
# logging and interactive sessions.
|
||||||
DEFAULT_SERIAL_CONSOLE_PORT = 1
|
DEFAULT_SERIAL_CONSOLE_PORT = 1
|
||||||
|
|
||||||
|
FLAVOR_ESPEC_REMOTEFX_RES = 'os:resolution'
|
||||||
|
FLAVOR_ESPEC_REMOTEFX_MONITORS = 'os:monitors'
|
||||||
|
FLAVOR_ESPEC_REMOTEFX_VRAM = 'os:vram'
|
||||||
|
|
||||||
SERIAL_CONSOLE_BUFFER_SIZE = 4 * units.Ki
|
SERIAL_CONSOLE_BUFFER_SIZE = 4 * units.Ki
|
||||||
MAX_CONSOLE_LOG_FILE_SIZE = units.Mi // 2
|
MAX_CONSOLE_LOG_FILE_SIZE = units.Mi // 2
|
||||||
|
|
||||||
|
|||||||
@@ -138,20 +138,20 @@ class HostOps(object):
|
|||||||
return objects.NUMATopology(cells=cells)
|
return objects.NUMATopology(cells=cells)
|
||||||
|
|
||||||
def _get_remotefx_gpu_info(self):
|
def _get_remotefx_gpu_info(self):
|
||||||
remotefx_total_video_ram = 0
|
total_video_ram = 0
|
||||||
remotefx_available_video_ram = 0
|
available_video_ram = 0
|
||||||
|
|
||||||
if CONF.hyperv.enable_remotefx:
|
if CONF.hyperv.enable_remotefx:
|
||||||
gpus = self._hostutils.get_remotefx_gpu_info()
|
gpus = self._hostutils.get_remotefx_gpu_info()
|
||||||
for gpu in gpus:
|
for gpu in gpus:
|
||||||
remotefx_total_video_ram += int(gpu['total_video_ram'])
|
total_video_ram += int(gpu['total_video_ram'])
|
||||||
remotefx_available_video_ram += int(gpu['available_video_ram'])
|
available_video_ram += int(gpu['available_video_ram'])
|
||||||
else:
|
else:
|
||||||
gpus = []
|
gpus = []
|
||||||
|
|
||||||
return {'remotefx_total_video_ram': remotefx_total_video_ram,
|
return {'total_video_ram': total_video_ram,
|
||||||
'remotefx_available_video_ram': remotefx_available_video_ram,
|
'used_video_ram': total_video_ram - available_video_ram,
|
||||||
'remotefx_gpu_info': jsonutils.dumps(gpus)}
|
'gpu_info': jsonutils.dumps(gpus)}
|
||||||
|
|
||||||
def get_available_resource(self):
|
def get_available_resource(self):
|
||||||
"""Retrieve resource info.
|
"""Retrieve resource info.
|
||||||
@@ -178,8 +178,6 @@ class HostOps(object):
|
|||||||
cpu_topology['cores'] *
|
cpu_topology['cores'] *
|
||||||
cpu_topology['threads'])
|
cpu_topology['threads'])
|
||||||
|
|
||||||
gpu_info = self._get_remotefx_gpu_info()
|
|
||||||
|
|
||||||
dic = {'vcpus': vcpus,
|
dic = {'vcpus': vcpus,
|
||||||
'memory_mb': total_mem_mb,
|
'memory_mb': total_mem_mb,
|
||||||
'memory_mb_used': used_mem_mb,
|
'memory_mb_used': used_mem_mb,
|
||||||
@@ -194,6 +192,8 @@ class HostOps(object):
|
|||||||
[(arch.I686, hv_type.HYPERV, vm_mode.HVM),
|
[(arch.I686, hv_type.HYPERV, vm_mode.HVM),
|
||||||
(arch.X86_64, hv_type.HYPERV, vm_mode.HVM)],
|
(arch.X86_64, hv_type.HYPERV, vm_mode.HVM)],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu_info = self._get_remotefx_gpu_info()
|
||||||
dic.update(gpu_info)
|
dic.update(gpu_info)
|
||||||
|
|
||||||
numa_topology = self._get_host_numa_topology()
|
numa_topology = self._get_host_numa_topology()
|
||||||
|
|||||||
@@ -393,11 +393,7 @@ class VMOps(object):
|
|||||||
CONF.hyperv.limit_cpu_features,
|
CONF.hyperv.limit_cpu_features,
|
||||||
dynamic_memory_ratio)
|
dynamic_memory_ratio)
|
||||||
|
|
||||||
flavor_extra_specs = instance.flavor.extra_specs
|
self._configure_remotefx(instance, vm_gen)
|
||||||
remote_fx_config = flavor_extra_specs.get(
|
|
||||||
constants.FLAVOR_REMOTE_FX_EXTRA_SPEC_KEY)
|
|
||||||
if remote_fx_config:
|
|
||||||
self._configure_remotefx(instance, vm_gen, remote_fx_config)
|
|
||||||
|
|
||||||
self._vmutils.create_scsi_controller(instance_name)
|
self._vmutils.create_scsi_controller(instance_name)
|
||||||
|
|
||||||
@@ -430,6 +426,45 @@ class VMOps(object):
|
|||||||
self._configure_secure_vm(context, instance, image_meta,
|
self._configure_secure_vm(context, instance, image_meta,
|
||||||
secure_boot_enabled)
|
secure_boot_enabled)
|
||||||
|
|
||||||
|
def _configure_remotefx(self, instance, vm_gen):
|
||||||
|
extra_specs = instance.flavor.extra_specs
|
||||||
|
remotefx_max_resolution = extra_specs.get(
|
||||||
|
constants.FLAVOR_ESPEC_REMOTEFX_RES)
|
||||||
|
if not remotefx_max_resolution:
|
||||||
|
# RemoteFX not required.
|
||||||
|
return
|
||||||
|
|
||||||
|
if not CONF.hyperv.enable_remotefx:
|
||||||
|
raise exception.InstanceUnacceptable(
|
||||||
|
_("enable_remotefx configuration option needs to be set to "
|
||||||
|
"True in order to use RemoteFX."))
|
||||||
|
|
||||||
|
if not self._hostutils.check_server_feature(
|
||||||
|
self._hostutils.FEATURE_RDS_VIRTUALIZATION):
|
||||||
|
raise exception.InstanceUnacceptable(
|
||||||
|
_("The RDS-Virtualization feature must be installed in order "
|
||||||
|
"to use RemoteFX."))
|
||||||
|
|
||||||
|
if not self._vmutils.vm_gen_supports_remotefx(vm_gen):
|
||||||
|
raise exception.InstanceUnacceptable(
|
||||||
|
_("RemoteFX is not supported on generation %s virtual "
|
||||||
|
"machines on this version of Windows.") % vm_gen)
|
||||||
|
|
||||||
|
instance_name = instance.name
|
||||||
|
LOG.debug('Configuring RemoteFX for instance: %s', instance_name)
|
||||||
|
|
||||||
|
remotefx_monitor_count = int(extra_specs.get(
|
||||||
|
constants.FLAVOR_ESPEC_REMOTEFX_MONITORS) or 1)
|
||||||
|
remotefx_vram = extra_specs.get(
|
||||||
|
constants.FLAVOR_ESPEC_REMOTEFX_VRAM)
|
||||||
|
vram_bytes = int(remotefx_vram) * units.Mi if remotefx_vram else None
|
||||||
|
|
||||||
|
self._vmutils.enable_remotefx_video_adapter(
|
||||||
|
instance_name,
|
||||||
|
remotefx_monitor_count,
|
||||||
|
remotefx_max_resolution,
|
||||||
|
vram_bytes)
|
||||||
|
|
||||||
def _attach_root_device(self, instance_name, root_dev_info):
|
def _attach_root_device(self, instance_name, root_dev_info):
|
||||||
if root_dev_info['type'] == constants.VOLUME:
|
if root_dev_info['type'] == constants.VOLUME:
|
||||||
self._volumeops.attach_volume(root_dev_info['connection_info'],
|
self._volumeops.attach_volume(root_dev_info['connection_info'],
|
||||||
@@ -570,40 +605,6 @@ class VMOps(object):
|
|||||||
|
|
||||||
return memory_per_numa_node, cpus_per_numa_node
|
return memory_per_numa_node, cpus_per_numa_node
|
||||||
|
|
||||||
def _configure_remotefx(self, instance, vm_gen, config):
|
|
||||||
if not CONF.hyperv.enable_remotefx:
|
|
||||||
reason = _("enable_remotefx configuration option needs to be set "
|
|
||||||
"to True in order to use RemoteFX")
|
|
||||||
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
|
||||||
reason=reason)
|
|
||||||
|
|
||||||
if not self._hostutils.check_server_feature(
|
|
||||||
self._hostutils.FEATURE_RDS_VIRTUALIZATION):
|
|
||||||
reason = _("The RDS-Virtualization feature must be installed in "
|
|
||||||
"order to use RemoteFX")
|
|
||||||
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
|
||||||
reason=reason)
|
|
||||||
|
|
||||||
if not self._vmutils.vm_gen_supports_remotefx(vm_gen):
|
|
||||||
reason = _("RemoteFX is not supported on generation %s virtual "
|
|
||||||
"machines on this version of Windows.") % vm_gen
|
|
||||||
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
|
|
||||||
reason=reason)
|
|
||||||
|
|
||||||
instance_name = instance.name
|
|
||||||
LOG.debug('Configuring RemoteFX for instance: %s', instance_name)
|
|
||||||
remotefx_args = config.split(',')
|
|
||||||
remotefx_max_resolution = remotefx_args[0]
|
|
||||||
remotefx_monitor_count = int(remotefx_args[1])
|
|
||||||
remotefx_vram = remotefx_args[2] if len(remotefx_args) == 3 else None
|
|
||||||
vram_bytes = int(remotefx_vram) * units.Mi if remotefx_vram else None
|
|
||||||
|
|
||||||
self._vmutils.enable_remotefx_video_adapter(
|
|
||||||
instance_name,
|
|
||||||
remotefx_monitor_count,
|
|
||||||
remotefx_max_resolution,
|
|
||||||
vram_bytes)
|
|
||||||
|
|
||||||
def attach_config_drive(self, instance, configdrive_path, vm_gen):
|
def attach_config_drive(self, instance, configdrive_path, vm_gen):
|
||||||
configdrive_ext = configdrive_path[(configdrive_path.rfind('.') + 1):]
|
configdrive_ext = configdrive_path[(configdrive_path.rfind('.') + 1):]
|
||||||
# Do the attach here and if there is a certain file format that isn't
|
# Do the attach here and if there is a certain file format that isn't
|
||||||
|
|||||||
@@ -135,6 +135,28 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_NUMATopology.assert_called_once_with(
|
mock_NUMATopology.assert_called_once_with(
|
||||||
cells=[mock_NUMACell.return_value])
|
cells=[mock_NUMACell.return_value])
|
||||||
|
|
||||||
|
def test_get_remotefx_gpu_info(self):
|
||||||
|
self.flags(enable_remotefx=True, group='hyperv')
|
||||||
|
fake_gpus = [{'total_video_ram': '2048',
|
||||||
|
'available_video_ram': '1024'},
|
||||||
|
{'total_video_ram': '1024',
|
||||||
|
'available_video_ram': '1024'}]
|
||||||
|
self._hostops._hostutils.get_remotefx_gpu_info.return_value = fake_gpus
|
||||||
|
|
||||||
|
ret_val = self._hostops._get_remotefx_gpu_info()
|
||||||
|
|
||||||
|
self.assertEqual(3072, ret_val['total_video_ram'])
|
||||||
|
self.assertEqual(1024, ret_val['used_video_ram'])
|
||||||
|
|
||||||
|
def test_get_remotefx_gpu_info_disabled(self):
|
||||||
|
self.flags(enable_remotefx=False, group='hyperv')
|
||||||
|
|
||||||
|
ret_val = self._hostops._get_remotefx_gpu_info()
|
||||||
|
|
||||||
|
self.assertEqual(0, ret_val['total_video_ram'])
|
||||||
|
self.assertEqual(0, ret_val['used_video_ram'])
|
||||||
|
self._hostops._hostutils.get_remotefx_gpu_info.assert_not_called()
|
||||||
|
|
||||||
@mock.patch.object(hostops.HostOps, '_get_host_numa_topology')
|
@mock.patch.object(hostops.HostOps, '_get_host_numa_topology')
|
||||||
@mock.patch.object(hostops.HostOps, '_get_remotefx_gpu_info')
|
@mock.patch.object(hostops.HostOps, '_get_remotefx_gpu_info')
|
||||||
@mock.patch.object(hostops.HostOps, '_get_cpu_info')
|
@mock.patch.object(hostops.HostOps, '_get_cpu_info')
|
||||||
@@ -185,7 +207,7 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
'numa_topology': mock.sentinel.numa_topology_json,
|
'numa_topology': mock.sentinel.numa_topology_json,
|
||||||
'remotefx_available_video_ram': 2048,
|
'remotefx_available_video_ram': 2048,
|
||||||
'remotefx_gpu_info': mock.sentinel.FAKE_GPU_INFO,
|
'remotefx_gpu_info': mock.sentinel.FAKE_GPU_INFO,
|
||||||
'remotefx_total_video_ram': 4096
|
'remotefx_total_video_ram': 4096,
|
||||||
}
|
}
|
||||||
self.assertEqual(expected, response)
|
self.assertEqual(expected, response)
|
||||||
|
|
||||||
@@ -227,18 +249,6 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected, response)
|
self.assertEqual(expected, response)
|
||||||
|
|
||||||
def test_get_remotefx_gpu_info(self):
|
|
||||||
self.flags(enable_remotefx=True, group='hyperv')
|
|
||||||
fake_gpus = [{'total_video_ram': '2048',
|
|
||||||
'available_video_ram': '1024'},
|
|
||||||
{'total_video_ram': '1024',
|
|
||||||
'available_video_ram': '1024'}]
|
|
||||||
self._hostops._hostutils.get_remotefx_gpu_info.return_value = fake_gpus
|
|
||||||
|
|
||||||
ret_val = self._hostops._get_remotefx_gpu_info()
|
|
||||||
self.assertEqual(3072, ret_val['remotefx_total_video_ram'])
|
|
||||||
self.assertEqual(2048, ret_val['remotefx_available_video_ram'])
|
|
||||||
|
|
||||||
@mock.patch.object(hostops.HostOps, '_wait_for_instance_pending_task')
|
@mock.patch.object(hostops.HostOps, '_wait_for_instance_pending_task')
|
||||||
@mock.patch.object(hostops.HostOps, '_set_service_state')
|
@mock.patch.object(hostops.HostOps, '_set_service_state')
|
||||||
@mock.patch.object(hostops.HostOps, '_migrate_vm')
|
@mock.patch.object(hostops.HostOps, '_migrate_vm')
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_configure_secure_vm,
|
mock_configure_secure_vm,
|
||||||
enable_instance_metrics,
|
enable_instance_metrics,
|
||||||
vm_gen=constants.VM_GEN_1, vnuma_enabled=False,
|
vm_gen=constants.VM_GEN_1, vnuma_enabled=False,
|
||||||
requires_sec_boot=True, remotefx=False):
|
requires_sec_boot=True):
|
||||||
mock_vif_driver = mock_get_vif_driver()
|
mock_vif_driver = mock_get_vif_driver()
|
||||||
self.flags(dynamic_memory_ratio=2.0, group='hyperv')
|
self.flags(dynamic_memory_ratio=2.0, group='hyperv')
|
||||||
self.flags(enable_instance_metrics_collection=enable_instance_metrics,
|
self.flags(enable_instance_metrics_collection=enable_instance_metrics,
|
||||||
@@ -582,77 +582,61 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
dynamic_memory_ratio = CONF.hyperv.dynamic_memory_ratio
|
dynamic_memory_ratio = CONF.hyperv.dynamic_memory_ratio
|
||||||
|
|
||||||
flavor = flavor_obj.Flavor(**test_flavor.fake_flavor)
|
flavor = flavor_obj.Flavor(**test_flavor.fake_flavor)
|
||||||
if remotefx is True:
|
|
||||||
flavor.extra_specs['hyperv:remotefx'] = "1920x1200,2"
|
|
||||||
mock_instance.flavor = flavor
|
mock_instance.flavor = flavor
|
||||||
|
|
||||||
if remotefx is True and vm_gen == constants.VM_GEN_2:
|
self._vmops.create_instance(
|
||||||
self.assertRaises(os_win_exc.HyperVException,
|
context=self.context,
|
||||||
self._vmops.create_instance,
|
instance=mock_instance,
|
||||||
context=self.context,
|
network_info=[fake_network_info],
|
||||||
instance=mock_instance,
|
block_device_info=block_device_info,
|
||||||
network_info=[fake_network_info],
|
root_device=root_device_info,
|
||||||
block_device_info=block_device_info,
|
vm_gen=vm_gen,
|
||||||
root_device=root_device_info,
|
image_meta=mock.sentinel.image_meta)
|
||||||
vm_gen=vm_gen,
|
|
||||||
image_meta=mock.sentinel.image_meta)
|
|
||||||
else:
|
|
||||||
self._vmops.create_instance(
|
|
||||||
context=self.context,
|
|
||||||
instance=mock_instance,
|
|
||||||
network_info=[fake_network_info],
|
|
||||||
block_device_info=block_device_info,
|
|
||||||
root_device=root_device_info,
|
|
||||||
vm_gen=vm_gen,
|
|
||||||
image_meta=mock.sentinel.image_meta)
|
|
||||||
if remotefx is True:
|
|
||||||
mock_configure_remotefx.assert_called_once_with(
|
|
||||||
mock_instance,
|
|
||||||
vm_gen,
|
|
||||||
flavor.extra_specs['hyperv:remotefx'])
|
|
||||||
|
|
||||||
self._vmops._vmutils.create_vm.assert_called_once_with(
|
mock_configure_remotefx.assert_called_once_with(mock_instance, vm_gen)
|
||||||
mock_instance.name, vnuma_enabled, vm_gen,
|
|
||||||
instance_path, [mock_instance.uuid])
|
|
||||||
self._vmops._vmutils.update_vm.assert_called_once_with(
|
|
||||||
mock_instance.name, mock_instance.memory_mb, mem_per_numa,
|
|
||||||
mock_instance.vcpus, cpus_per_numa,
|
|
||||||
CONF.hyperv.limit_cpu_features, dynamic_memory_ratio)
|
|
||||||
|
|
||||||
mock_create_scsi_ctrl = self._vmops._vmutils.create_scsi_controller
|
self._vmops._vmutils.create_vm.assert_called_once_with(
|
||||||
mock_create_scsi_ctrl.assert_called_once_with(mock_instance.name)
|
mock_instance.name, vnuma_enabled, vm_gen,
|
||||||
|
instance_path, [mock_instance.uuid])
|
||||||
|
self._vmops._vmutils.update_vm.assert_called_once_with(
|
||||||
|
mock_instance.name, mock_instance.memory_mb, mem_per_numa,
|
||||||
|
mock_instance.vcpus, cpus_per_numa,
|
||||||
|
CONF.hyperv.limit_cpu_features, dynamic_memory_ratio)
|
||||||
|
|
||||||
mock_attach_root_device.assert_called_once_with(mock_instance.name,
|
mock_create_scsi_ctrl = self._vmops._vmutils.create_scsi_controller
|
||||||
root_device_info)
|
mock_create_scsi_ctrl.assert_called_once_with(mock_instance.name)
|
||||||
mock_attach_ephemerals.assert_called_once_with(mock_instance.name,
|
|
||||||
block_device_info['ephemerals'])
|
|
||||||
mock_attach_volumes.assert_called_once_with(
|
|
||||||
block_device_info['block_device_mapping'], mock_instance.name)
|
|
||||||
|
|
||||||
mock_get_port_settings.assert_called_with(mock.sentinel.image_meta)
|
mock_attach_root_device.assert_called_once_with(mock_instance.name,
|
||||||
mock_create_pipes.assert_called_once_with(
|
root_device_info)
|
||||||
mock_instance, mock_get_port_settings.return_value)
|
mock_attach_ephemerals.assert_called_once_with(mock_instance.name,
|
||||||
|
block_device_info['ephemerals'])
|
||||||
|
mock_attach_volumes.assert_called_once_with(
|
||||||
|
block_device_info['block_device_mapping'], mock_instance.name)
|
||||||
|
|
||||||
self._vmops._vmutils.create_nic.assert_called_once_with(
|
mock_get_port_settings.assert_called_with(mock.sentinel.image_meta)
|
||||||
mock_instance.name, mock.sentinel.ID, mock.sentinel.ADDRESS)
|
mock_create_pipes.assert_called_once_with(
|
||||||
mock_vif_driver.plug.assert_called_once_with(mock_instance,
|
mock_instance, mock_get_port_settings.return_value)
|
||||||
fake_network_info)
|
|
||||||
mock_enable = (
|
self._vmops._vmutils.create_nic.assert_called_once_with(
|
||||||
self._vmops._metricsutils.enable_vm_metrics_collection)
|
mock_instance.name, mock.sentinel.ID, mock.sentinel.ADDRESS)
|
||||||
if enable_instance_metrics:
|
mock_vif_driver.plug.assert_called_once_with(mock_instance,
|
||||||
mock_enable.assert_called_once_with(mock_instance.name)
|
fake_network_info)
|
||||||
mock_set_qos_specs.assert_called_once_with(mock_instance)
|
mock_enable = (
|
||||||
if requires_sec_boot:
|
self._vmops._metricsutils.enable_vm_metrics_collection)
|
||||||
mock_requires_secure_boot.assert_called_once_with(
|
if enable_instance_metrics:
|
||||||
mock_instance, mock.sentinel.image_meta, vm_gen)
|
mock_enable.assert_called_once_with(mock_instance.name)
|
||||||
mock_requires_certificate.assert_called_once_with(
|
mock_set_qos_specs.assert_called_once_with(mock_instance)
|
||||||
mock_instance.uuid,
|
if requires_sec_boot:
|
||||||
mock.sentinel.image_meta)
|
mock_requires_secure_boot.assert_called_once_with(
|
||||||
enable_secure_boot = self._vmops._vmutils.enable_secure_boot
|
mock_instance, mock.sentinel.image_meta, vm_gen)
|
||||||
enable_secure_boot.assert_called_once_with(
|
mock_requires_certificate.assert_called_once_with(
|
||||||
mock_instance.name, mock_requires_certificate.return_value)
|
mock_instance.uuid,
|
||||||
mock_configure_secure_vm.assert_called_once_with(self.context,
|
mock.sentinel.image_meta)
|
||||||
mock_instance, mock.sentinel.image_meta, requires_sec_boot)
|
enable_secure_boot = self._vmops._vmutils.enable_secure_boot
|
||||||
|
enable_secure_boot.assert_called_once_with(
|
||||||
|
mock_instance.name, mock_requires_certificate.return_value)
|
||||||
|
mock_configure_secure_vm.assert_called_once_with(self.context,
|
||||||
|
mock_instance, mock.sentinel.image_meta, requires_sec_boot)
|
||||||
|
|
||||||
def test_create_instance(self):
|
def test_create_instance(self):
|
||||||
self._test_create_instance(enable_instance_metrics=True)
|
self._test_create_instance(enable_instance_metrics=True)
|
||||||
@@ -670,14 +654,6 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._test_create_instance(enable_instance_metrics=False,
|
self._test_create_instance(enable_instance_metrics=False,
|
||||||
vm_gen=constants.VM_GEN_2)
|
vm_gen=constants.VM_GEN_2)
|
||||||
|
|
||||||
def test_create_instance_with_remote_fx(self):
|
|
||||||
self._test_create_instance(enable_instance_metrics=False,
|
|
||||||
remotefx=True)
|
|
||||||
|
|
||||||
def test_create_instance_with_remote_fx_gen2(self):
|
|
||||||
self._test_create_instance(enable_instance_metrics=False,
|
|
||||||
remotefx=True)
|
|
||||||
|
|
||||||
@mock.patch.object(vmops.volumeops.VolumeOps, 'attach_volume')
|
@mock.patch.object(vmops.volumeops.VolumeOps, 'attach_volume')
|
||||||
def test_attach_root_device_volume(self, mock_attach_volume):
|
def test_attach_root_device_volume(self, mock_attach_volume):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
@@ -1292,6 +1268,64 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.call(mock.sentinel.FAKE_DVD_PATH2,
|
mock.call(mock.sentinel.FAKE_DVD_PATH2,
|
||||||
mock.sentinel.FAKE_DEST_PATH))
|
mock.sentinel.FAKE_DEST_PATH))
|
||||||
|
|
||||||
|
def _setup_remotefx_mocks(self):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_instance.flavor.extra_specs = {
|
||||||
|
'os:resolution': os_win_const.REMOTEFX_MAX_RES_1920x1200,
|
||||||
|
'os:monitors': '2',
|
||||||
|
'os:vram': '256'}
|
||||||
|
|
||||||
|
return mock_instance
|
||||||
|
|
||||||
|
def test_configure_remotefx_not_required(self):
|
||||||
|
self.flags(enable_remotefx=False, group='hyperv')
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
|
||||||
|
self._vmops._configure_remotefx(mock_instance, mock.sentinel.VM_GEN)
|
||||||
|
|
||||||
|
def test_configure_remotefx_exception_enable_config(self):
|
||||||
|
self.flags(enable_remotefx=False, group='hyperv')
|
||||||
|
mock_instance = self._setup_remotefx_mocks()
|
||||||
|
|
||||||
|
self.assertRaises(exception.InstanceUnacceptable,
|
||||||
|
self._vmops._configure_remotefx,
|
||||||
|
mock_instance, mock.sentinel.VM_GEN)
|
||||||
|
|
||||||
|
def test_configure_remotefx_exception_server_feature(self):
|
||||||
|
self.flags(enable_remotefx=True, group='hyperv')
|
||||||
|
mock_instance = self._setup_remotefx_mocks()
|
||||||
|
self._vmops._hostutils.check_server_feature.return_value = False
|
||||||
|
|
||||||
|
self.assertRaises(exception.InstanceUnacceptable,
|
||||||
|
self._vmops._configure_remotefx,
|
||||||
|
mock_instance, mock.sentinel.VM_GEN)
|
||||||
|
|
||||||
|
def test_configure_remotefx_exception_vm_gen(self):
|
||||||
|
self.flags(enable_remotefx=True, group='hyperv')
|
||||||
|
mock_instance = self._setup_remotefx_mocks()
|
||||||
|
self._vmops._hostutils.check_server_feature.return_value = True
|
||||||
|
self._vmops._vmutils.vm_gen_supports_remotefx.return_value = False
|
||||||
|
|
||||||
|
self.assertRaises(exception.InstanceUnacceptable,
|
||||||
|
self._vmops._configure_remotefx,
|
||||||
|
mock_instance, mock.sentinel.VM_GEN)
|
||||||
|
|
||||||
|
def test_configure_remotefx(self):
|
||||||
|
self.flags(enable_remotefx=True, group='hyperv')
|
||||||
|
mock_instance = self._setup_remotefx_mocks()
|
||||||
|
self._vmops._hostutils.check_server_feature.return_value = True
|
||||||
|
self._vmops._vmutils.vm_gen_supports_remotefx.return_value = True
|
||||||
|
extra_specs = mock_instance.flavor.extra_specs
|
||||||
|
|
||||||
|
self._vmops._configure_remotefx(mock_instance,
|
||||||
|
constants.VM_GEN_1)
|
||||||
|
mock_enable_remotefx = (
|
||||||
|
self._vmops._vmutils.enable_remotefx_video_adapter)
|
||||||
|
mock_enable_remotefx.assert_called_once_with(
|
||||||
|
mock_instance.name, int(extra_specs['os:monitors']),
|
||||||
|
extra_specs['os:resolution'],
|
||||||
|
int(extra_specs['os:vram']) * units.Mi)
|
||||||
|
|
||||||
@mock.patch('nova.virt.configdrive.required_by')
|
@mock.patch('nova.virt.configdrive.required_by')
|
||||||
@mock.patch.object(vmops.VMOps, '_create_root_vhd')
|
@mock.patch.object(vmops.VMOps, '_create_root_vhd')
|
||||||
@mock.patch.object(vmops.VMOps, 'get_image_vm_generation')
|
@mock.patch.object(vmops.VMOps, 'get_image_vm_generation')
|
||||||
@@ -1506,46 +1540,6 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
def test_get_instance_vnuma_config_no_topology(self):
|
def test_get_instance_vnuma_config_no_topology(self):
|
||||||
self._check_get_instance_vnuma_config()
|
self._check_get_instance_vnuma_config()
|
||||||
|
|
||||||
def _test_configure_remotefx(self, fail=True, enable_remotefx=True):
|
|
||||||
self.flags(enable_remotefx=enable_remotefx, group='hyperv')
|
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
|
||||||
|
|
||||||
fake_resolution = "1920x1200"
|
|
||||||
fake_monitor_count = 3
|
|
||||||
fake_vram_mb = 64
|
|
||||||
fake_config = "%s,%s,%s" % (
|
|
||||||
fake_resolution, fake_monitor_count, fake_vram_mb)
|
|
||||||
|
|
||||||
enable_remotefx = self._vmops._vmutils.enable_remotefx_video_adapter
|
|
||||||
|
|
||||||
if fail:
|
|
||||||
self.assertRaises(exception.InstanceUnacceptable,
|
|
||||||
self._vmops._configure_remotefx,
|
|
||||||
mock_instance, mock.sentinel.vm_gen, fake_config)
|
|
||||||
else:
|
|
||||||
self._vmops._configure_remotefx(
|
|
||||||
mock_instance, mock.sentinel.vm_gen, fake_config)
|
|
||||||
enable_remotefx.assert_called_once_with(mock_instance.name,
|
|
||||||
fake_monitor_count,
|
|
||||||
fake_resolution,
|
|
||||||
fake_vram_mb * units.Mi)
|
|
||||||
|
|
||||||
def test_configure_remotefx_disabled(self):
|
|
||||||
self._test_configure_remotefx(enable_remotefx=False)
|
|
||||||
|
|
||||||
def test_configure_remotefx_no_server_feature(self):
|
|
||||||
self._vmops._hostutils.check_server_feature.return_value = False
|
|
||||||
self._test_configure_remotefx()
|
|
||||||
self._vmops._hostutils.check_server_feature.assert_called_once_with(
|
|
||||||
self._vmops._hostutils.FEATURE_RDS_VIRTUALIZATION)
|
|
||||||
|
|
||||||
def test_configure_remotefx_unsupported_vm_gen(self):
|
|
||||||
self._vmops._vmutils.vm_gen_supports_remotefx.return_value = False
|
|
||||||
self._test_configure_remotefx()
|
|
||||||
|
|
||||||
def test_configure_remotefx(self):
|
|
||||||
self._test_configure_remotefx(fail=False)
|
|
||||||
|
|
||||||
@mock.patch.object(vmops.VMOps, '_get_vif_driver')
|
@mock.patch.object(vmops.VMOps, '_get_vif_driver')
|
||||||
def test_unplug_vifs(self, mock_get_vif_driver):
|
def test_unplug_vifs(self, mock_get_vif_driver):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
|||||||
Reference in New Issue
Block a user