summaryrefslogtreecommitdiff
path: root/neutron
diff options
context:
space:
mode:
authorFernando Royo <froyo@redhat.com>2022-06-07 13:51:51 +0200
committerFernando Royo <froyo@redhat.com>2022-06-20 15:18:03 +0000
commita57d47bcb2df76df81eed3ad44c8f49e7dbda0e8 (patch)
tree974511b01e9ef62b9ed1dd8f3bc576750f1cddbb /neutron
parent0dcef25bd98b3e3f9a6f08ca09c2761693b53830 (diff)
downloadneutron-a57d47bcb2df76df81eed3ad44c8f49e7dbda0e8.tar.gz
Optimize queries for port operations
Port create/update are most time-consuming operations on subnet creation. As example, in those cases where several subnets are created over the same network the response time for those port operations is linearly increased as the total subnets increases. This patch improves the number of queries required on port operations in order to reduce the response time. Closes-Bug: #1977831 Change-Id: I0fccf36a2035e8f6c2fa8dab0307358da600c8f7 (cherry picked from commit c25097b0b0da2af9021699036a69cd10e66533b1)
Diffstat (limited to 'neutron')
-rw-r--r--neutron/objects/subnet.py11
-rw-r--r--neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py70
-rw-r--r--neutron/tests/unit/objects/test_subnet.py20
-rw-r--r--neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py59
4 files changed, 134 insertions, 26 deletions
diff --git a/neutron/objects/subnet.py b/neutron/objects/subnet.py
index 267cfbf029..3a17687bca 100644
--- a/neutron/objects/subnet.py
+++ b/neutron/objects/subnet.py
@@ -364,12 +364,17 @@ class Subnet(base.NeutronDbObject):
:raises: FixedIpsSubnetsNotOnSameSegment
"""
segment_ids = []
+ subnets = query.all()
+
for fixed_ip in fixed_ips:
subnet = None
if 'subnet_id' in fixed_ip:
try:
- subnet = query.filter(
- cls.db_model.id == fixed_ip['subnet_id']).all()[0]
+ subnet = [
+ sub
+ for sub in subnets
+ if sub['id'] == fixed_ip['subnet_id']
+ ][0]
except IndexError:
# NOTE(hjensas): The subnet is invalid for the network,
# return all subnets. This will be detected in following
@@ -378,7 +383,7 @@ class Subnet(base.NeutronDbObject):
elif 'ip_address' in fixed_ip:
ip = netaddr.IPNetwork(fixed_ip['ip_address'])
- for s in query.all():
+ for s in subnets:
if ip in netaddr.IPNetwork(s.cidr):
subnet = s
break
diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
index 77940fba94..b0c6a9a7a8 100644
--- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
+++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
@@ -281,27 +281,46 @@ class OVNClient(object):
parent_name = binding_prof.get('parent_name', [])
tag = binding_prof.get('tag', [])
address = port['mac_address']
- for ip in port.get('fixed_ips', []):
- try:
- subnet = self._plugin.get_subnet(context, ip['subnet_id'])
- except n_exc.SubnetNotFound:
- continue
- ip_addr = ip['ip_address']
- address += ' ' + ip_addr
- cidrs += ' {}/{}'.format(ip['ip_address'],
- subnet['cidr'].split('/')[1])
-
- # Check if the port being created is a virtual port
- parents = utils.get_virtual_port_parents(
- self._nb_idl, ip_addr, port['network_id'], port['id'])
- if not parents:
- continue
- port_type = ovn_const.LSP_TYPE_VIRTUAL
- options[ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY] = ip_addr
- options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = (
- ','.join(parents))
- break
+ ip_subnets = port.get('fixed_ips', [])
+ subnet_ids = [
+ ip['subnet_id']
+ for ip in ip_subnets
+ if 'subnet_id' in ip
+ ]
+ subnets = self._plugin.get_subnets(
+ context, filters={'id': subnet_ids})
+ if subnets:
+ for ip in ip_subnets:
+ ip_addr = ip['ip_address']
+ address += ' ' + ip_addr
+ subnet = None
+
+ try:
+ subnet = [
+ sub
+ for sub in subnets
+ if sub["id"] == ip["subnet_id"]
+ ][0]
+ except IndexError:
+ LOG.debug('Subnet not found for ip address %s',
+ ip_addr)
+ continue
+
+ cidrs += ' {}/{}'.format(ip['ip_address'],
+ subnet['cidr'].split('/')[1])
+
+ # Check if the port being created is a virtual port
+ parents = utils.get_virtual_port_parents(
+ self._nb_idl, ip_addr, port['network_id'], port['id'])
+ if not parents:
+ continue
+
+ port_type = ovn_const.LSP_TYPE_VIRTUAL
+ options[ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY] = ip_addr
+ options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = (
+ ','.join(parents))
+ break
# Metadata port.
if port['device_owner'] == const.DEVICE_OWNER_DISTRIBUTED:
@@ -616,9 +635,14 @@ class OVNClient(object):
if self.is_metadata_port(port):
context = n_context.get_admin_context()
network = self._plugin.get_network(context, port['network_id'])
- subnet_ids = set(_ip['subnet_id'] for _ip in port['fixed_ips'])
- for subnet_id in subnet_ids:
- subnet = self._plugin.get_subnet(context, subnet_id)
+ subnet_ids = [
+ _ip['subnet_id']
+ for _ip in port['fixed_ips']
+ if 'subnet_id' in _ip
+ ]
+
+ for subnet in self._plugin.get_subnets(
+ context, filters={'id': subnet_ids}):
if not subnet['enable_dhcp']:
continue
self._update_subnet_dhcp_options(subnet, network, txn)
diff --git a/neutron/tests/unit/objects/test_subnet.py b/neutron/tests/unit/objects/test_subnet.py
index 7e2eee08c9..05ddb9a3eb 100644
--- a/neutron/tests/unit/objects/test_subnet.py
+++ b/neutron/tests/unit/objects/test_subnet.py
@@ -268,6 +268,26 @@ class SubnetDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
self.assertEqual([service_type_obj.service_type],
obj1.service_types)
+ def test_find_candidate_subnets(self):
+ network = self._create_test_network()
+ subnet_data = dict(self.obj_fields[0])
+ subnet_data['network_id'] = network['id']
+ subnet_net = self._make_object(subnet_data)
+ subnet2_data = dict(self.obj_fields[0])
+ subnet2_data['id'] = uuidutils.generate_uuid()
+ subnet2_data['network_id'] = network['id']
+ subnet2_net = self._make_object(subnet2_data)
+ subnet_net.create()
+ subnet2_net.create()
+ fixed_ips = [
+ {'subnet_id': subnet_data['id'], 'ip_address': '10.0.0.2'},
+ {'subnet_id': subnet2_data['id'], 'ip_address': '10.0.1.2'}]
+ candidate_subnet = subnet.Subnet.find_candidate_subnets(
+ self.context, network['id'], None, None, True, fixed_ips)
+ self.assertEqual(2, len(candidate_subnet))
+ self.assertNotEqual(
+ candidate_subnet[0]['id'], candidate_subnet[1]['id'])
+
class NetworkSubnetLockTestCase(obj_test_base.BaseObjectIfaceTestCase):
diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py
index d8a64665ba..04808f8d53 100644
--- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py
+++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py
@@ -1823,6 +1823,65 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
context.current, context.network.current, mock.ANY)
umd.assert_called_once_with(mock.ANY, 'id', subnet=subnet)
+ def test__get_port_options(self):
+ with mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \
+ mock_get_subnets:
+ port = {'id': 'virt-port',
+ 'mac_address': '00:00:00:00:00:00',
+ 'device_owner': 'device_owner',
+ 'network_id': 'foo',
+ 'fixed_ips': [{'subnet_id': 'subnet-1',
+ 'ip_address': '10.0.0.55'},
+ {'subnet_id': 'subnet-2',
+ 'ip_address': '10.0.1.55'},
+ ]}
+ subnet_ids = [
+ ip['subnet_id']
+ for ip in port.get('fixed_ips')
+ ]
+ self.mech_driver._ovn_client._get_port_options(port)
+ mock_get_subnets.assert_called_once_with(
+ mock.ANY,
+ filters={'id': subnet_ids})
+
+ def test_update_port(self):
+ with mock.patch.object(
+ self.mech_driver._ovn_client, 'is_metadata_port') as \
+ mock_is_metadata_port, \
+ mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \
+ mock_get_subnets, \
+ mock.patch.object(self.mech_driver._plugin, 'get_network') as \
+ mock_get_network:
+ net_attrs = {az_def.AZ_HINTS: ['az0', 'az1', 'az2']}
+ fake_net = (
+ fakes.FakeNetwork.create_one_network(attrs=net_attrs).info())
+ port = {'id': 'virt-port',
+ 'mac_address': '00:00:00:00:00:00',
+ 'name': 'port-foo',
+ 'device_id': 'device_id-foo',
+ 'project_id': 'project_id-foo',
+ 'device_owner': 'device_owner',
+ 'network_id': 'foo',
+ 'admin_state_up': True,
+ 'fixed_ips': [{'subnet_id': 'subnet-1',
+ 'ip_address': '10.0.0.55'},
+ {'subnet_id': 'subnet-2',
+ 'ip_address': '10.0.1.55'},
+ ]}
+ subnet_ids = [
+ ip['subnet_id']
+ for ip in port.get('fixed_ips')
+ ]
+
+ mock_is_metadata_port.return_value = [True]
+ mock_get_network.return_value = fake_net
+ self.mech_driver._ovn_client.update_port(
+ self.context, port)
+ self.assertEqual(mock_get_subnets.call_count, 2)
+ mock_get_subnets.assert_called_with(
+ mock.ANY,
+ filters={'id': subnet_ids})
+
def test_update_metadata_port_with_subnet(self):
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
group='ovn')