Browse Source

Merge "Revert resize: wait for events according to hybrid plug" into stable/rocky

tags/18.2.2
Zuul 1 month ago
parent
commit
dbe35cc27a

+ 48
- 14
nova/compute/manager.py View File

@@ -4144,6 +4144,49 @@ class ComputeManager(manager.Manager):
4144 4144
             self.compute_rpcapi.finish_revert_resize(context, instance,
4145 4145
                     migration, migration.source_compute)
4146 4146
 
4147
+    def _finish_revert_resize_network_migrate_finish(self, context, instance,
4148
+                                                     migration):
4149
+        """Causes port binding to be updated. In some Neutron or port
4150
+        configurations - see NetworkModel.get_bind_time_events() - we
4151
+        expect the vif-plugged event from Neutron immediately and wait for it.
4152
+        The rest of the time, the event is expected further along in the
4153
+        virt driver, so we don't wait here.
4154
+
4155
+        :param context: The request context.
4156
+        :param instance: The instance undergoing the revert resize.
4157
+        :param migration: The Migration object of the resize being reverted.
4158
+        :raises: eventlet.timeout.Timeout or
4159
+                 exception.VirtualInterfacePlugException.
4160
+        """
4161
+        network_info = instance.get_network_info()
4162
+        events = []
4163
+        deadline = CONF.vif_plugging_timeout
4164
+        if deadline and utils.is_neutron() and network_info:
4165
+            events = network_info.get_bind_time_events(migration)
4166
+            if events:
4167
+                LOG.debug('Will wait for bind-time events: %s', events)
4168
+        error_cb = self._neutron_failed_migration_callback
4169
+        try:
4170
+            with self.virtapi.wait_for_instance_event(instance, events,
4171
+                                                      deadline=deadline,
4172
+                                                      error_callback=error_cb):
4173
+                # NOTE(hanrong): we need to change migration.dest_compute to
4174
+                # source host temporarily.
4175
+                # "network_api.migrate_instance_finish" will setup the network
4176
+                # for the instance on the destination host. For revert resize,
4177
+                # the instance will back to the source host, the setup of the
4178
+                # network for instance should be on the source host. So set
4179
+                # the migration.dest_compute to source host at here.
4180
+                with utils.temporary_mutation(
4181
+                        migration, dest_compute=migration.source_compute):
4182
+                    self.network_api.migrate_instance_finish(context,
4183
+                                                             instance,
4184
+                                                             migration)
4185
+        except eventlet.timeout.Timeout:
4186
+            with excutils.save_and_reraise_exception():
4187
+                LOG.error('Timeout waiting for Neutron events: %s', events,
4188
+                          instance=instance)
4189
+
4147 4190
     @wrap_exception()
4148 4191
     @reverts_task_state
4149 4192
     @wrap_instance_event(prefix='compute')
@@ -4182,17 +4225,8 @@ class ComputeManager(manager.Manager):
4182 4225
 
4183 4226
             self.network_api.setup_networks_on_host(context, instance,
4184 4227
                                                     migration.source_compute)
4185
-            # NOTE(hanrong): we need to change migration.dest_compute to
4186
-            # source host temporarily. "network_api.migrate_instance_finish"
4187
-            # will setup the network for the instance on the destination host.
4188
-            # For revert resize, the instance will back to the source host, the
4189
-            # setup of the network for instance should be on the source host.
4190
-            # So set the migration.dest_compute to source host at here.
4191
-            with utils.temporary_mutation(
4192
-                    migration, dest_compute=migration.source_compute):
4193
-                self.network_api.migrate_instance_finish(context,
4194
-                                                         instance,
4195
-                                                         migration)
4228
+            self._finish_revert_resize_network_migrate_finish(
4229
+                context, instance, migration)
4196 4230
             network_info = self.network_api.get_instance_nw_info(context,
4197 4231
                                                                  instance)
4198 4232
 
@@ -6322,8 +6356,8 @@ class ComputeManager(manager.Manager):
6322 6356
         return migrate_data
6323 6357
 
6324 6358
     @staticmethod
6325
-    def _neutron_failed_live_migration_callback(event_name, instance):
6326
-        msg = ('Neutron reported failure during live migration '
6359
+    def _neutron_failed_migration_callback(event_name, instance):
6360
+        msg = ('Neutron reported failure during migration '
6327 6361
                'with %(event)s for instance %(uuid)s')
6328 6362
         msg_args = {'event': event_name, 'uuid': instance.uuid}
6329 6363
         if CONF.vif_plugging_is_fatal:
@@ -6401,7 +6435,7 @@ class ComputeManager(manager.Manager):
6401 6435
                 disk = None
6402 6436
 
6403 6437
             deadline = CONF.vif_plugging_timeout
6404
-            error_cb = self._neutron_failed_live_migration_callback
6438
+            error_cb = self._neutron_failed_migration_callback
6405 6439
             # In order to avoid a race with the vif plugging that the virt
6406 6440
             # driver does on the destination host, we register our events
6407 6441
             # to wait for before calling pre_live_migration. Then if the

+ 25
- 0
nova/network/model.py View File

@@ -456,6 +456,17 @@ class VIF(Model):
456 456
                     'ips': ips}
457 457
         return []
458 458
 
459
+    def has_bind_time_event(self, migration):
460
+        """Returns whether this VIF's network-vif-plugged external event will
461
+        be sent by Neutron at "bind-time" - in other words, as soon as the port
462
+        binding is updated. This is in the context of updating the port binding
463
+        to a host that already has the instance in a shutoff state - in
464
+        practice, this means reverting either a cold migration or a
465
+        non-same-host resize.
466
+        """
467
+        return (self.is_hybrid_plug_enabled() and not
468
+                migration.is_same_host())
469
+
459 470
     def is_hybrid_plug_enabled(self):
460 471
         return self['details'].get(VIF_DETAILS_OVS_HYBRID_PLUG, False)
461 472
 
@@ -513,6 +524,20 @@ class NetworkInfo(list):
513 524
     def json(self):
514 525
         return jsonutils.dumps(self)
515 526
 
527
+    def get_bind_time_events(self, migration):
528
+        """Returns whether any of our VIFs have "bind-time" events. See
529
+        has_bind_time_event() docstring for more details.
530
+        """
531
+        return [('network-vif-plugged', vif['id'])
532
+                for vif in self if vif.has_bind_time_event(migration)]
533
+
534
+    def get_plug_time_events(self, migration):
535
+        """Complementary to get_bind_time_events(), any event that does not
536
+        fall in that category is a plug-time event.
537
+        """
538
+        return [('network-vif-plugged', vif['id'])
539
+                for vif in self if not vif.has_bind_time_event(migration)]
540
+
516 541
 
517 542
 class NetworkInfoAsyncWrapper(NetworkInfo):
518 543
     """Wrapper around NetworkInfo that allows retrieving NetworkInfo

+ 3
- 0
nova/objects/migration.py View File

@@ -176,6 +176,9 @@ class Migration(base.NovaPersistentObject, base.NovaObject,
176 176
     def instance(self, instance):
177 177
         self._cached_instance = instance
178 178
 
179
+    def is_same_host(self):
180
+        return self.source_compute == self.dest_compute
181
+
179 182
 
180 183
 @base.NovaObjectRegistry.register
181 184
 class MigrationList(base.ObjectListBase, base.NovaObject):

+ 6
- 2
nova/tests/unit/compute/test_compute.py View File

@@ -5941,7 +5941,9 @@ class ComputeTestCase(BaseTestCase,
5941 5941
             old_vm_state = vm_states.ACTIVE
5942 5942
         else:
5943 5943
             old_vm_state = vm_states.STOPPED
5944
-        params = {'vm_state': old_vm_state}
5944
+        params = {'vm_state': old_vm_state,
5945
+                  'info_cache': objects.InstanceInfoCache(
5946
+                      network_info=network_model.NetworkInfo([]))}
5945 5947
         instance = self._create_fake_instance_obj(params)
5946 5948
 
5947 5949
         self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
@@ -6091,7 +6093,9 @@ class ComputeTestCase(BaseTestCase,
6091 6093
         def fake(*args, **kwargs):
6092 6094
             pass
6093 6095
 
6094
-        instance = self._create_fake_instance_obj()
6096
+        params = {'info_cache': objects.InstanceInfoCache(
6097
+                      network_info=network_model.NetworkInfo([]))}
6098
+        instance = self._create_fake_instance_obj(params)
6095 6099
 
6096 6100
         self.stub_out('nova.virt.fake.FakeDriver.finish_migration', fake)
6097 6101
         self.stub_out('nova.virt.fake.FakeDriver.finish_revert_migration',

+ 91
- 0
nova/tests/unit/compute/test_compute_mgr.py View File

@@ -4936,6 +4936,97 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
4936 4936
                     self.context, fake_instance, fake_bdm)
4937 4937
         block_stats.assert_called_once_with(fake_instance, 'vda')
4938 4938
 
4939
+    def _test_finish_revert_resize_network_migrate_finish(
4940
+            self, vifs, events, migration=None):
4941
+        instance = fake_instance.fake_instance_obj(self.context)
4942
+        instance.info_cache = objects.InstanceInfoCache(
4943
+            network_info=network_model.NetworkInfo(vifs))
4944
+        if migration is None:
4945
+            migration = objects.Migration(
4946
+                source_compute='fake-source',
4947
+                dest_compute='fake-dest')
4948
+
4949
+        def fake_migrate_instance_finish(context, instance, migration):
4950
+            # NOTE(artom) This looks weird, but it's checking that the
4951
+            # temporaty_mutation() context manager did its job.
4952
+            self.assertEqual(migration.dest_compute, migration.source_compute)
4953
+
4954
+        with test.nested(
4955
+            mock.patch.object(self.compute.virtapi,
4956
+                              'wait_for_instance_event'),
4957
+            mock.patch.object(self.compute.network_api,
4958
+                              'migrate_instance_finish',
4959
+                              side_effect=fake_migrate_instance_finish)
4960
+        ) as (mock_wait, mock_migrate_instance_finish):
4961
+            self.compute._finish_revert_resize_network_migrate_finish(
4962
+                self.context, instance, migration)
4963
+            mock_wait.assert_called_once_with(
4964
+                instance, events, deadline=CONF.vif_plugging_timeout,
4965
+                error_callback=self.compute._neutron_failed_migration_callback)
4966
+            mock_migrate_instance_finish.assert_called_once_with(
4967
+                self.context, instance, migration)
4968
+
4969
+    def test_finish_revert_resize_network_migrate_finish_wait(self):
4970
+        """Test that we wait for bind-time events if we have a hybrid-plugged
4971
+        VIF.
4972
+        """
4973
+        self._test_finish_revert_resize_network_migrate_finish(
4974
+            [network_model.VIF(id=uuids.hybrid_vif,
4975
+                               details={'ovs_hybrid_plug': True}),
4976
+             network_model.VIF(id=uuids.normal_vif,
4977
+                               details={'ovs_hybrid_plug': False})],
4978
+            [('network-vif-plugged', uuids.hybrid_vif)])
4979
+
4980
+    def test_finish_revert_resize_network_migrate_finish_same_host(self):
4981
+        """Test that we're not waiting for any events if its a same host
4982
+        resize revert.
4983
+        """
4984
+        migration = objects.Migration(
4985
+            source_compute='fake-source', dest_compute='fake-source')
4986
+
4987
+        self._test_finish_revert_resize_network_migrate_finish(
4988
+            [network_model.VIF(id=uuids.hybrid_vif,
4989
+                               details={'ovs_hybrid_plug': True}),
4990
+             network_model.VIF(id=uuids.normal_vif,
4991
+                               details={'ovs_hybrid_plug': False})],
4992
+            [], migration=migration
4993
+        )
4994
+
4995
+    def test_finish_revert_resize_network_migrate_finish_dont_wait(self):
4996
+        """Test that we're not waiting for any events if we don't have any
4997
+        hybrid-plugged VIFs.
4998
+        """
4999
+        self._test_finish_revert_resize_network_migrate_finish(
5000
+            [network_model.VIF(id=uuids.hybrid_vif,
5001
+                               details={'ovs_hybrid_plug': False}),
5002
+             network_model.VIF(id=uuids.normal_vif,
5003
+                               details={'ovs_hybrid_plug': False})],
5004
+            [])
5005
+
5006
+    def test_finish_revert_resize_network_migrate_finish_no_vif_timeout(self):
5007
+        """Test that we're not waiting for any events if vif_plugging_timeout
5008
+        is 0.
5009
+        """
5010
+        self.flags(vif_plugging_timeout=0)
5011
+        self._test_finish_revert_resize_network_migrate_finish(
5012
+            [network_model.VIF(id=uuids.hybrid_vif,
5013
+                               details={'ovs_hybrid_plug': True}),
5014
+             network_model.VIF(id=uuids.normal_vif,
5015
+                               details={'ovs_hybrid_plug': True})],
5016
+            [])
5017
+
5018
+    @mock.patch.object(utils, 'is_neutron', return_value=False)
5019
+    def test_finish_revert_resize_network_migrate_finish_not_neutron(self, _):
5020
+        """Test that we're not waiting for any events if we're not using
5021
+        Neutron.
5022
+        """
5023
+        self._test_finish_revert_resize_network_migrate_finish(
5024
+            [network_model.VIF(id=uuids.hybrid_vif,
5025
+                               details={'ovs_hybrid_plug': True}),
5026
+             network_model.VIF(id=uuids.normal_vif,
5027
+                               details={'ovs_hybrid_plug': True})],
5028
+            [])
5029
+
4939 5030
 
4940 5031
 class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
4941 5032
     def setUp(self):

+ 30
- 0
nova/tests/unit/network/test_network_info.py View File

@@ -18,8 +18,10 @@ from oslo_config import cfg
18 18
 
19 19
 from nova import exception
20 20
 from nova.network import model
21
+from nova import objects
21 22
 from nova import test
22 23
 from nova.tests.unit import fake_network_cache_model
24
+from nova.tests import uuidsentinel as uuids
23 25
 from nova.virt import netutils
24 26
 
25 27
 
@@ -857,6 +859,34 @@ iface eth1 inet static
857 859
                 libvirt_virt_type='lxc')
858 860
         self.assertEqual(expected, template)
859 861
 
862
+    def test_get_events(self):
863
+        network_info = model.NetworkInfo([
864
+            model.VIF(
865
+                id=uuids.hybrid_vif,
866
+                details={'ovs_hybrid_plug': True}),
867
+            model.VIF(
868
+                id=uuids.normal_vif,
869
+                details={'ovs_hybrid_plug': False})])
870
+        same_host = objects.Migration(source_compute='fake-host',
871
+                                      dest_compute='fake-host')
872
+        diff_host = objects.Migration(source_compute='fake-host1',
873
+                                      dest_compute='fake-host2')
874
+        # Same-host migrations will have all events be plug-time.
875
+        self.assertItemsEqual(
876
+            [('network-vif-plugged', uuids.normal_vif),
877
+             ('network-vif-plugged', uuids.hybrid_vif)],
878
+            network_info.get_plug_time_events(same_host))
879
+        # Same host migration will have no plug-time events.
880
+        self.assertEqual([], network_info.get_bind_time_events(same_host))
881
+        # Diff-host migration + OVS hybrid plug = bind-time events
882
+        self.assertEqual(
883
+            [('network-vif-plugged', uuids.hybrid_vif)],
884
+            network_info.get_bind_time_events(diff_host))
885
+        # Diff-host migration + normal OVS = plug-time events
886
+        self.assertEqual(
887
+            [('network-vif-plugged', uuids.normal_vif)],
888
+            network_info.get_plug_time_events(diff_host))
889
+
860 890
 
861 891
 class TestNetworkMetadata(test.NoDBTestCase):
862 892
     def setUp(self):

+ 8
- 0
nova/tests/unit/objects/test_migration.py View File

@@ -276,6 +276,14 @@ class _TestMigrationObject(object):
276 276
         mig = objects.Migration.get_by_id(self.context, db_mig.id)
277 277
         self.assertEqual(uuid, mig.uuid)
278 278
 
279
+    def test_is_same_host(self):
280
+        same_host = objects.Migration(source_compute='fake-host',
281
+                                      dest_compute='fake-host')
282
+        diff_host = objects.Migration(source_compute='fake-host1',
283
+                                      dest_compute='fake-host2')
284
+        self.assertTrue(same_host.is_same_host())
285
+        self.assertFalse(diff_host.is_same_host())
286
+
279 287
 
280 288
 class TestMigrationObject(test_objects._LocalTest,
281 289
                           _TestMigrationObject):

+ 116
- 20
nova/tests/unit/virt/libvirt/test_driver.py View File

@@ -16916,8 +16916,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16916 16916
             self.assertEqual(0, domain.resume.call_count)
16917 16917
 
16918 16918
     def _test_create_with_network_events(self, neutron_failure=None,
16919
-                                         power_on=True):
16919
+                                         power_on=True, events=None):
16920 16920
         generated_events = []
16921
+        events_passed_to_prepare = []
16921 16922
 
16922 16923
         def wait_timeout():
16923 16924
             event = mock.MagicMock()
@@ -16935,6 +16936,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16935 16936
             m.event_name = '%s-%s' % (name, tag)
16936 16937
             m.wait.side_effect = wait_timeout
16937 16938
             generated_events.append(m)
16939
+            events_passed_to_prepare.append((name, tag))
16938 16940
             return m
16939 16941
 
16940 16942
         virtapi = manager.ComputeVirtAPI(mock.MagicMock())
@@ -16954,7 +16956,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16954 16956
         def test_create(cleanup, create, fw_driver, plug_vifs):
16955 16957
             domain = drvr._create_domain_and_network(self.context, 'xml',
16956 16958
                                                      instance, vifs,
16957
-                                                     power_on=power_on)
16959
+                                                     power_on=power_on,
16960
+                                                     external_events=events)
16958 16961
             plug_vifs.assert_called_with(instance, vifs)
16959 16962
 
16960 16963
             pause = self._get_pause_flag(drvr, vifs, power_on=power_on)
@@ -16969,7 +16972,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16969 16972
 
16970 16973
         test_create()
16971 16974
 
16972
-        if utils.is_neutron() and CONF.vif_plugging_timeout and power_on:
16975
+        if events and utils.is_neutron() and CONF.vif_plugging_timeout:
16976
+            self.assertEqual(events_passed_to_prepare, events)
16977
+        elif utils.is_neutron() and CONF.vif_plugging_timeout and power_on:
16973 16978
             prepare.assert_has_calls([
16974 16979
                 mock.call(instance, 'network-vif-plugged', uuids.vif_1),
16975 16980
                 mock.call(instance, 'network-vif-plugged', uuids.vif_2)])
@@ -16982,6 +16987,22 @@ class LibvirtConnTestCase(test.NoDBTestCase,
16982 16987
         else:
16983 16988
             self.assertEqual(0, prepare.call_count)
16984 16989
 
16990
+    @mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=True))
16991
+    def test_create_with_network_events_passed_in(self):
16992
+        self._test_create_with_network_events(
16993
+            events=[('network-vif-plugged', uuids.fake_vif)])
16994
+
16995
+    @mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=False))
16996
+    def test_create_with_network_events_passed_in_nova_net(self):
16997
+        self._test_create_with_network_events(
16998
+            events=[('network-vif-plugged', uuids.fake_vif)])
16999
+
17000
+    @mock.patch('nova.utils.is_neutron', new=mock.Mock(return_value=True))
17001
+    def test_create_with_network_events_passed_in_0_timeout(self):
17002
+        self.flags(vif_plugging_timeout=0)
17003
+        self._test_create_with_network_events(
17004
+            events=[('network-vif-plugged', uuids.fake_vif)])
17005
+
16985 17006
     @mock.patch('nova.utils.is_neutron', return_value=True)
16986 17007
     def test_create_with_network_events_neutron(self, is_neutron):
16987 17008
         self._test_create_with_network_events()
@@ -18926,7 +18947,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
18926 18947
     def test_finish_migration_power_off(self):
18927 18948
         self._test_finish_migration(power_on=False)
18928 18949
 
18929
-    def _test_finish_revert_migration(self, power_on):
18950
+    def _test_finish_revert_migration(self, power_on, migration):
18930 18951
         """Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
18931 18952
         .finish_revert_migration.
18932 18953
         """
@@ -18942,10 +18963,13 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
18942 18963
 
18943 18964
         def fake_create_domain(context, xml, instance, network_info,
18944 18965
                                block_device_info=None, power_on=None,
18945
-                               vifs_already_plugged=None):
18966
+                               vifs_already_plugged=None,
18967
+                               external_events=None):
18946 18968
             self.fake_create_domain_called = True
18947 18969
             self.assertEqual(powered_on, power_on)
18948 18970
             self.assertFalse(vifs_already_plugged)
18971
+            self.assertEqual(self.events_passed_to_fake_create,
18972
+                             external_events)
18949 18973
             return mock.MagicMock()
18950 18974
 
18951 18975
         def fake_enable_hairpin():
@@ -18979,6 +19003,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
18979 19003
         with utils.tempdir() as tmpdir:
18980 19004
             self.flags(instances_path=tmpdir)
18981 19005
             ins_ref = self._create_instance()
19006
+            ins_ref.migration_context = objects.MigrationContext(
19007
+                migration_id=migration.id)
18982 19008
             os.mkdir(os.path.join(tmpdir, ins_ref['name']))
18983 19009
             libvirt_xml_path = os.path.join(tmpdir,
18984 19010
                                             ins_ref['name'],
@@ -18986,16 +19012,50 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
18986 19012
             f = open(libvirt_xml_path, 'w')
18987 19013
             f.close()
18988 19014
 
18989
-            self.drvr.finish_revert_migration(
18990
-                                       context.get_admin_context(), ins_ref,
18991
-                                       [], None, power_on)
19015
+            network_info = network_model.NetworkInfo(
19016
+                [network_model.VIF(id=uuids.normal_vif),
19017
+                 network_model.VIF(id=uuids.hybrid_vif,
19018
+                                   details={'ovs_hybrid_plug': True})])
19019
+            if migration.is_same_host():
19020
+                # Same host is all plug-time
19021
+                self.events_passed_to_fake_create = [
19022
+                    ('network-vif-plugged', uuids.normal_vif),
19023
+                    ('network-vif-plugged', uuids.hybrid_vif)]
19024
+            else:
19025
+                # For different host migration only non-hybrid plug
19026
+                # ("normal") VIFs "emit" plug-time events.
19027
+                self.events_passed_to_fake_create = [
19028
+                    ('network-vif-plugged', uuids.normal_vif)]
19029
+
19030
+            with mock.patch.object(objects.Migration, 'get_by_id_and_instance',
19031
+                                   return_value=migration) as mock_get_mig:
19032
+                self.drvr.finish_revert_migration(
19033
+                    context.get_admin_context(), ins_ref,
19034
+                    network_info, None, power_on)
19035
+                mock_get_mig.assert_called_with(mock.ANY, migration.id,
19036
+                                                ins_ref.uuid)
19037
+
18992 19038
             self.assertTrue(self.fake_create_domain_called)
18993 19039
 
18994 19040
     def test_finish_revert_migration_power_on(self):
18995
-        self._test_finish_revert_migration(True)
19041
+        migration = objects.Migration(id=42, source_compute='fake-host1',
19042
+                                      dest_compute='fake-host2')
19043
+        self._test_finish_revert_migration(power_on=True, migration=migration)
18996 19044
 
18997 19045
     def test_finish_revert_migration_power_off(self):
18998
-        self._test_finish_revert_migration(False)
19046
+        migration = objects.Migration(id=42, source_compute='fake-host1',
19047
+                                      dest_compute='fake-host2')
19048
+        self._test_finish_revert_migration(power_on=False, migration=migration)
19049
+
19050
+    def test_finish_revert_migration_same_host(self):
19051
+        migration = objects.Migration(id=42, source_compute='fake-host',
19052
+                                      dest_compute='fake-host')
19053
+        self._test_finish_revert_migration(power_on=True, migration=migration)
19054
+
19055
+    def test_finish_revert_migration_diff_host(self):
19056
+        migration = objects.Migration(id=42, source_compute='fake-host1',
19057
+                                      dest_compute='fake-host2')
19058
+        self._test_finish_revert_migration(power_on=True, migration=migration)
18999 19059
 
19000 19060
     def _test_finish_revert_migration_after_crash(self, backup_made=True,
19001 19061
                                                   del_inst_failed=False):
@@ -19005,6 +19065,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
19005 19065
         drvr.image_backend.by_name.return_value = drvr.image_backend
19006 19066
         context = 'fake_context'
19007 19067
         ins_ref = self._create_instance()
19068
+        ins_ref.migration_context = objects.MigrationContext(
19069
+            migration_id=42)
19070
+        migration = objects.Migration(source_compute='fake-host1',
19071
+                                      dest_compute='fake-host2')
19008 19072
 
19009 19073
         with test.nested(
19010 19074
                 mock.patch.object(os.path, 'exists', return_value=backup_made),
@@ -19014,13 +19078,17 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
19014 19078
                 mock.patch.object(drvr, '_get_guest_xml'),
19015 19079
                 mock.patch.object(shutil, 'rmtree'),
19016 19080
                 mock.patch.object(loopingcall, 'FixedIntervalLoopingCall'),
19081
+                mock.patch.object(objects.Migration, 'get_by_id_and_instance',
19082
+                                  return_value=migration)
19017 19083
         ) as (mock_stat, mock_path, mock_rename, mock_cdn, mock_ggx,
19018
-              mock_rmtree, mock_looping_call):
19084
+              mock_rmtree, mock_looping_call, mock_get_mig):
19019 19085
             mock_path.return_value = '/fake/foo'
19020 19086
             if del_inst_failed:
19021 19087
                 mock_rmtree.side_effect = OSError(errno.ENOENT,
19022 19088
                                                   'test exception')
19023
-            drvr.finish_revert_migration(context, ins_ref, [])
19089
+            drvr.finish_revert_migration(context, ins_ref,
19090
+                                         network_model.NetworkInfo())
19091
+            mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
19024 19092
             if backup_made:
19025 19093
                 mock_rename.assert_called_once_with('/fake/foo_resize',
19026 19094
                                                     '/fake/foo')
@@ -19049,6 +19117,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
19049 19117
         image_meta = {"disk_format": "raw",
19050 19118
                       "properties": {"hw_disk_bus": "ide"}}
19051 19119
         instance = self._create_instance()
19120
+        instance.migration_context = objects.MigrationContext(
19121
+            migration_id=42)
19122
+        migration = objects.Migration(source_compute='fake-host1',
19123
+                                      dest_compute='fake-host2')
19052 19124
 
19053 19125
         drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19054 19126
 
@@ -19058,22 +19130,37 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
19058 19130
                 mock.patch.object(utils, 'get_image_from_system_metadata',
19059 19131
                                   return_value=image_meta),
19060 19132
                 mock.patch.object(drvr, '_get_guest_xml',
19061
-                                  side_effect=fake_get_guest_xml)):
19062
-            drvr.finish_revert_migration('', instance, None, power_on=False)
19133
+                                  side_effect=fake_get_guest_xml),
19134
+                mock.patch.object(objects.Migration, 'get_by_id_and_instance',
19135
+                                  return_value=migration)
19136
+        ) as (mock_img_bkend, mock_cdan, mock_gifsm, mock_ggxml, mock_get_mig):
19137
+            drvr.finish_revert_migration('', instance,
19138
+                                         network_model.NetworkInfo(),
19139
+                                         power_on=False)
19140
+            mock_get_mig.assert_called_with(mock.ANY, 42, instance.uuid)
19063 19141
 
19064 19142
     def test_finish_revert_migration_snap_backend(self):
19065 19143
         drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
19066 19144
         drvr.image_backend = mock.Mock()
19067 19145
         drvr.image_backend.by_name.return_value = drvr.image_backend
19068 19146
         ins_ref = self._create_instance()
19147
+        ins_ref.migration_context = objects.MigrationContext(
19148
+            migration_id=42)
19149
+        migration = objects.Migration(source_compute='fake-host1',
19150
+                                      dest_compute='fake-host2')
19069 19151
 
19070 19152
         with test.nested(
19071 19153
                 mock.patch.object(utils, 'get_image_from_system_metadata'),
19072 19154
                 mock.patch.object(drvr, '_create_domain_and_network'),
19073
-                mock.patch.object(drvr, '_get_guest_xml')) as (
19074
-                mock_image, mock_cdn, mock_ggx):
19155
+                mock.patch.object(drvr, '_get_guest_xml'),
19156
+                mock.patch.object(objects.Migration, 'get_by_id_and_instance',
19157
+                                  return_value=migration)) as (
19158
+                mock_image, mock_cdn, mock_ggx, mock_get_mig):
19075 19159
             mock_image.return_value = {'disk_format': 'raw'}
19076
-            drvr.finish_revert_migration('', ins_ref, None, power_on=False)
19160
+            drvr.finish_revert_migration('', ins_ref,
19161
+                                         network_model.NetworkInfo(),
19162
+                                         power_on=False)
19163
+            mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
19077 19164
 
19078 19165
             drvr.image_backend.rollback_to_snap.assert_called_once_with(
19079 19166
                     libvirt_utils.RESIZE_SNAPSHOT_NAME)
@@ -19105,17 +19192,26 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
19105 19192
         drvr.image_backend.by_name.return_value = drvr.image_backend
19106 19193
         drvr.image_backend.exists.return_value = False
19107 19194
         ins_ref = self._create_instance()
19195
+        ins_ref.migration_context = objects.MigrationContext(
19196
+            migration_id=42)
19197
+        migration = objects.Migration(source_compute='fake-host1',
19198
+                                      dest_compute='fake-host2')
19108 19199
 
19109 19200
         with test.nested(
19110 19201
                 mock.patch.object(rbd_utils, 'RBDDriver'),
19111 19202
                 mock.patch.object(utils, 'get_image_from_system_metadata'),
19112 19203
                 mock.patch.object(drvr, '_create_domain_and_network'),
19113
-                mock.patch.object(drvr, '_get_guest_xml')) as (
19114
-                mock_rbd, mock_image, mock_cdn, mock_ggx):
19204
+                mock.patch.object(drvr, '_get_guest_xml'),
19205
+                mock.patch.object(objects.Migration, 'get_by_id_and_instance',
19206
+                                  return_value=migration)) as (
19207
+                mock_rbd, mock_image, mock_cdn, mock_ggx, mock_get_mig):
19115 19208
             mock_image.return_value = {'disk_format': 'raw'}
19116
-            drvr.finish_revert_migration('', ins_ref, None, power_on=False)
19209
+            drvr.finish_revert_migration('', ins_ref,
19210
+                                         network_model.NetworkInfo(),
19211
+                                         power_on=False)
19117 19212
             self.assertFalse(drvr.image_backend.rollback_to_snap.called)
19118 19213
             self.assertFalse(drvr.image_backend.remove_snap.called)
19214
+            mock_get_mig.assert_called_with(mock.ANY, 42, ins_ref.uuid)
19119 19215
 
19120 19216
     def test_cleanup_failed_migration(self):
19121 19217
         self.mox.StubOutWithMock(shutil, 'rmtree')

+ 24
- 8
nova/virt/libvirt/driver.py View File

@@ -5639,14 +5639,15 @@ class LibvirtDriver(driver.ComputeDriver):
5639 5639
                                    block_device_info=None, power_on=True,
5640 5640
                                    vifs_already_plugged=False,
5641 5641
                                    post_xml_callback=None,
5642
-                                   destroy_disks_on_failure=False):
5642
+                                   destroy_disks_on_failure=False,
5643
+                                   external_events=None):
5643 5644
 
5644 5645
         """Do required network setup and create domain."""
5645 5646
         timeout = CONF.vif_plugging_timeout
5646
-        if (self._conn_supports_start_paused and
5647
-            utils.is_neutron() and not
5648
-            vifs_already_plugged and power_on and timeout):
5649
-            events = self._get_neutron_events(network_info)
5647
+        if (self._conn_supports_start_paused and utils.is_neutron() and not
5648
+                vifs_already_plugged and power_on and timeout):
5649
+            events = (external_events if external_events
5650
+                      else self._get_neutron_events(network_info))
5650 5651
         else:
5651 5652
             events = []
5652 5653
 
@@ -8542,9 +8543,24 @@ class LibvirtDriver(driver.ComputeDriver):
8542 8543
         xml = self._get_guest_xml(context, instance, network_info, disk_info,
8543 8544
                                   instance.image_meta,
8544 8545
                                   block_device_info=block_device_info)
8545
-        self._create_domain_and_network(context, xml, instance, network_info,
8546
-                                        block_device_info=block_device_info,
8547
-                                        power_on=power_on)
8546
+        # NOTE(artom) In some Neutron or port configurations we've already
8547
+        # waited for vif-plugged events in the compute manager's
8548
+        # _finish_revert_resize_network_migrate_finish(), right after updating
8549
+        # the port binding. For any ports not covered by those "bind-time"
8550
+        # events, we wait for "plug-time" events here.
8551
+        # TODO(artom) This DB lookup is done for backportability. A subsequent
8552
+        # patch will remove it and change the finish_revert_migration() method
8553
+        # signature to pass is the migration object.
8554
+        migration = objects.Migration.get_by_id_and_instance(
8555
+            context, instance.migration_context.migration_id, instance.uuid)
8556
+        events = network_info.get_plug_time_events(migration)
8557
+        if events:
8558
+            LOG.debug('Instance is using plug-time events: %s', events,
8559
+                      instance=instance)
8560
+        self._create_domain_and_network(
8561
+            context, xml, instance, network_info,
8562
+            block_device_info=block_device_info, power_on=power_on,
8563
+            external_events=events)
8548 8564
 
8549 8565
         if power_on:
8550 8566
             timer = loopingcall.FixedIntervalLoopingCall(

Loading…
Cancel
Save