diff --git a/nova/network/neutron.py b/nova/network/neutron.py index 3160846a3b77..33ad71306a51 100644 --- a/nova/network/neutron.py +++ b/nova/network/neutron.py @@ -710,7 +710,7 @@ class API: # dns_domain), or if we cannot retrieve network info, we use the # admin client to reset dns_name. if ( - self.has_dns_extension(client=port_client) and + detach and self.has_dns_extension(client=port_client) and not network.get('dns_domain') ): port_req_body['port']['dns_name'] = '' @@ -726,7 +726,8 @@ class API: # NOTE: For external DNS integration, we use the neutron client # with user's context to reset the dns_name since the recordset is # under user's zone. - self._reset_port_dns_name(network, port_id, neutron) + if detach: + self._reset_port_dns_name(network, port_id, neutron) def _validate_requested_port_ids(self, context, instance, neutron, requested_networks): diff --git a/nova/tests/functional/test_servers.py b/nova/tests/functional/test_servers.py index 705c1f3cda18..cf8df5bb761a 100644 --- a/nova/tests/functional/test_servers.py +++ b/nova/tests/functional/test_servers.py @@ -37,6 +37,7 @@ from nova.compute import manager as compute_manager from nova.compute import rpcapi as compute_rpcapi from nova import context from nova import exception +from nova.network import constants as neutron_constants from nova.network import neutron as neutronapi from nova import objects from nova.objects import block_device as block_device_obj @@ -6784,6 +6785,21 @@ class PortBindingShelvedServerTest(integrated_helpers._IntegratedTestBase): super(PortBindingShelvedServerTest, self).setUp() self.flavor_id = self._create_flavor( disk=10, ephemeral=20, swap=5 * 1024) + self.neutron.list_extensions = self.list_extensions + + def list_extensions(self, *args, **kwargs): + return { + 'extensions': [ + { + # Copied from neutron-lib dns.py + "updated": "2015-08-15T18:00:00-00:00", + "name": neutron_constants.DNS_INTEGRATION, + "links": [], + "alias": "dns-integration", + "description": "Provides integration with DNS." + } + ] + } def test_shelve_offload_with_port(self): # Do not wait before offloading @@ -6811,3 +6827,27 @@ class PortBindingShelvedServerTest(integrated_helpers._IntegratedTestBase): self.assertEqual(port['device_id'], server['id']) self.assertIsNone(port['binding:host_id']) self.assertNotIn('binding:status', port) + + def test_shelve_offload_with_port_preserve_dns(self): + # Do not wait before offloading + self.flags(shelved_offload_time=0) + + server_name = 'dns-name-test' + server = self._create_server( + name=server_name, + flavor_id=self.flavor_id, + networks=[{'port': self.neutron.port_1['id']}]) + + port = self.neutron.show_port(self.neutron.port_1['id'])['port'] + + # Assert that the port uses a DNS name matching the server name + self.assertEqual(port['dns_name'], server_name) + + # Do shelve + server = self._shelve_server(server, 'SHELVED_OFFLOADED') + + # Retrieve the updated port + port = self.neutron.show_port(self.neutron.port_1['id'])['port'] + + # Assert that the port still uses the same DNS name + self.assertEqual(port['dns_name'], server_name) diff --git a/nova/tests/unit/network/test_neutron.py b/nova/tests/unit/network/test_neutron.py index fa794cb012bc..935cc6e71f39 100644 --- a/nova/tests/unit/network/test_neutron.py +++ b/nova/tests/unit/network/test_neutron.py @@ -6223,8 +6223,36 @@ class TestAPI(TestAPIBase): @mock.patch('nova.network.neutron.API.has_dns_extension', new=mock.Mock(return_value=True)) + @mock.patch('nova.network.neutron.API._reset_port_dns_name') @mock.patch('nova.network.neutron.API._show_port') - def test_unbind_ports_reset_dns_name_by_admin(self, mock_show): + def test_unbind_ports_keep_dns_name_no_detach(self, + mock_show, + mock_reset_dns): + neutron = mock.Mock() + neutron.show_network.return_value = { + 'network': { + 'id': 'net1', + 'dns_domain': None + } + } + port_client = mock.Mock() + ports = [uuids.port_id] + mock_show.return_value = {'id': uuids.port} + self.api._reset_port_dns_name = mock.Mock() + self.api._unbind_ports(self.context, ports, neutron, port_client, + detach=False) + port_req_body = {'port': {'binding:host_id': None, + 'binding:profile': {}}} + port_client.update_port.assert_called_once_with( + uuids.port_id, port_req_body) + mock_reset_dns.assert_not_called() + + @mock.patch('nova.network.neutron.API.has_dns_extension', + new=mock.Mock(return_value=True)) + @mock.patch('nova.network.neutron.API._reset_port_dns_name') + @mock.patch('nova.network.neutron.API._show_port') + def test_unbind_ports_reset_dns_name_by_admin(self, mock_show, + mock_reset_dns): neutron = mock.Mock() neutron.show_network.return_value = { 'network': { @@ -6244,6 +6272,9 @@ class TestAPI(TestAPIBase): port_client.update_port.assert_called_once_with( uuids.port_id, port_req_body) neutron.update_port.assert_not_called() + mock_reset_dns.assert_called_once_with( + neutron.show_network.return_value['network'], + uuids.port_id, neutron) @mock.patch('nova.network.neutron.API.has_dns_extension', new=mock.Mock(return_value=True))