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):