From bef7e9a717f3bca45b5edd0ebc65ab1dde5942d9 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Wed, 25 Jul 2018 10:17:50 +0300 Subject: [PATCH] Hyper-V + OVS: plug vifs before starting VMs At the moment, the Hyper-V driver will bind OVS ports only after starting the VMs. This is because of an old limitation of the OVS Windows port, which has been addressed in OVS 2.5. This also means that the Nova driver fails when waiting for neutron vif plug events, since this step is performed later. So deployers are forced to set "vif_plugging_is_fatal", which means that the ports will be down, at least for a short time after the instance is reported as ACTIVE. This also breaks some Tempest tests. Since the Windows OVS version has been fixed, we can now bind the ports before actually starting the VMs. Note that this fix has been merged in the out-of-tree compute-hyperv some time ago. Co-authored-by: Alin Balutoiu Change-Id: I0116a593c29808a53423cc9bbe098f2661fcb584 Closes-Bug: #1684250 --- nova/tests/unit/virt/hyperv/test_vmops.py | 19 ++++++++++++++++--- nova/virt/hyperv/vmops.py | 15 ++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/nova/tests/unit/virt/hyperv/test_vmops.py b/nova/tests/unit/virt/hyperv/test_vmops.py index 99a3a152b786..a5fa8e90fe3b 100644 --- a/nova/tests/unit/virt/hyperv/test_vmops.py +++ b/nova/tests/unit/virt/hyperv/test_vmops.py @@ -373,6 +373,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): self._vmops._vmutils.set_boot_order.assert_called_once_with( mock.sentinel.instance_name, mock_get_boot_order.return_value) + @mock.patch.object(vmops.VMOps, 'plug_vifs') @mock.patch('nova.virt.hyperv.vmops.VMOps.destroy') @mock.patch('nova.virt.hyperv.vmops.VMOps.power_on') @mock.patch('nova.virt.hyperv.vmops.VMOps.set_boot_order') @@ -394,8 +395,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): mock_configdrive_required, mock_create_config_drive, mock_attach_config_drive, mock_set_boot_order, - mock_power_on, mock_destroy, exists, - configdrive_required, fail, + mock_power_on, mock_destroy, mock_plug_vifs, + exists, configdrive_required, fail, fake_vm_gen=constants.VM_GEN_2): mock_instance = fake_instance.fake_instance_obj(self.context) mock_image_meta = mock.MagicMock() @@ -445,6 +446,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): mock_create_instance.assert_called_once_with( mock_instance, mock.sentinel.network_info, root_device_info, block_device_info, fake_vm_gen, mock_image_meta) + mock_plug_vifs.assert_called_once_with(mock_instance, + mock.sentinel.network_info) mock_save_device_metadata.assert_called_once_with( self.context, mock_instance, block_device_info) mock_configdrive_required.assert_called_once_with(mock_instance) @@ -458,7 +461,9 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): mock_set_boot_order.assert_called_once_with( mock_instance.name, fake_vm_gen, block_device_info) mock_power_on.assert_called_once_with( - mock_instance, network_info=mock.sentinel.network_info) + mock_instance, + network_info=mock.sentinel.network_info, + should_plug_vifs=False) def test_spawn(self): self._test_spawn(exists=False, configdrive_required=True, fail=None) @@ -1326,6 +1331,14 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase): mock_plug_vifs.assert_called_once_with( mock_instance, mock.sentinel.fake_network_info) + @mock.patch.object(vmops.VMOps, 'plug_vifs') + def test_power_on_vifs_already_plugged(self, mock_plug_vifs): + mock_instance = fake_instance.fake_instance_obj(self.context) + + self._vmops.power_on(mock_instance, + should_plug_vifs=False) + self.assertFalse(mock_plug_vifs.called) + def _test_set_vm_state(self, state): mock_instance = fake_instance.fake_instance_obj(self.context) diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py index 6a3f1523360b..b37b525d30a9 100644 --- a/nova/virt/hyperv/vmops.py +++ b/nova/virt/hyperv/vmops.py @@ -286,6 +286,8 @@ class VMOps(object): # waiting will occur after the instance is created. self.create_instance(instance, network_info, root_device, block_device_info, vm_gen, image_meta) + # This is supported starting from OVS version 2.5 + self.plug_vifs(instance, network_info) self._save_device_metadata(context, instance, block_device_info) @@ -298,7 +300,12 @@ class VMOps(object): self.attach_config_drive(instance, configdrive_path, vm_gen) self.set_boot_order(instance.name, vm_gen, block_device_info) - self.power_on(instance, network_info=network_info) + # vifs are already plugged in at this point. We waited on the vif + # plug event previously when we created the instance. Skip the + # plug vifs during power on in this case + self.power_on(instance, + network_info=network_info, + should_plug_vifs=False) except Exception: with excutils.save_and_reraise_exception(): self.destroy(instance, network_info, block_device_info) @@ -843,7 +850,8 @@ class VMOps(object): LOG.debug("Instance not found. Skipping power off", instance=instance) - def power_on(self, instance, block_device_info=None, network_info=None): + def power_on(self, instance, block_device_info=None, network_info=None, + should_plug_vifs=True): """Power on the specified instance.""" LOG.debug("Power on instance", instance=instance) @@ -851,8 +859,9 @@ class VMOps(object): self._volumeops.fix_instance_volume_disk_paths(instance.name, block_device_info) + if should_plug_vifs: + self.plug_vifs(instance, network_info) self._set_vm_state(instance, os_win_const.HYPERV_VM_STATE_ENABLED) - self.plug_vifs(instance, network_info) def _set_vm_state(self, instance, req_state): instance_name = instance.name