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 c25097b0b0
)
This commit is contained in:
parent
8ed85b6ce6
commit
acd55bc4d0
|
@ -366,12 +366,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
|
||||
|
@ -380,7 +385,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
|
||||
|
|
|
@ -230,27 +230,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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# Only adjust the OVN type if the port is not owned by Neutron
|
||||
# DHCP agents.
|
||||
|
@ -497,9 +516,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)
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -1608,6 +1608,65 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
|
|||
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 = {}
|
||||
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')
|
||||
|
|
Loading…
Reference in New Issue