From 31773715687326c92d4ad46ebb32b14645bbc614 Mon Sep 17 00:00:00 2001 From: Kobi Samoray Date: Tue, 27 Aug 2019 13:49:05 +0300 Subject: [PATCH] Avoid fetching metadata when no subnets found Metadata service uses the provider id to identify the requesting instance. However, when provider query doesn't find any networks, the request should fail. The same goes to the case where multiple ports are found. Closes-Bug: #1841933 Change-Id: I8ce3703ca86a3a0769edd42a790d82796d1071d7 --- nova/api/metadata/handler.py | 20 +++++++++-- nova/tests/unit/test_metadata.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index ac8d07f076c7..12e3ebb022f6 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -209,16 +209,21 @@ class MetadataRequestHandler(wsgi.Application): advanced_service_providers=[provider_id], fields=['network_id']) + if not md_subnets or not md_subnets.get('subnets'): + msg = _('Could not find any subnets for provider %s') % provider_id + LOG.error(msg) + raise webob.exc.HTTPBadRequest(explanation=msg) + md_networks = [subnet['network_id'] for subnet in md_subnets['subnets']] try: # Retrieve the instance data from the instance's port - instance_data = neutron.list_ports( + ports = neutron.list_ports( context, fixed_ips='ip_address=' + instance_address, network_id=md_networks, - fields=['device_id', 'tenant_id'])['ports'][0] + fields=['device_id', 'tenant_id'])['ports'] except Exception as e: LOG.error('Failed to get instance id for metadata ' 'request, provider %(provider)s ' @@ -232,6 +237,17 @@ class MetadataRequestHandler(wsgi.Application): 'Please try your request again.') raise webob.exc.HTTPBadRequest(explanation=msg) + if len(ports) != 1: + msg = _('Expected a single port matching provider %(pr)s ' + 'and IP %(ip)s. Found %(count)d.' % { + 'pr': provider_id, + 'ip': instance_address, + 'count': len(ports)}) + + LOG.error(msg) + raise webob.exc.HTTPBadRequest(explanation=msg) + + instance_data = ports[0] instance_id = instance_data['device_id'] tenant_id = instance_data['tenant_id'] diff --git a/nova/tests/unit/test_metadata.py b/nova/tests/unit/test_metadata.py index fc00ee7dac4f..fc33561991a5 100644 --- a/nova/tests/unit/test_metadata.py +++ b/nova/tests/unit/test_metadata.py @@ -1579,6 +1579,63 @@ class MetadataHandlerTestCase(test.TestCase): 'X-Metadata-Provider-Signature': signature}) self.assertEqual(403, response.status_int) + @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) + def test_metadata_lb_net_not_found(self, mock_get_client): + + self.flags(service_metadata_proxy=True, group='neutron') + + # with X-Metadata-Provider + proxy_lb_id = 'edge-x' + mock_client = mock_get_client.return_value + mock_client.list_ports.return_value = { + 'ports': [{'device_id': 'a-b-c-d', 'tenant_id': 'test'}]} + mock_client.list_subnets.return_value = { + 'subnets': []} + + response = fake_request( + self, self.mdinst, + relpath="/2009-04-04/user-data", + address="192.192.192.2", + fake_get_metadata_by_instance_id=self._fake_x_get_metadata, + headers={'X-Forwarded-For': '192.192.192.2', + 'X-Metadata-Provider': proxy_lb_id}) + self.assertEqual(400, response.status_int) + + def _test_metadata_lb_incorrect_port_count(self, mock_get_client, ports): + + self.flags(service_metadata_proxy=True, group='neutron') + + # with X-Metadata-Provider + proxy_lb_id = 'edge-x' + mock_client = mock_get_client.return_value + mock_client.list_ports.return_value = {'ports': ports} + mock_client.list_ports.return_value = { + 'ports': [{'device_id': 'a-b-c-d', 'tenant_id': 'test'}, + {'device_id': 'x-y-z', 'tenant_id': 'test'}]} + mock_client.list_subnets.return_value = { + 'subnets': [{'network_id': 'f-f-f-f'}]} + + response = fake_request( + self, self.mdinst, + relpath="/2009-04-04/user-data", + address="192.192.192.2", + fake_get_metadata_by_instance_id=self._fake_x_get_metadata, + headers={'X-Forwarded-For': '192.192.192.2', + 'X-Metadata-Provider': proxy_lb_id}) + self.assertEqual(400, response.status_int) + + @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) + def test_metadata_lb_too_many_ports(self, mock_get_client): + self._test_metadata_lb_incorrect_port_count( + mock_get_client, + [{'device_id': 'a-b-c-d', 'tenant_id': 'test'}, + {'device_id': 'x-y-z', 'tenant_id': 'test'}]) + + @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) + def test_metadata_no_ports_found(self, mock_get_client): + self._test_metadata_lb_incorrect_port_count( + mock_get_client, []) + @mock.patch.object(context, 'get_admin_context') @mock.patch('nova.network.neutron.API') def test_get_metadata_by_address(self, mock_net_api, mock_get_context):