Get ports directly instead of via loop

In order to get a list of available ports, the current implementation
of the form code in `project/instances/attach_interface` loops through
the available networks in the project and requests a list of ports for
each via the Neutron API. This implementation is O(N) in time with a
large prefactor (the time for a Neutron API call) where N is the
number of networks.

Instead of calling the Neutron API for each network this change uses
one call to the Neutron API to get the list of ports on all networks
and filters this list via a list comprehension. While this
implementation is still O(N) the prefactor is significantly smaller.

Includes some unit test fixes to workaround the fact that patch
6ac31e0 is not backported beyond Y.

Closes-Bug: #1943639
Change-Id: I8fd32c3aad22d8ef7f57201f5144f6b2e357ef10
Signed-off-by: Nicolas Bock <nicolas.bock@canonical.com>
(cherry picked from commit 9f5d659d16)
(cherry picked from commit 1561fcce55)
This commit is contained in:
Nicolas Bock 2021-09-15 09:39:31 -06:00
parent 419ed2cc72
commit ec39137c2d
2 changed files with 30 additions and 39 deletions

View File

@ -148,13 +148,9 @@ class InstanceTableTestMixin(object):
shared=False),
mock.call(helpers.IsHttpRequest(), shared=True),
])
self.assertEqual(len(self.networks.list()),
self.mock_port_list_with_trunk_types.call_count)
self.mock_port_list_with_trunk_types(
[mock.call(helpers.IsHttpRequest(),
network_id=net.id,
tenant_id=self.tenant.id)
for net in self.networks.list()])
self.mock_port_list_with_trunk_types.assert_called_once_with(
helpers.IsHttpRequest(),
tenant_id=self.tenant.id)
def _mock_nova_lists(self):
self.mock_flavor_list.return_value = self.flavors.list()
@ -2348,12 +2344,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
mock.call(helpers.IsHttpRequest(), shared=True),
])
self.assertEqual(4, self.mock_network_list.call_count)
self.mock_port_list_with_trunk_types.assert_has_calls(
[mock.call(helpers.IsHttpRequest(),
network_id=net.id, tenant_id=self.tenant.id)
for net in self.networks.list()])
self.assertEqual(len(self.networks.list()),
self.mock_port_list_with_trunk_types.call_count)
self.mock_port_list_with_trunk_types.assert_called_once_with(
helpers.IsHttpRequest(),
tenant_id=self.tenant.id)
self.mock_server_group_list.assert_called_once_with(
helpers.IsHttpRequest())
self.mock_tenant_quota_usages.assert_called_once_with(
@ -2525,10 +2518,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
mock.call(helpers.IsHttpRequest(), shared=True),
])
self.assertEqual(4, self.mock_network_list.call_count)
self.mock_port_list_with_trunk_types.assert_has_calls(
[mock.call(helpers.IsHttpRequest(),
network_id=net.id, tenant_id=self.tenant.id)
for net in self.networks.list()])
self.mock_port_list_with_trunk_types.assert_called_once_with(
helpers.IsHttpRequest(),
tenant_id=self.tenant.id)
self.mock_server_group_list.assert_called_once_with(
helpers.IsHttpRequest())
self.mock_tenant_quota_usages.assert_called_once_with(
@ -2621,10 +2613,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
mock.call(helpers.IsHttpRequest(), shared=True),
])
self.assertEqual(4, self.mock_network_list.call_count)
self.mock_port_list_with_trunk_types.assert_has_calls(
[mock.call(helpers.IsHttpRequest(),
network_id=net.id, tenant_id=self.tenant.id)
for net in self.networks.list()])
self.mock_port_list_with_trunk_types.assert_called_once_with(
helpers.IsHttpRequest(),
tenant_id=self.tenant.id)
self.mock_server_group_list.assert_called_once_with(
helpers.IsHttpRequest())
self.mock_tenant_quota_usages.assert_called_once_with(
@ -4006,13 +3997,9 @@ class InstanceLaunchInstanceTests(InstanceTestBase,
helpers.IsHttpRequest(),
shared=True),
])
self.assertEqual(len(self.networks.list()),
self.mock_port_list_with_trunk_types.call_count)
self.mock_port_list_with_trunk_types.assert_has_calls(
[mock.call(helpers.IsHttpRequest(),
network_id=net.id,
tenant_id=self.tenant.id)
for net in self.networks.list()])
self.mock_port_list_with_trunk_types.assert_called_once_with(
helpers.IsHttpRequest(),
tenant_id=self.tenant.id)
self.mock_volume_list.assert_has_calls([
mock.call(helpers.IsHttpRequest(),
search_opts=VOLUME_SEARCH_OPTS),
@ -5608,7 +5595,8 @@ class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
self.assertRaises(exceptions.NotAvailable,
console.get_console, None, 'FAKE', None)
@helpers.create_mocks({api.neutron: ('network_list_for_tenant',)})
@helpers.create_mocks({api.neutron: ('network_list_for_tenant',
'port_list_with_trunk_types',)})
def test_interface_attach_get(self):
server = self.servers.first()
self.mock_network_list_for_tenant.side_effect = [
@ -5628,7 +5616,8 @@ class ConsoleManagerTests(helpers.ResetImageAPIVersionMixin, helpers.TestCase):
])
self.assertEqual(2, self.mock_network_list_for_tenant.call_count)
@helpers.create_mocks({api.neutron: ('network_list_for_tenant',),
@helpers.create_mocks({api.neutron: ('network_list_for_tenant',
'port_list_with_trunk_types',),
api.nova: ('interface_attach',)})
def test_interface_attach_post(self):
fixed_ip = '10.0.0.10'

View File

@ -197,7 +197,7 @@ def port_field_data(request, with_network=False):
port_name = "{} ({})".format(
port.name_or_id, ",".join(
[ip['ip_address'] for ip in port['fixed_ips']]))
if with_network and network:
if with_network:
port_name += " - {}".format(network.name_or_id)
return port_name
@ -205,14 +205,16 @@ def port_field_data(request, with_network=False):
if api.base.is_service_enabled(request, 'network'):
network_list = api.neutron.network_list_for_tenant(
request, request.user.tenant_id)
for network in network_list:
ports.extend(
[(port.id, add_more_info_port_name(port, network))
for port in api.neutron.port_list_with_trunk_types(
request, network_id=network.id,
tenant_id=request.user.tenant_id)
if (not port.device_owner and
not isinstance(port, api.neutron.PortTrunkSubport))])
network_dict = dict((n.id, n) for n in network_list)
ports = [
(port.id,
add_more_info_port_name(port, network_dict[port.network_id]))
for port
in api.neutron.port_list_with_trunk_types(
request, tenant_id=request.user.tenant_id)
if (not port.device_owner and
not isinstance(port, api.neutron.PortTrunkSubport))
]
ports.sort(key=lambda obj: obj[1])
return ports