From ac07fcd78b39b9aa1bda1734c774d2a6dc8e2691 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Wed, 15 Feb 2017 19:01:41 +0200 Subject: [PATCH] vmutils: Allows updating and disabling instance RemoteFX When resizing an instance, if it was imported, its resources needs to be updated according to the new flavor. If a flavor has different RemoteFX requirements, os_win should allow updates to the instance's RemoteFX configuration. Change-Id: Ib83bbc5c7fd07b55888f58d3407e1dd1a50e9b7e Partial-Bug: #1663238 --- .../tests/unit/utils/compute/test_vmutils.py | 98 ++++++++++++++++--- .../unit/utils/compute/test_vmutils10.py | 13 +++ os_win/utils/compute/vmutils.py | 87 ++++++++++++---- os_win/utils/compute/vmutils10.py | 17 +--- 4 files changed, 166 insertions(+), 49 deletions(-) diff --git a/os_win/tests/unit/utils/compute/test_vmutils.py b/os_win/tests/unit/utils/compute/test_vmutils.py index cb643440..9738c0af 100644 --- a/os_win/tests/unit/utils/compute/test_vmutils.py +++ b/os_win/tests/unit/utils/compute/test_vmutils.py @@ -1209,12 +1209,51 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): self._vmutils._validate_remotefx_params, 1, '1024x700') + @ddt.data(True, False) + @mock.patch.object(vmutils.VMUtils, '_set_remotefx_vram') + @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') + def test_set_remotefx_display_controller(self, new_obj, mock_get_new_rsd, + mock_set_remotefx_vram): + if new_obj: + remotefx_ctrl_res = None + expected_res = mock_get_new_rsd.return_value + else: + remotefx_ctrl_res = mock.MagicMock() + expected_res = remotefx_ctrl_res + + self._vmutils._set_remotefx_display_controller( + mock.sentinel.fake_vm, remotefx_ctrl_res, + mock.sentinel.monitor_count, mock.sentinel.max_resolution, + mock.sentinel.vram_bytes) + + self.assertEqual(mock.sentinel.monitor_count, + expected_res.MaximumMonitors) + self.assertEqual(mock.sentinel.max_resolution, + expected_res.MaximumScreenResolution) + mock_set_remotefx_vram.assert_called_once_with( + expected_res, mock.sentinel.vram_bytes) + + if new_obj: + mock_get_new_rsd.assert_called_once_with( + self._vmutils._REMOTEFX_DISP_CTRL_RES_SUB_TYPE, + self._vmutils._REMOTEFX_DISP_ALLOCATION_SETTING_DATA_CLASS) + self._vmutils._jobutils.add_virt_resource.assert_called_once_with( + expected_res, mock.sentinel.fake_vm) + else: + self.assertFalse(mock_get_new_rsd.called) + modify_virt_res = self._vmutils._jobutils.modify_virt_resource + modify_virt_res.assert_called_once_with(expected_res) + + def test_set_remotefx_vram(self): + self._vmutils._set_remotefx_vram(mock.sentinel.remotefx_ctrl_res, + mock.sentinel.vram_bytes) + @mock.patch.object(_wqlutils, 'get_element_associated_class') - @mock.patch.object(vmutils.VMUtils, '_add_3d_display_controller') + @mock.patch.object(vmutils.VMUtils, '_set_remotefx_display_controller') @mock.patch.object(vmutils.VMUtils, '_vm_has_s3_controller') def test_enable_remotefx_video_adapter(self, mock_vm_has_s3_controller, - mock_add_3d_ctrl, + mock_set_remotefx_ctrl, mock_get_element_associated_class): mock_vm = self._lookup_vm() @@ -1234,12 +1273,12 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): mock_get_element_associated_class.assert_called_once_with( self._vmutils._conn, self._vmutils._CIM_RES_ALLOC_SETTING_DATA_CLASS, - element_uuid=mock_vm.Name) + element_instance_id=mock_vm.InstanceID) self._vmutils._jobutils.remove_virt_resource.assert_called_once_with( mock_r1) - mock_add_3d_ctrl.assert_called_once_with( - mock_vm, self._FAKE_MONITOR_COUNT, + mock_set_remotefx_ctrl.assert_called_once_with( + mock_vm, None, self._FAKE_MONITOR_COUNT, self._vmutils._remote_fx_res_map[ constants.REMOTEFX_MAX_RES_1024x768], None) @@ -1249,24 +1288,53 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase): self.assertEqual(self._vmutils._DISP_CTRL_ADDRESS_DX_11, mock_r2.Address) + @mock.patch.object(vmutils.VMUtils, '_vm_has_s3_controller') + @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') @mock.patch.object(_wqlutils, 'get_element_associated_class') - def test_enable_remotefx_video_adapter_already_configured( - self, mock_get_element_associated_class): + def test_disable_remotefx_video_adapter(self, + mock_get_element_associated_class, + mock_get_new_rsd, + mock_vm_has_s3_controller): mock_vm = self._lookup_vm() + mock_r1 = mock.MagicMock( + ResourceSubType=self._vmutils._REMOTEFX_DISP_CTRL_RES_SUB_TYPE) + mock_r2 = mock.MagicMock( + ResourceSubType=self._vmutils._S3_DISP_CTRL_RES_SUB_TYPE) - mock_r = mock.MagicMock() - mock_r.ResourceSubType = self._vmutils._SYNTH_3D_DISP_CTRL_RES_SUB_TYPE + mock_get_element_associated_class.return_value = [mock_r1, mock_r2] - mock_get_element_associated_class.return_value = [mock_r] + self._vmutils.disable_remotefx_video_adapter( + mock.sentinel.fake_vm_name) - self.assertRaises(exceptions.HyperVRemoteFXException, - self._vmutils.enable_remotefx_video_adapter, - mock.sentinel.fake_vm_name, self._FAKE_MONITOR_COUNT, - constants.REMOTEFX_MAX_RES_1024x768) mock_get_element_associated_class.assert_called_once_with( self._vmutils._conn, self._vmutils._CIM_RES_ALLOC_SETTING_DATA_CLASS, - element_uuid=mock_vm.Name) + element_instance_id=mock_vm.InstanceID) + self._vmutils._jobutils.remove_virt_resource.assert_called_once_with( + mock_r1) + mock_get_new_rsd.assert_called_once_with( + self._vmutils._SYNTH_DISP_CTRL_RES_SUB_TYPE, + self._vmutils._SYNTH_DISP_ALLOCATION_SETTING_DATA_CLASS) + self._vmutils._jobutils.add_virt_resource.assert_called_once_with( + mock_get_new_rsd.return_value, mock_vm) + self._vmutils._jobutils.modify_virt_resource.assert_called_once_with( + mock_r2) + self.assertEqual(self._vmutils._DISP_CTRL_ADDRESS, mock_r2.Address) + + @mock.patch.object(_wqlutils, 'get_element_associated_class') + def test_disable_remotefx_video_adapter_not_found( + self, mock_get_element_associated_class): + mock_vm = self._lookup_vm() + mock_get_element_associated_class.return_value = [] + + self._vmutils.disable_remotefx_video_adapter( + mock.sentinel.fake_vm_name) + + mock_get_element_associated_class.assert_called_once_with( + self._vmutils._conn, + self._vmutils._CIM_RES_ALLOC_SETTING_DATA_CLASS, + element_instance_id=mock_vm.InstanceID) + self.assertFalse(self._vmutils._jobutils.remove_virt_resource.called) @mock.patch.object(vmutils.VMUtils, 'get_vm_generation') def test_vm_has_s3_controller(self, mock_get_vm_generation): diff --git a/os_win/tests/unit/utils/compute/test_vmutils10.py b/os_win/tests/unit/utils/compute/test_vmutils10.py index a6096d14..2340a753 100644 --- a/os_win/tests/unit/utils/compute/test_vmutils10.py +++ b/os_win/tests/unit/utils/compute/test_vmutils10.py @@ -14,6 +14,7 @@ import ddt import mock +import six from os_win import constants from os_win import exceptions @@ -118,6 +119,18 @@ class VMUtils10TestCase(test_base.OsWinBaseTestCase): 1, constants.REMOTEFX_MAX_RES_1024x768, vram_bytes=10000) + def test_validate_remotefx(self): + self._vmutils._validate_remotefx_params( + 1, constants.REMOTEFX_MAX_RES_1024x768) + + def test_set_remotefx_vram(self): + remotefx_ctrl_res = mock.MagicMock() + vram_bytes = 512 + + self._vmutils._set_remotefx_vram(remotefx_ctrl_res, vram_bytes) + self.assertEqual(six.text_type(vram_bytes), + remotefx_ctrl_res.VRAMSizeBytes) + @mock.patch.object(vmutils10.VMUtils10, 'get_vm_generation') def _test_vm_has_s3_controller(self, vm_gen, mock_get_vm_gen): mock_get_vm_gen.return_value = vm_gen diff --git a/os_win/utils/compute/vmutils.py b/os_win/utils/compute/vmutils.py index 0444ed11..8d10fa5d 100644 --- a/os_win/utils/compute/vmutils.py +++ b/os_win/utils/compute/vmutils.py @@ -75,9 +75,11 @@ class VMUtils(baseutils.BaseUtilsVirt): _S3_DISP_CTRL_RES_SUB_TYPE = 'Microsoft:Hyper-V:S3 Display Controller' _SYNTH_DISP_CTRL_RES_SUB_TYPE = ('Microsoft:Hyper-V:Synthetic Display ' 'Controller') - _SYNTH_3D_DISP_CTRL_RES_SUB_TYPE = ('Microsoft:Hyper-V:Synthetic 3D ' + _REMOTEFX_DISP_CTRL_RES_SUB_TYPE = ('Microsoft:Hyper-V:Synthetic 3D ' 'Display Controller') - _SYNTH_3D_DISP_ALLOCATION_SETTING_DATA_CLASS = ( + _SYNTH_DISP_ALLOCATION_SETTING_DATA_CLASS = ( + 'Msvm_SyntheticDisplayControllerSettingData') + _REMOTEFX_DISP_ALLOCATION_SETTING_DATA_CLASS = ( 'Msvm_Synthetic3DDisplayControllerSettingData') _VIRTUAL_SYSTEM_SUBTYPE = 'VirtualSystemSubType' @@ -112,6 +114,7 @@ class VMUtils(baseutils.BaseUtilsVirt): } _DISP_CTRL_ADDRESS_DX_11 = "02C1,00000000,01" + _DISP_CTRL_ADDRESS = "5353,00000000,00" _vm_power_states_map = {constants.HYPERV_VM_STATE_ENABLED: 2, constants.HYPERV_VM_STATE_DISABLED: 3, @@ -1066,44 +1069,86 @@ class VMUtils(baseutils.BaseUtilsVirt): 'max_monitors': self._remotefx_max_monitors_map[max_resolution]}) - def _add_3d_display_controller(self, vm, monitor_count, - max_resolution, vram_bytes=None): - synth_3d_disp_ctrl_res = self._get_new_resource_setting_data( - self._SYNTH_3D_DISP_CTRL_RES_SUB_TYPE, - self._SYNTH_3D_DISP_ALLOCATION_SETTING_DATA_CLASS) + def _set_remotefx_display_controller(self, vm, remotefx_disp_ctrl_res, + monitor_count, max_resolution, + vram_bytes=None): + new_wmi_obj = False + if not remotefx_disp_ctrl_res: + new_wmi_obj = True + remotefx_disp_ctrl_res = self._get_new_resource_setting_data( + self._REMOTEFX_DISP_CTRL_RES_SUB_TYPE, + self._REMOTEFX_DISP_ALLOCATION_SETTING_DATA_CLASS) - synth_3d_disp_ctrl_res.MaximumMonitors = monitor_count - synth_3d_disp_ctrl_res.MaximumScreenResolution = max_resolution + remotefx_disp_ctrl_res.MaximumMonitors = monitor_count + remotefx_disp_ctrl_res.MaximumScreenResolution = max_resolution + self._set_remotefx_vram(remotefx_disp_ctrl_res, vram_bytes) - self._jobutils.add_virt_resource(synth_3d_disp_ctrl_res, vm) + if new_wmi_obj: + self._jobutils.add_virt_resource(remotefx_disp_ctrl_res, vm) + else: + self._jobutils.modify_virt_resource(remotefx_disp_ctrl_res) + + def _set_remotefx_vram(self, remotefx_disp_ctrl_res, vram_bytes): + pass def enable_remotefx_video_adapter(self, vm_name, monitor_count, max_resolution, vram_bytes=None): - vm = self._lookup_vm_check(vm_name, as_vssd=False) - self._validate_remotefx_params(monitor_count, max_resolution, vram_bytes=vram_bytes) + vm = self._lookup_vm_check(vm_name) rasds = _wqlutils.get_element_associated_class( self._compat_conn, self._CIM_RES_ALLOC_SETTING_DATA_CLASS, - element_uuid=vm.Name) - if [r for r in rasds if r.ResourceSubType == - self._SYNTH_3D_DISP_CTRL_RES_SUB_TYPE]: - raise exceptions.HyperVRemoteFXException( - _("RemoteFX is already configured for this VM")) + element_instance_id=vm.InstanceID) synth_disp_ctrl_res_list = [r for r in rasds if r.ResourceSubType == self._SYNTH_DISP_CTRL_RES_SUB_TYPE] if synth_disp_ctrl_res_list: + # we need to remove the generic display controller first. self._jobutils.remove_virt_resource(synth_disp_ctrl_res_list[0]) + remotefx_disp_ctrl_res = [r for r in rasds if r.ResourceSubType == + self._REMOTEFX_DISP_CTRL_RES_SUB_TYPE] + remotefx_disp_ctrl_res = (remotefx_disp_ctrl_res[0] + if remotefx_disp_ctrl_res else None) + max_res_value = self._remote_fx_res_map.get(max_resolution) - self._add_3d_display_controller(vm, monitor_count, max_res_value, - vram_bytes) - if self._vm_has_s3_controller(vm.ElementName): + self._set_remotefx_display_controller( + vm, remotefx_disp_ctrl_res, monitor_count, max_res_value, + vram_bytes) + + if self._vm_has_s3_controller(vm_name): s3_disp_ctrl_res = [r for r in rasds if r.ResourceSubType == self._S3_DISP_CTRL_RES_SUB_TYPE][0] - s3_disp_ctrl_res.Address = self._DISP_CTRL_ADDRESS_DX_11 + if s3_disp_ctrl_res.Address != self._DISP_CTRL_ADDRESS_DX_11: + s3_disp_ctrl_res.Address = self._DISP_CTRL_ADDRESS_DX_11 + self._jobutils.modify_virt_resource(s3_disp_ctrl_res) + + def disable_remotefx_video_adapter(self, vm_name): + vm = self._lookup_vm_check(vm_name) + rasds = _wqlutils.get_element_associated_class( + self._compat_conn, self._CIM_RES_ALLOC_SETTING_DATA_CLASS, + element_instance_id=vm.InstanceID) + + remotefx_disp_ctrl_res = [r for r in rasds if r.ResourceSubType == + self._REMOTEFX_DISP_CTRL_RES_SUB_TYPE] + + if not remotefx_disp_ctrl_res: + # VM does not have RemoteFX configured. + return + + # we need to remove the RemoteFX display controller first. + self._jobutils.remove_virt_resource(remotefx_disp_ctrl_res[0]) + + synth_disp_ctrl_res = self._get_new_resource_setting_data( + self._SYNTH_DISP_CTRL_RES_SUB_TYPE, + self._SYNTH_DISP_ALLOCATION_SETTING_DATA_CLASS) + self._jobutils.add_virt_resource(synth_disp_ctrl_res, vm) + + if self._vm_has_s3_controller(vm_name): + s3_disp_ctrl_res = [r for r in rasds if r.ResourceSubType == + self._S3_DISP_CTRL_RES_SUB_TYPE][0] + s3_disp_ctrl_res.Address = self._DISP_CTRL_ADDRESS self._jobutils.modify_virt_resource(s3_disp_ctrl_res) def _vm_has_s3_controller(self, vm_name): diff --git a/os_win/utils/compute/vmutils10.py b/os_win/utils/compute/vmutils10.py index 05f199cc..3fcf56e9 100644 --- a/os_win/utils/compute/vmutils10.py +++ b/os_win/utils/compute/vmutils10.py @@ -16,6 +16,7 @@ import re from oslo_log import log as logging +import six from os_win._i18n import _ from os_win import constants @@ -112,26 +113,16 @@ class VMUtils10(vmutils.VMUtils): vram_bytes=None): super(VMUtils10, self)._validate_remotefx_params(monitor_count, max_resolution) - if vram_bytes not in self._remotefx_vram_vals: + 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 _add_3d_display_controller(self, vm, monitor_count, - max_resolution, vram_bytes=None): - synth_3d_disp_ctrl_res = self._get_new_resource_setting_data( - self._SYNTH_3D_DISP_CTRL_RES_SUB_TYPE, - self._SYNTH_3D_DISP_ALLOCATION_SETTING_DATA_CLASS) - - synth_3d_disp_ctrl_res.MaximumMonitors = monitor_count - synth_3d_disp_ctrl_res.MaximumScreenResolution = max_resolution - + def _set_remotefx_vram(self, remotefx_disp_ctrl_res, vram_bytes): if vram_bytes: - synth_3d_disp_ctrl_res.VRAMSizeBytes = unicode(vram_bytes) - - self._jobutils.add_virt_resource(synth_3d_disp_ctrl_res, vm) + 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