Hello community, here is the log from the commit of package python-os-win for openSUSE:Factory checked in at 2017-04-06 11:01:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-os-win (Old) and /work/SRC/openSUSE:Factory/.python-os-win.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-os-win" Thu Apr 6 11:01:43 2017 rev:2 rq:482821 version:1.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-os-win/python-os-win.changes 2017-02-21 13:42:45.748508752 +0100 +++ /work/SRC/openSUSE:Factory/.python-os-win.new/python-os-win.changes 2017-04-06 11:01:53.545769584 +0200 @@ -1,0 +2,13 @@ +Mon Mar 27 08:53:38 UTC 2017 - [email protected] + +- update to version 1.4.1: + - vmutils: Adds vnuma_enabled argument to update_vm + - vmutils: set all *DataRoot paths to the same value + - removes the VirtualSystemType kwarg when fetching VMs + - Update .gitreview for stable/ocata + - Update UPPER_CONSTRAINTS_FILE for stable/ocata + - pathutils: Adds retry decorator to rmdir + - vmutils: Allows updating and disabling instance RemoteFX + - vmutils: Adds remove_all_pci_devices method + +------------------------------------------------------------------- Old: ---- os-win-1.4.0.tar.gz New: ---- os-win-1.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-os-win.spec ++++++ --- /var/tmp/diff_new_pack.kmFPhC/_old 2017-04-06 11:01:54.189678578 +0200 +++ /var/tmp/diff_new_pack.kmFPhC/_new 2017-04-06 11:01:54.193678013 +0200 @@ -18,7 +18,7 @@ %global sname os-win Name: python-os-win -Version: 1.4.0 +Version: 1.4.1 Release: 0 Summary: Hyper-V library for OpenStack projects License: Apache-2.0 ++++++ os-win-1.4.0.tar.gz -> os-win-1.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/AUTHORS new/os-win-1.4.1/AUTHORS --- old/os-win-1.4.0/AUTHORS 2017-01-18 20:58:29.000000000 +0100 +++ new/os-win-1.4.1/AUTHORS 2017-03-06 20:04:40.000000000 +0100 @@ -15,6 +15,7 @@ Lucian Petrut <[email protected]> Monty Taylor <[email protected]> Nashwan Azhari <[email protected]> +OpenStack Release Bot <[email protected]> Simona Iuliana Toader <[email protected]> Swapnil Kulkarni (coolsvap) <[email protected]> Thomas Bechtold <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/ChangeLog new/os-win-1.4.1/ChangeLog --- old/os-win-1.4.0/ChangeLog 2017-01-18 20:58:29.000000000 +0100 +++ new/os-win-1.4.1/ChangeLog 2017-03-06 20:04:40.000000000 +0100 @@ -1,6 +1,18 @@ CHANGES ======= +1.4.1 +----- + +* pathutils: Adds retry decorator to rmdir +* vmutils: Adds vnuma_enabled argument to update_vm +* vmutils: Allows updating and disabling instance RemoteFX +* vmutils: Adds remove_all_pci_devices method +* removes the VirtualSystemType kwarg when fetching VMs +* vmutils: set all *DataRoot paths to the same value +* Update UPPER_CONSTRAINTS_FILE for stable/ocata +* Update .gitreview for stable/ocata + 1.4.0 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/PKG-INFO new/os-win-1.4.1/PKG-INFO --- old/os-win-1.4.0/PKG-INFO 2017-01-18 20:58:29.000000000 +0100 +++ new/os-win-1.4.1/PKG-INFO 2017-03-06 20:04:41.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: os-win -Version: 1.4.0 +Version: 1.4.1 Summary: Windows / Hyper-V library for OpenStack projects. Home-page: http://www.cloudbase.it/ Author: OpenStack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/exceptions.py new/os-win-1.4.1/os_win/exceptions.py --- old/os-win-1.4.0/os_win/exceptions.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/exceptions.py 2017-03-06 20:02:30.000000000 +0100 @@ -24,11 +24,16 @@ # Define WMI specific exceptions, so WMI won't have to be imported in any # module that expects those exceptions. if sys.platform == 'win32': + from six.moves.builtins import WindowsError import wmi x_wmi = wmi.x_wmi x_wmi_timed_out = wmi.x_wmi_timed_out else: + class WindowsError(Exception): + def __init__(self, winerror=None): + self.winerror = winerror + class x_wmi(Exception): pass @@ -41,6 +46,7 @@ def __init__(self, message=None, **kwargs): self.kwargs = kwargs + self.error_code = kwargs.get('error_code') if not message: message = self.msg_fmt % kwargs @@ -100,10 +106,6 @@ "Error code: %(error_code)s. " "Error message: %(error_message)s") - def __init__(self, message=None, **kwargs): - self.error_code = kwargs.get('error_code') - super(Win32Exception, self).__init__(message=message, **kwargs) - class VHDException(OSWinException): pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/tests/unit/utils/compute/test_vmutils.py new/os-win-1.4.1/os_win/tests/unit/utils/compute/test_vmutils.py --- old/os-win-1.4.0/os_win/tests/unit/utils/compute/test_vmutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/tests/unit/utils/compute/test_vmutils.py 2017-03-06 20:02:31.000000000 +0100 @@ -64,7 +64,6 @@ _DEFINE_SYSTEM = 'DefineSystem' _DESTROY_SYSTEM = 'DestroySystem' _DESTROY_SNAPSHOT = 'DestroySnapshot' - _SETTING_TYPE = 'VirtualSystemType' _VM_GEN = constants.VM_GEN_2 _VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized' @@ -119,11 +118,15 @@ as_vssd=False) def test_lookup_vm_as_vssd(self): + vssd = mock.MagicMock() + expected_vssd = mock.MagicMock( + VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) + self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [ - mock.sentinel.fake_vssd] + vssd, expected_vssd] vssd = self._vmutils._lookup_vm_check(self._FAKE_VM_NAME) - self.assertEqual(mock.sentinel.fake_vssd, vssd) + self.assertEqual(expected_vssd, vssd) def test_set_vm_memory_static(self): self._test_set_vm_memory_dynamic(dynamic_memory_ratio=1.0) @@ -218,40 +221,27 @@ @mock.patch.object(vmutils.VMUtils, '_get_vm_disks') @mock.patch.object(vmutils.VMUtils, '_lookup_vm_check') - @mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type') - def test_get_vm_storage_paths(self, mock_get_virtual_system_type, - mock_lookup_vm_check, mock_get_vm_disks): - virtual_system_type = mock_get_virtual_system_type.return_value + def test_get_vm_storage_paths(self, mock_lookup_vm_check, + mock_get_vm_disks): mock_rasds = self._create_mock_disks() mock_get_vm_disks.return_value = ([mock_rasds[0]], [mock_rasds[1]]) - storage = self._vmutils.get_vm_storage_paths( - self._FAKE_VM_NAME, - is_planned_vm=False) + storage = self._vmutils.get_vm_storage_paths(self._FAKE_VM_NAME) (disk_files, volume_drives) = storage self.assertEqual([self._FAKE_VHD_PATH], disk_files) self.assertEqual([self._FAKE_VOLUME_DRIVE_PATH], volume_drives) - mock_get_virtual_system_type.assert_called_once_with(False) - mock_lookup_vm_check.assert_called_once_with( - self._FAKE_VM_NAME, virtual_system_type=virtual_system_type) + mock_lookup_vm_check.assert_called_once_with(self._FAKE_VM_NAME) @mock.patch.object(vmutils.VMUtils, '_get_vm_disks') - @mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type') - def test_get_vm_disks_by_instance_name(self, - mock_get_virtual_system_type, - mock_get_vm_disks): - virtual_system_type = mock_get_virtual_system_type.return_value + def test_get_vm_disks_by_instance_name(self, mock_get_vm_disks): self._lookup_vm() mock_get_vm_disks.return_value = mock.sentinel.vm_disks - vm_disks = self._vmutils.get_vm_disks( - self._FAKE_VM_NAME, is_planned_vm=False) + vm_disks = self._vmutils.get_vm_disks(self._FAKE_VM_NAME) - mock_get_virtual_system_type.assert_called_once_with(False) self._vmutils._lookup_vm_check.assert_called_once_with( - self._FAKE_VM_NAME, - virtual_system_type=virtual_system_type) + self._FAKE_VM_NAME) self.assertEqual(mock.sentinel.vm_disks, vm_disks) @mock.patch.object(_wqlutils, 'get_element_associated_class') @@ -304,9 +294,8 @@ mock.sentinel.vm_name, mock.sentinel.state) @ddt.data( + {'vnuma_enabled': mock.sentinel.vnuma_enabled}, {'configuration_root_dir': mock.sentinel.configuration_root_dir}, - {'snapshot_dir': mock.sentinel.snapshot_dir, 'is_planned_vm': True}, - {'is_planned_vm': True}, {'host_shutdown_action': mock.sentinel.shutdown_action}, {}) @ddt.unpack @@ -314,34 +303,45 @@ @mock.patch.object(vmutils.VMUtils, '_set_vm_vcpus') @mock.patch.object(vmutils.VMUtils, '_set_vm_memory') @mock.patch.object(vmutils.VMUtils, '_lookup_vm_check') - @mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type') - def test_update_vm(self, mock_get_virtual_system_type, - mock_lookup_vm_check, mock_set_mem, mock_set_vcpus, - mock_modify_virtual_system, + def test_update_vm(self, mock_lookup_vm_check, mock_set_mem, + mock_set_vcpus, mock_modify_virtual_system, host_shutdown_action=None, - configuration_root_dir=None, - snapshot_dir=None, is_planned_vm=False): + configuration_root_dir=None, vnuma_enabled=None): mock_vmsettings = mock_lookup_vm_check.return_value - virtual_system_type = mock_get_virtual_system_type.return_value self._vmutils.update_vm( mock.sentinel.vm_name, mock.sentinel.memory_mb, mock.sentinel.memory_per_numa, mock.sentinel.vcpus_num, mock.sentinel.vcpus_per_numa, mock.sentinel.limit_cpu_features, mock.sentinel.dynamic_mem_ratio, configuration_root_dir, - snapshot_dir, host_shutdown_action=host_shutdown_action, - is_planned_vm=is_planned_vm) + vnuma_enabled=vnuma_enabled) - mock_get_virtual_system_type.assert_called_once_with(is_planned_vm) - mock_lookup_vm_check.assert_called_once_with( - mock.sentinel.vm_name, virtual_system_type=virtual_system_type) + mock_lookup_vm_check.assert_called_once_with(mock.sentinel.vm_name) mock_set_mem.assert_called_once_with( mock_vmsettings, mock.sentinel.memory_mb, mock.sentinel.memory_per_numa, mock.sentinel.dynamic_mem_ratio) mock_set_vcpus.assert_called_once_with( mock_vmsettings, mock.sentinel.vcpus_num, mock.sentinel.vcpus_per_numa, mock.sentinel.limit_cpu_features) - if configuration_root_dir or snapshot_dir or host_shutdown_action: + + if configuration_root_dir: + self.assertEqual(configuration_root_dir, + mock_vmsettings.ConfigurationDataRoot) + self.assertEqual(configuration_root_dir, + mock_vmsettings.LogDataRoot) + self.assertEqual(configuration_root_dir, + mock_vmsettings.SnapshotDataRoot) + self.assertEqual(configuration_root_dir, + mock_vmsettings.SuspendDataRoot) + self.assertEqual(configuration_root_dir, + mock_vmsettings.SwapFileDataRoot) + if host_shutdown_action: + self.assertEqual(host_shutdown_action, + mock_vmsettings.AutomaticShutdownAction) + if vnuma_enabled: + self.assertEqual(vnuma_enabled, mock_vmsettings.VirtualNumaEnabled) + + if configuration_root_dir or host_shutdown_action or vnuma_enabled: mock_modify_virtual_system.assert_called_once_with( mock_vmsettings) else: @@ -365,14 +365,11 @@ @mock.patch.object(vmutils.VMUtils, '_set_vm_vcpus') @mock.patch.object(vmutils.VMUtils, '_set_vm_memory') def test_old_create_vm(self, mock_set_mem, mock_set_vcpus): + mock_vmsetting = self._lookup_vm() mock_svc = self._vmutils._vs_man_svc getattr(mock_svc, self._DEFINE_SYSTEM).return_value = ( None, self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - mock_vmsetting = mock.MagicMock() - self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [ - mock_vmsetting] - self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB, self._FAKE_VCPUS_NUM, False, self._FAKE_DYNAMIC_MEMORY_RATIO, @@ -587,11 +584,9 @@ mock_get_vm_disks.return_value = ([], [mock_phys_disk]) - result = self._vmutils.get_vm_physical_disk_mapping( - self._FAKE_VM_NAME, is_planned_vm=False) + result = self._vmutils.get_vm_physical_disk_mapping(self._FAKE_VM_NAME) self.assertEqual(expected_mapping, result) - mock_get_vm_disks.assert_called_once_with( - self._FAKE_VM_NAME, is_planned_vm=False) + mock_get_vm_disks.assert_called_once_with(self._FAKE_VM_NAME) @mock.patch.object(vmutils.VMUtils, '_get_wmi_obj') def test_set_disk_host_res(self, mock_get_wmi_obj): @@ -1220,12 +1215,51 @@ 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() @@ -1245,12 +1279,12 @@ 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) @@ -1260,24 +1294,53 @@ 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) + + 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._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) - 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.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): @@ -1297,15 +1360,6 @@ disk_resource) self.assertEqual(disk_resource.HostResource, [mock.sentinel.new_path]) - @ddt.data(True, False) - def test_get_virtual_system_type(self, is_planned_vm): - exp_result = ( - self._vmutils._VIRTUAL_SYSTEM_TYPE_PLANNED if is_planned_vm - else self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) - actual_result = self._vmutils._get_virtual_system_type( - is_planned_vm=is_planned_vm) - self.assertEqual(exp_result, actual_result) - def test_add_pci_device(self): self.assertRaises(NotImplementedError, self._vmutils.add_pci_device, @@ -1317,3 +1371,6 @@ self._vmutils.remove_pci_device, mock.sentinel.vm_name, mock.sentinel.vendor_id, mock.sentinel.product_id) + + def test_remove_all_pci_devices(self): + self._vmutils.remove_all_pci_devices(mock.sentinel.vm_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/tests/unit/utils/compute/test_vmutils10.py new/os-win-1.4.1/os_win/tests/unit/utils/compute/test_vmutils10.py --- old/os-win-1.4.0/os_win/tests/unit/utils/compute/test_vmutils10.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/tests/unit/utils/compute/test_vmutils10.py 2017-03-06 20:02:31.000000000 +0100 @@ -14,6 +14,7 @@ import ddt import mock +import six from os_win import constants from os_win import exceptions @@ -118,6 +119,18 @@ 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 @@ -319,3 +332,20 @@ vmsettings.InstanceID) self._vmutils._jobutils.remove_virt_resource.assert_called_once_with( pci_setting_data) + + @mock.patch.object(_wqlutils, 'get_element_associated_class') + @mock.patch.object(vmutils10.VMUtils10, '_lookup_vm_check') + def test_remove_all_pci_devices(self, mock_lookup_vm_check, + mock_get_element_associated_class): + vmsettings = mock_lookup_vm_check.return_value + + self._vmutils.remove_all_pci_devices(mock.sentinel.vm_name) + + mock_lookup_vm_check.assert_called_once_with(mock.sentinel.vm_name) + mock_get_element_associated_class.assert_called_once_with( + self._vmutils._conn, self._vmutils._PCI_EXPRESS_SETTING_DATA, + vmsettings.InstanceID) + mock_remove_multiple_virt_resource = ( + self._vmutils._jobutils.remove_multiple_virt_resources) + mock_remove_multiple_virt_resource.assert_called_once_with( + mock_get_element_associated_class.return_value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/tests/unit/utils/metrics/test_metricsutils.py new/os-win-1.4.1/os_win/tests/unit/utils/metrics/test_metricsutils.py --- old/os-win-1.4.0/os_win/tests/unit/utils/metrics/test_metricsutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/tests/unit/utils/metrics/test_metricsutils.py 2017-03-06 20:02:30.000000000 +0100 @@ -387,9 +387,7 @@ self.assertEqual(mock_unique_result.return_value, result) conn_class = self.utils._conn.Msvm_VirtualSystemSettingData - conn_class.assert_called_once_with( - ElementName=mock.sentinel.vm_name, - VirtualSystemType=self.utils._VIRTUAL_SYSTEM_TYPE_REALIZED) + conn_class.assert_called_once_with(ElementName=mock.sentinel.vm_name) mock_unique_result.assert_called_once_with(conn_class.return_value, mock.sentinel.vm_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/tests/unit/utils/test_pathutils.py new/os-win-1.4.1/os_win/tests/unit/utils/test_pathutils.py --- old/os-win-1.4.0/os_win/tests/unit/utils/test_pathutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/tests/unit/utils/test_pathutils.py 2017-03-06 20:02:30.000000000 +0100 @@ -73,11 +73,31 @@ self._pathutils.move_folder_files(src_dir, dest_dir) mock_rename.assert_called_once_with(src_fname, dest_fname) - @mock.patch.object(pathutils.PathUtils, 'rmtree') - def test_rmtree(self, mock_rmtree): + @mock.patch('time.sleep') + @mock.patch.object(pathutils.shutil, 'rmtree') + def test_rmtree(self, mock_rmtree, mock_sleep): + exc = exceptions.WindowsError() + exc.winerror = pathutils.ERROR_DIR_IS_NOT_EMPTY + mock_rmtree.side_effect = [exc] * 5 + [None] + self._pathutils.rmtree(mock.sentinel.FAKE_PATH) - mock_rmtree.assert_called_once_with(mock.sentinel.FAKE_PATH) + mock_rmtree.assert_has_calls([mock.call(mock.sentinel.FAKE_PATH)] * 6) + + @mock.patch('time.sleep') + @mock.patch.object(pathutils.shutil, 'rmtree') + def _check_rmtree(self, mock_rmtree, mock_sleep, side_effect): + mock_rmtree.side_effect = side_effect + self.assertRaises(exceptions.OSWinException, self._pathutils.rmtree, + mock.sentinel.FAKE_PATH) + + def test_rmtree_unexpected(self): + self._check_rmtree(side_effect=exceptions.WindowsError) + + def test_rmtree_exceeded(self): + exc = exceptions.WindowsError() + exc.winerror = pathutils.ERROR_DIR_IS_NOT_EMPTY + self._check_rmtree(side_effect=[exc] * 6) @mock.patch.object(pathutils.PathUtils, 'makedirs') @mock.patch.object(pathutils.PathUtils, 'exists') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/utils/compute/vmutils.py new/os-win-1.4.1/os_win/utils/compute/vmutils.py --- old/os-win-1.4.0/os_win/utils/compute/vmutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/utils/compute/vmutils.py 2017-03-06 20:02:31.000000000 +0100 @@ -42,6 +42,9 @@ LOG = logging.getLogger(__name__) +# TODO(claudiub): remove the is_planned_vm argument from methods once it is not +# used anymore. + class VMUtils(baseutils.BaseUtilsVirt): @@ -72,9 +75,11 @@ _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' @@ -109,6 +114,7 @@ } _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, @@ -191,21 +197,19 @@ settings = self.get_vm_summary_info(vm_name) return settings['EnabledState'] - def _lookup_vm_check(self, vm_name, as_vssd=True, for_update=False, - virtual_system_type=_VIRTUAL_SYSTEM_TYPE_REALIZED): - vm = self._lookup_vm(vm_name, as_vssd, for_update, - virtual_system_type) + def _lookup_vm_check(self, vm_name, as_vssd=True, for_update=False): + vm = self._lookup_vm(vm_name, as_vssd, for_update) if not vm: raise exceptions.HyperVVMNotFoundException(vm_name=vm_name) return vm - def _lookup_vm(self, vm_name, as_vssd=True, for_update=False, - virtual_system_type=_VIRTUAL_SYSTEM_TYPE_REALIZED): + def _lookup_vm(self, vm_name, as_vssd=True, for_update=False): if as_vssd: conn = self._compat_conn if for_update else self._conn - vms = conn.Msvm_VirtualSystemSettingData( - ElementName=vm_name, - VirtualSystemType=virtual_system_type) + vms = conn.Msvm_VirtualSystemSettingData(ElementName=vm_name) + vms = [v for v in vms if + v.VirtualSystemType in [self._VIRTUAL_SYSTEM_TYPE_PLANNED, + self._VIRTUAL_SYSTEM_TYPE_REALIZED]] else: vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) n = len(vms) @@ -282,26 +286,31 @@ def update_vm(self, vm_name, memory_mb, memory_per_numa_node, vcpus_num, vcpus_per_numa_node, limit_cpu_features, dynamic_mem_ratio, configuration_root_dir=None, snapshot_dir=None, - host_shutdown_action=None, + host_shutdown_action=None, vnuma_enabled=None, is_planned_vm=False): - virtual_system_type = self._get_virtual_system_type(is_planned_vm) - - vmsetting = self._lookup_vm_check( - vm_name, virtual_system_type=virtual_system_type) + vmsetting = self._lookup_vm_check(vm_name) if host_shutdown_action: vmsetting.AutomaticShutdownAction = host_shutdown_action if configuration_root_dir: + # Created VMs must have their *DataRoot paths in the same location + # as the VM's path. vmsetting.ConfigurationDataRoot = configuration_root_dir - if snapshot_dir: - vmsetting.SnapshotDataRoot = snapshot_dir + vmsetting.LogDataRoot = configuration_root_dir + vmsetting.SnapshotDataRoot = configuration_root_dir + vmsetting.SuspendDataRoot = configuration_root_dir + vmsetting.SwapFileDataRoot = configuration_root_dir + if vnuma_enabled is not None: + vmsetting.VirtualNumaEnabled = vnuma_enabled + self._set_vm_memory(vmsetting, memory_mb, memory_per_numa_node, dynamic_mem_ratio) self._set_vm_vcpus(vmsetting, vcpus_num, vcpus_per_numa_node, limit_cpu_features) - update_needed = (configuration_root_dir or snapshot_dir or - host_shutdown_action) + update_needed = (configuration_root_dir or host_shutdown_action or + vnuma_enabled is not None) + if update_needed: self._modify_virtual_system(vmsetting) @@ -522,7 +531,7 @@ def get_vm_physical_disk_mapping(self, vm_name, is_planned_vm=False): mapping = {} physical_disks = ( - self.get_vm_disks(vm_name, is_planned_vm=is_planned_vm)[1]) + self.get_vm_disks(vm_name)[1]) for diskdrive in physical_disks: mapping[diskdrive.ElementName] = dict( resource_path=diskdrive.path_(), @@ -632,10 +641,7 @@ return vmsettings.ConfigurationDataRoot def get_vm_storage_paths(self, vm_name, is_planned_vm=False): - virtual_system_type = self._get_virtual_system_type(is_planned_vm) - vmsettings = self._lookup_vm_check( - vm_name, - virtual_system_type=virtual_system_type) + vmsettings = self._lookup_vm_check(vm_name) (disk_resources, volume_resources) = self._get_vm_disks(vmsettings) volume_drives = [] @@ -651,9 +657,7 @@ return (disk_files, volume_drives) def get_vm_disks(self, vm_name, is_planned_vm=False): - virtual_system_type = self._get_virtual_system_type(is_planned_vm) - vmsettings = self._lookup_vm_check( - vm_name, virtual_system_type=virtual_system_type) + vmsettings = self._lookup_vm_check(vm_name) return self._get_vm_disks(vmsettings) def _get_vm_disks(self, vmsettings): @@ -1070,44 +1074,86 @@ '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) + + remotefx_disp_ctrl_res.MaximumMonitors = monitor_count + remotefx_disp_ctrl_res.MaximumScreenResolution = max_resolution + self._set_remotefx_vram(remotefx_disp_ctrl_res, vram_bytes) - synth_3d_disp_ctrl_res.MaximumMonitors = monitor_count - synth_3d_disp_ctrl_res.MaximumScreenResolution = max_resolution + if new_wmi_obj: + self._jobutils.add_virt_resource(remotefx_disp_ctrl_res, vm) + else: + self._jobutils.modify_virt_resource(remotefx_disp_ctrl_res) - self._jobutils.add_virt_resource(synth_3d_disp_ctrl_res, vm) + 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] + 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_DX_11 + 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): @@ -1122,11 +1168,6 @@ disk_resource.HostResource = [new_disk_path] self._jobutils.modify_virt_resource(disk_resource) - def _get_virtual_system_type(self, is_planned_vm): - return ( - self._VIRTUAL_SYSTEM_TYPE_PLANNED if is_planned_vm - else self._VIRTUAL_SYSTEM_TYPE_REALIZED) - def add_pci_device(self, vm_name, vendor_id, product_id): """Adds the given PCI device to the given VM. @@ -1144,3 +1185,11 @@ """ raise NotImplementedError(_('PCI passthrough is supported on ' 'Windows / Hyper-V Server 2016 or newer.')) + + def remove_all_pci_devices(self, vm_name): + """Removes all the PCI devices from the given VM. + + There are no PCI devices attached to Windows / Hyper-V Server 2012 R2 + or older VMs. + """ + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/utils/compute/vmutils10.py new/os-win-1.4.1/os_win/utils/compute/vmutils10.py --- old/os-win-1.4.0/os_win/utils/compute/vmutils10.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/utils/compute/vmutils10.py 2017-03-06 20:02:31.000000000 +0100 @@ -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 @@ 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 @@ -282,3 +273,18 @@ "%(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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/utils/jobutils.py new/os-win-1.4.1/os_win/utils/jobutils.py --- old/os-win-1.4.0/os_win/utils/jobutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/utils/jobutils.py 2017-03-06 20:02:31.000000000 +0100 @@ -200,10 +200,13 @@ ResourceSettings=[virt_resource.GetText_(1)]) self.check_ret_val(ret_val, job_path) - @_utils.retry_decorator(exceptions=exceptions.HyperVException) def remove_virt_resource(self, virt_resource): + self.remove_multiple_virt_resources([virt_resource]) + + @_utils.retry_decorator(exceptions=exceptions.HyperVException) + def remove_multiple_virt_resources(self, virt_resources): (job, ret_val) = self._vs_man_svc.RemoveResourceSettings( - ResourceSettings=[virt_resource.path_()]) + ResourceSettings=[r.path_() for r in virt_resources]) self.check_ret_val(ret_val, job) def add_virt_feature(self, virt_feature, parent): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/utils/metrics/metricsutils.py new/os-win-1.4.1/os_win/utils/metrics/metricsutils.py --- old/os-win-1.4.0/os_win/utils/metrics/metricsutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/utils/metrics/metricsutils.py 2017-03-06 20:02:30.000000000 +0100 @@ -270,8 +270,7 @@ def _get_vm_setting_data(self, vm_name): vssds = self._conn.Msvm_VirtualSystemSettingData( - ElementName=vm_name, - VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED) + ElementName=vm_name) return self._unique_result(vssds, vm_name) @staticmethod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win/utils/pathutils.py new/os-win-1.4.1/os_win/utils/pathutils.py --- old/os-win-1.4.0/os_win/utils/pathutils.py 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/os_win/utils/pathutils.py 2017-03-06 20:02:30.000000000 +0100 @@ -25,6 +25,7 @@ import six from os_win._i18n import _ +from os_win import _utils from os_win import exceptions from os_win.utils import _acl_utils from os_win.utils import win32utils @@ -107,8 +108,16 @@ if os.path.isfile(src): self.rename(src, os.path.join(dest_dir, fname)) + @_utils.retry_decorator(exceptions=exceptions.OSWinException, + error_codes=[ERROR_DIR_IS_NOT_EMPTY]) def rmtree(self, path): - shutil.rmtree(path) + try: + shutil.rmtree(path) + except exceptions.WindowsError as ex: + # NOTE(claudiub): convert it to an OSWinException in order to use + # the retry_decorator. + raise exceptions.OSWinException(six.text_type(ex), + error_code=ex.winerror) def check_create_dir(self, path): if not self.exists(path): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win.egg-info/PKG-INFO new/os-win-1.4.1/os_win.egg-info/PKG-INFO --- old/os-win-1.4.0/os_win.egg-info/PKG-INFO 2017-01-18 20:58:29.000000000 +0100 +++ new/os-win-1.4.1/os_win.egg-info/PKG-INFO 2017-03-06 20:04:40.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: os-win -Version: 1.4.0 +Version: 1.4.1 Summary: Windows / Hyper-V library for OpenStack projects. Home-page: http://www.cloudbase.it/ Author: OpenStack diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/os_win.egg-info/pbr.json new/os-win-1.4.1/os_win.egg-info/pbr.json --- old/os-win-1.4.0/os_win.egg-info/pbr.json 2017-01-18 20:58:29.000000000 +0100 +++ new/os-win-1.4.1/os_win.egg-info/pbr.json 2017-03-06 20:04:40.000000000 +0100 @@ -1 +1 @@ -{"is_release": true, "git_version": "1c8f6de"} \ No newline at end of file +{"is_release": true, "git_version": "3d4f397"} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/os-win-1.4.0/tox.ini new/os-win-1.4.1/tox.ini --- old/os-win-1.4.0/tox.ini 2017-01-18 20:56:51.000000000 +0100 +++ new/os-win-1.4.1/tox.ini 2017-03-06 20:02:31.000000000 +0100 @@ -5,7 +5,7 @@ [testenv] usedevelop = True -install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} +install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/ocata} {opts} {packages} setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master
