diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 5e0b22477827..23eb382bfe96 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -2520,6 +2520,13 @@ class API(base_api.NetworkAPI): def cleanup_instance_network_on_host(self, context, instance, host): """Cleanup network for specified instance on host.""" + # TODO(mriedem): This should likely be implemented at least for the + # shelve offload operation because the instance is being removed from + # a compute host, VIFs are unplugged, etc, so the ports should also + # be unbound, albeit still logically attached to the instance (for the + # shelve scenario). If _unbind_ports was going to be leveraged here, it + # would have to be adjusted a bit since it currently clears the + # device_id field on the port which is not what we'd want for shelve. pass def _get_pci_devices_from_migration_context(self, migration_context, @@ -2579,6 +2586,10 @@ class API(base_api.NetworkAPI): # same host, there is nothing to do. if p.get(BINDING_HOST_ID) != host: updates[BINDING_HOST_ID] = host + # If the host changed, the AZ could have also changed so we + # need to update the device_owner. + updates['device_owner'] = ( + 'compute:%s' % instance.availability_zone) # NOTE: Before updating the port binding make sure we # remove the pre-migration status from the binding profile if binding_profile.get(MIGRATING_ATTR): diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index a39351269b69..4b119c091946 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -3932,6 +3932,9 @@ class TestNeutronv2WithMock(test.TestCase): # removed since it does not match with the current host. update_port_mock.assert_called_once_with( 'fake-port-1', {'port': {neutronapi.BINDING_HOST_ID: 'my-host', + 'device_owner': + 'compute:%s' % + instance.availability_zone, neutronapi.BINDING_PROFILE: { 'fake_profile': 'fake_data'}}}) @@ -3958,7 +3961,10 @@ class TestNeutronv2WithMock(test.TestCase): # Assert that update_port was called on the port with a # different host but with no binding profile. update_port_mock.assert_called_once_with( - uuids.portid, {'port': {neutronapi.BINDING_HOST_ID: 'my-host'}}) + uuids.portid, {'port': {neutronapi.BINDING_HOST_ID: 'my-host', + 'device_owner': + 'compute:%s' % + instance.availability_zone}}) @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) def test_update_port_bindings_for_instance_same_host(self, @@ -3981,7 +3987,9 @@ class TestNeutronv2WithMock(test.TestCase): # Assert that update_port was only called on the port without a host. update_port_mock.assert_called_once_with( 'fake-port-2', - {'port': {neutronapi.BINDING_HOST_ID: instance.host}}) + {'port': {neutronapi.BINDING_HOST_ID: instance.host, + 'device_owner': 'compute:%s' % + instance.availability_zone}}) @mock.patch.object(pci_whitelist.Whitelist, 'get_devspec') @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) @@ -4035,6 +4043,7 @@ class TestNeutronv2WithMock(test.TestCase): 'fake-port-1', {'port': {neutronapi.BINDING_HOST_ID: 'fake-host', + 'device_owner': 'compute:%s' % instance.availability_zone, neutronapi.BINDING_PROFILE: {'pci_slot': '0000:0b:00.1', 'physical_network': 'physnet1', @@ -5260,7 +5269,9 @@ class TestNeutronv2Portbinding(TestNeutronv2Base): ports = {'ports': [{'id': 'test1'}]} self.moxed_client.list_ports(**search_opts).AndReturn(ports) port_req_body = {'port': - {neutronapi.BINDING_HOST_ID: expected_bind_host}} + {neutronapi.BINDING_HOST_ID: expected_bind_host, + 'device_owner': 'compute:%s' % + self.instance['availability_zone']}} self.moxed_client.update_port('test1', port_req_body).AndReturn(None) self.mox.ReplayAll()