Browse Source

Merge "update allocation in binding profile during migrate"

changes/90/614190/15
Zuul 2 weeks ago
parent
commit
a2b814619b

+ 21
- 16
nova/compute/manager.py View File

@@ -4203,8 +4203,8 @@ class ComputeManager(manager.Manager):
4203 4203
             self.compute_rpcapi.finish_revert_resize(context, instance,
4204 4204
                     migration, migration.source_compute, request_spec)
4205 4205
 
4206
-    def _finish_revert_resize_network_migrate_finish(self, context, instance,
4207
-                                                     migration):
4206
+    def _finish_revert_resize_network_migrate_finish(
4207
+            self, context, instance, migration, provider_mappings):
4208 4208
         """Causes port binding to be updated. In some Neutron or port
4209 4209
         configurations - see NetworkModel.get_bind_time_events() - we
4210 4210
         expect the vif-plugged event from Neutron immediately and wait for it.
@@ -4214,6 +4214,8 @@ class ComputeManager(manager.Manager):
4214 4214
         :param context: The request context.
4215 4215
         :param instance: The instance undergoing the revert resize.
4216 4216
         :param migration: The Migration object of the resize being reverted.
4217
+        :param provider_mappings: a dict of list of resource provider uuids
4218
+            keyed by port uuid
4217 4219
         :raises: eventlet.timeout.Timeout or
4218 4220
                  exception.VirtualInterfacePlugException.
4219 4221
         """
@@ -4238,9 +4240,8 @@ class ComputeManager(manager.Manager):
4238 4240
                 # the migration.dest_compute to source host at here.
4239 4241
                 with utils.temporary_mutation(
4240 4242
                         migration, dest_compute=migration.source_compute):
4241
-                    self.network_api.migrate_instance_finish(context,
4242
-                                                             instance,
4243
-                                                             migration)
4243
+                    self.network_api.migrate_instance_finish(
4244
+                        context, instance, migration, provider_mappings)
4244 4245
         except eventlet.timeout.Timeout:
4245 4246
             with excutils.save_and_reraise_exception():
4246 4247
                 LOG.error('Timeout waiting for Neutron events: %s', events,
@@ -4292,10 +4293,12 @@ class ComputeManager(manager.Manager):
4292 4293
                            'migration_uuid': migration.uuid})
4293 4294
                 raise
4294 4295
 
4296
+            provider_mappings = self._get_request_group_mapping(request_spec)
4297
+
4295 4298
             self.network_api.setup_networks_on_host(context, instance,
4296 4299
                                                     migration.source_compute)
4297 4300
             self._finish_revert_resize_network_migrate_finish(
4298
-                context, instance, migration)
4301
+                context, instance, migration, provider_mappings)
4299 4302
             network_info = self.network_api.get_instance_nw_info(context,
4300 4303
                                                                  instance)
4301 4304
 
@@ -4746,7 +4749,7 @@ class ComputeManager(manager.Manager):
4746 4749
                         context, bdm.attachment_id)
4747 4750
 
4748 4751
     def _finish_resize(self, context, instance, migration, disk_info,
4749
-                       image_meta, bdms):
4752
+                       image_meta, bdms, request_spec):
4750 4753
         resize_instance = False  # indicates disks have been resized
4751 4754
         old_instance_type_id = migration['old_instance_type_id']
4752 4755
         new_instance_type_id = migration['new_instance_type_id']
@@ -4772,11 +4775,12 @@ class ComputeManager(manager.Manager):
4772 4775
         # NOTE(tr3buchet): setup networks on destination host
4773 4776
         self.network_api.setup_networks_on_host(context, instance,
4774 4777
                                                 migration.dest_compute)
4778
+        provider_mappings = self._get_request_group_mapping(request_spec)
4779
+
4775 4780
         # For neutron, migrate_instance_finish updates port bindings for this
4776 4781
         # host including any PCI devices claimed for SR-IOV ports.
4777
-        self.network_api.migrate_instance_finish(context,
4778
-                                                 instance,
4779
-                                                 migration)
4782
+        self.network_api.migrate_instance_finish(
4783
+            context, instance, migration, provider_mappings)
4780 4784
 
4781 4785
         network_info = self.network_api.get_instance_nw_info(context, instance)
4782 4786
 
@@ -4850,7 +4854,7 @@ class ComputeManager(manager.Manager):
4850 4854
         """
4851 4855
         try:
4852 4856
             self._finish_resize_helper(context, disk_info, image, instance,
4853
-                                       migration)
4857
+                                       migration, request_spec)
4854 4858
         except Exception:
4855 4859
             with excutils.save_and_reraise_exception():
4856 4860
                 # At this point, resize_instance (which runs on the source) has
@@ -4871,7 +4875,7 @@ class ComputeManager(manager.Manager):
4871 4875
                     context, instance, migration)
4872 4876
 
4873 4877
     def _finish_resize_helper(self, context, disk_info, image, instance,
4874
-                              migration):
4878
+                              migration, request_spec):
4875 4879
         """Completes the migration process.
4876 4880
 
4877 4881
         The caller must revert the instance's allocations if the migration
@@ -4883,7 +4887,8 @@ class ComputeManager(manager.Manager):
4883 4887
         with self._error_out_instance_on_exception(context, instance):
4884 4888
             image_meta = objects.ImageMeta.from_dict(image)
4885 4889
             network_info = self._finish_resize(context, instance, migration,
4886
-                                               disk_info, image_meta, bdms)
4890
+                                               disk_info, image_meta, bdms,
4891
+                                               request_spec)
4887 4892
 
4888 4893
         # TODO(melwitt): We should clean up instance console tokens here. The
4889 4894
         # instance is on a new host and will need to establish a new console
@@ -7242,9 +7247,9 @@ class ComputeManager(manager.Manager):
7242 7247
         migration = {'source_compute': instance.host,
7243 7248
                      'dest_compute': self.host,
7244 7249
                      'migration_type': 'live-migration'}
7245
-        self.network_api.migrate_instance_finish(context,
7246
-                                                 instance,
7247
-                                                 migration)
7250
+        # TODO(gibi): calculate and pass resource_provider_mapping
7251
+        self.network_api.migrate_instance_finish(
7252
+            context, instance, migration, provider_mappings=None)
7248 7253
 
7249 7254
         network_info = self.network_api.get_instance_nw_info(context, instance)
7250 7255
         self._notify_about_instance_usage(

+ 5
- 4
nova/network/api.py View File

@@ -502,7 +502,8 @@ class API(base_api.NetworkAPI):
502 502
 
503 503
         self.network_rpcapi.migrate_instance_start(context, **args)
504 504
 
505
-    def migrate_instance_finish(self, context, instance, migration):
505
+    def migrate_instance_finish(
506
+            self, context, instance, migration, provider_mappings):
506 507
         """Finish migrating the network of an instance."""
507 508
         flavor = instance.get_flavor()
508 509
         args = dict(
@@ -524,9 +525,9 @@ class API(base_api.NetworkAPI):
524 525
     def setup_instance_network_on_host(self, context, instance, host,
525 526
                                        migration=None):
526 527
         """Setup network for specified instance on host."""
527
-        self.migrate_instance_finish(context, instance,
528
-                                     {'source_compute': None,
529
-                                      'dest_compute': host})
528
+        self.migrate_instance_finish(
529
+            context, instance, {'source_compute': None, 'dest_compute': host},
530
+            None)
530 531
 
531 532
     def cleanup_instance_network_on_host(self, context, instance, host):
532 533
         """Cleanup network for specified instance on host."""

+ 10
- 2
nova/network/base_api.py View File

@@ -343,8 +343,16 @@ class NetworkAPI(base.Base):
343 343
         """Start to migrate the network of an instance."""
344 344
         raise NotImplementedError()
345 345
 
346
-    def migrate_instance_finish(self, context, instance, migration):
347
-        """Finish migrating the network of an instance."""
346
+    def migrate_instance_finish(
347
+            self, context, instance, migration, provider_mappings):
348
+        """Finish migrating the network of an instance.
349
+
350
+        :param context: The request context.
351
+        :param instance: nova.objects.instance.Instance object.
352
+        :param migration: nova.objects.migration.Migration object.
353
+        :param provider_mappings: a dict of list of resource provider uuids
354
+            keyed by port uuid
355
+        """
348 356
         raise NotImplementedError()
349 357
 
350 358
     def setup_instance_network_on_host(self, context, instance, host,

+ 31
- 9
nova/network/neutronv2/api.py View File

@@ -2774,11 +2774,12 @@ class API(base_api.NetworkAPI):
2774 2774
                           'Error: %s', vif['id'], dest_host, resp.status_code,
2775 2775
                           resp.text)
2776 2776
 
2777
-    def migrate_instance_finish(self, context, instance, migration):
2777
+    def migrate_instance_finish(
2778
+            self, context, instance, migration, provider_mappings):
2778 2779
         """Finish migrating the network of an instance."""
2779
-        self._update_port_binding_for_instance(context, instance,
2780
-                                               migration['dest_compute'],
2781
-                                               migration=migration)
2780
+        self._update_port_binding_for_instance(
2781
+            context, instance, migration['dest_compute'], migration=migration,
2782
+            provider_mappings=provider_mappings)
2782 2783
 
2783 2784
     def add_network_to_project(self, context, project_id, network_uuid=None):
2784 2785
         """Force add a network to the project."""
@@ -3249,8 +3250,10 @@ class API(base_api.NetworkAPI):
3249 3250
                   migration.get('status') == 'reverted')
3250 3251
         return instance.migration_context.get_pci_mapping_for_migration(revert)
3251 3252
 
3252
-    def _update_port_binding_for_instance(self, context, instance, host,
3253
-                                          migration=None):
3253
+    def _update_port_binding_for_instance(
3254
+            self, context, instance, host, migration=None,
3255
+            provider_mappings=None):
3256
+
3254 3257
         neutron = get_client(context, admin=True)
3255 3258
         search_opts = {'device_id': instance.uuid,
3256 3259
                        'tenant_id': instance.project_id}
@@ -3269,9 +3272,6 @@ class API(base_api.NetworkAPI):
3269 3272
             vif_type = p.get('binding:vif_type')
3270 3273
             if (p.get(constants.BINDING_HOST_ID) != host or
3271 3274
                     vif_type in FAILED_VIF_TYPES):
3272
-                # TODO(gibi): To support ports with resource request during
3273
-                # server move operations we need to take care of 'allocation'
3274
-                # key in the binding profile per binding.
3275 3275
 
3276 3276
                 updates[constants.BINDING_HOST_ID] = host
3277 3277
                 # If the host changed, the AZ could have also changed so we
@@ -3311,6 +3311,28 @@ class API(base_api.NetworkAPI):
3311 3311
                         reason=_("Unable to correlate PCI slot %s") %
3312 3312
                                  pci_slot)
3313 3313
 
3314
+            if p.get('resource_request'):
3315
+                if not provider_mappings:
3316
+                    # NOTE(gibi): This should not happen as the API level
3317
+                    # minimum compute service version check ensures that the
3318
+                    # compute services already send the RequestSpec during
3319
+                    # the move operations between the source and the
3320
+                    # destination and the dest compute calculates the
3321
+                    # mapping based on that.
3322
+                    raise exception.PortUpdateFailed(
3323
+                        port_id=p['id'],
3324
+                        reason=_("Provider mappings wasn't provided for the "
3325
+                                 "port with resource request"))
3326
+
3327
+                # NOTE(gibi): In the resource provider mapping there can be
3328
+                # more than one RP fulfilling a request group. But resource
3329
+                # requests of a Neutron port is always mapped to a
3330
+                # numbered request group that is always fulfilled by one
3331
+                # resource provider. So we only pass that single RP UUID here.
3332
+                binding_profile[constants.ALLOCATION] = \
3333
+                    provider_mappings[p['id']][0]
3334
+                updates[constants.BINDING_PROFILE] = binding_profile
3335
+
3314 3336
             port_updates.append((p['id'], updates))
3315 3337
 
3316 3338
         # Avoid rolling back updates if we catch an error above.

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

@@ -4866,7 +4866,7 @@ class ComputeTestCase(BaseTestCase,
4866 4866
             mock_setup.assert_called_once_with(self.context, instance,
4867 4867
                                                'fake-mini')
4868 4868
             mock_net_mig.assert_called_once_with(self.context,
4869
-                test.MatchType(objects.Instance), migration)
4869
+                test.MatchType(objects.Instance), migration, None)
4870 4870
             mock_get_nw.assert_called_once_with(self.context, instance)
4871 4871
             mock_notify.assert_has_calls([
4872 4872
                 mock.call(self.context, instance, 'finish_resize.start',

+ 8
- 5
nova/tests/unit/compute/test_compute_mgr.py View File

@@ -5263,7 +5263,8 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
5263 5263
                 source_compute='fake-source',
5264 5264
                 dest_compute='fake-dest')
5265 5265
 
5266
-        def fake_migrate_instance_finish(context, instance, migration):
5266
+        def fake_migrate_instance_finish(
5267
+                context, instance, migration, mapping):
5267 5268
             # NOTE(artom) This looks weird, but it's checking that the
5268 5269
             # temporaty_mutation() context manager did its job.
5269 5270
             self.assertEqual(migration.dest_compute, migration.source_compute)
@@ -5276,12 +5277,12 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
5276 5277
                               side_effect=fake_migrate_instance_finish)
5277 5278
         ) as (mock_wait, mock_migrate_instance_finish):
5278 5279
             self.compute._finish_revert_resize_network_migrate_finish(
5279
-                self.context, instance, migration)
5280
+                self.context, instance, migration, mock.sentinel.mapping)
5280 5281
             mock_wait.assert_called_once_with(
5281 5282
                 instance, events, deadline=CONF.vif_plugging_timeout,
5282 5283
                 error_callback=self.compute._neutron_failed_migration_callback)
5283 5284
             mock_migrate_instance_finish.assert_called_once_with(
5284
-                self.context, instance, migration)
5285
+                self.context, instance, migration, mock.sentinel.mapping)
5285 5286
 
5286 5287
     def test_finish_revert_resize_network_migrate_finish_wait(self):
5287 5288
         """Test that we wait for bind-time events if we have a hybrid-plugged
@@ -7410,7 +7411,8 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase,
7410 7411
     def test_finish_revert_resize_network_calls_order(self):
7411 7412
         self.nw_info = None
7412 7413
 
7413
-        def _migrate_instance_finish(context, instance, migration):
7414
+        def _migrate_instance_finish(
7415
+                context, instance, migration, provider_mappings):
7414 7416
             # The migration.dest_compute is temporarily set to source_compute.
7415 7417
             self.assertEqual(migration.source_compute, migration.dest_compute)
7416 7418
             self.nw_info = 'nw_info'
@@ -8459,7 +8461,8 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase,
8459 8461
                 self.context, self.instance,
8460 8462
                 {'source_compute': cn_old,
8461 8463
                  'dest_compute': self.compute.host,
8462
-                 'migration_type': 'live-migration'})
8464
+                 'migration_type': 'live-migration'},
8465
+                provider_mappings=None)
8463 8466
             _get_instance_block_device_info.assert_called_once_with(
8464 8467
                 self.context, self.instance
8465 8468
             )

+ 4
- 3
nova/tests/unit/network/test_api.py View File

@@ -279,14 +279,14 @@ class ApiTestCase(test.TestCase):
279 279
         arg1, arg2, expected = self._stub_migrate_instance_calls(
280 280
                 'migrate_instance_finish', True, info)
281 281
         expected['host'] = 'fake_compute_dest'
282
-        self.network_api.migrate_instance_finish(self.context, arg1, arg2)
282
+        self.network_api.migrate_instance_finish(self.context, arg1, arg2, {})
283 283
         self.assertEqual(info['kwargs'], expected)
284 284
 
285 285
     def test_migrate_instance_finish_without_multihost(self):
286 286
         info = {'kwargs': {}}
287 287
         arg1, arg2, expected = self._stub_migrate_instance_calls(
288 288
                 'migrate_instance_finish', False, info)
289
-        self.network_api.migrate_instance_finish(self.context, arg1, arg2)
289
+        self.network_api.migrate_instance_finish(self.context, arg1, arg2, {})
290 290
         self.assertEqual(info['kwargs'], expected)
291 291
 
292 292
     def test_is_multi_host_instance_has_no_fixed_ip(self):
@@ -516,7 +516,8 @@ class ApiTestCase(test.TestCase):
516 516
             self.context, instance, 'fake_compute_source')
517 517
         fake_migrate_finish.assert_called_once_with(
518 518
             self.context, instance,
519
-            {'source_compute': None, 'dest_compute': 'fake_compute_source'})
519
+            {'source_compute': None, 'dest_compute': 'fake_compute_source'},
520
+            None)
520 521
 
521 522
     @mock.patch('oslo_concurrency.lockutils.lock')
522 523
     @mock.patch.object(api.API, '_get_instance_nw_info')

+ 55
- 2
nova/tests/unit/network/test_neutronv2.py View File

@@ -4531,6 +4531,57 @@ class TestNeutronv2WithMock(TestNeutronv2Base):
4531 4531
                 constants.BINDING_HOST_ID],
4532 4532
             'new-host')
4533 4533
 
4534
+    @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
4535
+    def test_update_port_bindings_for_instance_with_resource_req(
4536
+            self, get_client_mock):
4537
+
4538
+        instance = fake_instance.fake_instance_obj(self.context)
4539
+        fake_ports = {'ports': [
4540
+            {'id': 'fake-port-1',
4541
+             'binding:vnic_type': 'normal',
4542
+             constants.BINDING_HOST_ID: 'old-host',
4543
+             constants.BINDING_PROFILE:
4544
+                 {'allocation': uuids.source_compute_rp},
4545
+             'resource_request': mock.sentinel.resource_request}]}
4546
+        migration = {'status': 'confirmed',
4547
+                     'migration_type': "migration"}
4548
+        list_ports_mock = mock.Mock(return_value=fake_ports)
4549
+        get_client_mock.return_value.list_ports = list_ports_mock
4550
+
4551
+        self.api._update_port_binding_for_instance(
4552
+            self.context, instance, 'new-host', migration,
4553
+            {'fake-port-1': [uuids.dest_compute_rp]})
4554
+        get_client_mock.return_value.update_port.assert_called_once_with(
4555
+            'fake-port-1',
4556
+            {'port': {'device_owner': 'compute:None',
4557
+                      'binding:profile': {'allocation': uuids.dest_compute_rp},
4558
+                      'binding:host_id': 'new-host'}})
4559
+
4560
+    @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock())
4561
+    def test_update_port_bindings_for_instance_with_resource_req_no_mapping(
4562
+            self, get_client_mock):
4563
+
4564
+        instance = fake_instance.fake_instance_obj(self.context)
4565
+        fake_ports = {'ports': [
4566
+            {'id': 'fake-port-1',
4567
+             'binding:vnic_type': 'normal',
4568
+             constants.BINDING_HOST_ID: 'old-host',
4569
+             constants.BINDING_PROFILE:
4570
+                 {'allocation': uuids.source_compute_rp},
4571
+             'resource_request': mock.sentinel.resource_request}]}
4572
+        migration = {'status': 'confirmed',
4573
+                     'migration_type': "migration"}
4574
+        list_ports_mock = mock.Mock(return_value=fake_ports)
4575
+        get_client_mock.return_value.list_ports = list_ports_mock
4576
+
4577
+        ex = self.assertRaises(
4578
+            exception.PortUpdateFailed,
4579
+            self.api._update_port_binding_for_instance, self.context,
4580
+            instance, 'new-host', migration, provider_mappings=None)
4581
+        self.assertIn(
4582
+            "Provider mappings wasn't provided for the port with resource "
4583
+            "request", six.text_type(ex))
4584
+
4534 4585
     def test_get_pci_mapping_for_migration(self):
4535 4586
         instance = fake_instance.fake_instance_obj(self.context)
4536 4587
         instance.migration_context = objects.MigrationContext()
@@ -6262,7 +6313,8 @@ class TestNeutronv2Portbinding(TestNeutronv2Base):
6262 6313
                                             'migrate_instance_finish',
6263 6314
                                             self.context,
6264 6315
                                             instance,
6265
-                                            migration)
6316
+                                            migration,
6317
+                                            {})
6266 6318
 
6267 6319
     def test_migrate_instance_finish_binding_true_exception(self):
6268 6320
         migration = {'source_compute': self.instance.get('host'),
@@ -6272,7 +6324,8 @@ class TestNeutronv2Portbinding(TestNeutronv2Base):
6272 6324
                                               'migrate_instance_finish',
6273 6325
                                               self.context,
6274 6326
                                               instance,
6275
-                                              migration)
6327
+                                              migration,
6328
+                                              {})
6276 6329
 
6277 6330
     def test_setup_instance_network_on_host_true(self):
6278 6331
         instance = self._fake_instance_object(self.instance)

Loading…
Cancel
Save