diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index 12e3ebb022..c9cc1b7bfe 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -39,6 +39,12 @@ from nova.network import neutron as neutronapi CONF = nova.conf.CONF LOG = logging.getLogger(__name__) +# 160 networks is large enough to satisfy most cases. +# Yet while reaching 182 networks Neutron server will break as URL length +# exceeds the maximum. Left this at 160 to allow additional parameters when +# they're needed. +MAX_QUERY_NETWORKS = 160 + class MetadataRequestHandler(wsgi.Application): """Serve metadata.""" @@ -219,11 +225,14 @@ class MetadataRequestHandler(wsgi.Application): try: # Retrieve the instance data from the instance's port - ports = neutron.list_ports( - context, - fixed_ips='ip_address=' + instance_address, - network_id=md_networks, - fields=['device_id', 'tenant_id'])['ports'] + ports = [] + while md_networks: + ports.extend(neutron.list_ports( + context, + fixed_ips='ip_address=' + instance_address, + network_id=md_networks[:MAX_QUERY_NETWORKS], + fields=['device_id', 'tenant_id'])['ports']) + md_networks = md_networks[MAX_QUERY_NETWORKS:] except Exception as e: LOG.error('Failed to get instance id for metadata ' 'request, provider %(provider)s ' diff --git a/nova/tests/unit/test_metadata.py b/nova/tests/unit/test_metadata.py index 27c5f33880..b30be8eb36 100644 --- a/nova/tests/unit/test_metadata.py +++ b/nova/tests/unit/test_metadata.py @@ -1400,6 +1400,45 @@ class MetadataHandlerTestCase(test.TestCase): self.assertEqual(200, response.status_int) + @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) + def test_metadata_lb_proxy_many_networks(self, mock_get_client): + + def fake_list_ports(context, fixed_ips, network_id, fields): + if 'f-f-f-f' in network_id: + return {'ports': + [{'device_id': 'a-b-c-d', 'tenant_id': 'test'}]} + return {'ports': []} + + self.flags(service_metadata_proxy=True, group='neutron') + handler.MAX_QUERY_NETWORKS = 10 + + self.expected_instance_id = b'a-b-c-d' + + # with X-Metadata-Provider + proxy_lb_id = 'edge-x' + + mock_client = mock_get_client.return_value + subnet_list = [{'network_id': 'f-f-f-' + chr(c)} + for c in range(ord('a'), ord('z'))] + mock_client.list_subnets.return_value = { + 'subnets': subnet_list} + + with mock.patch.object( + mock_client, 'list_ports', + side_effect=fake_list_ports) as mock_list_ports: + + 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(3, mock_list_ports.call_count) + + self.assertEqual(200, response.status_int) + @mock.patch.object(neutronapi, 'get_client', return_value=mock.Mock()) def _metadata_handler_with_provider_id(self, hnd, mock_get_client): # with X-Metadata-Provider