Improve DHCP RPC handler
Remove unnecessary DB retrieval operations from "get_active_networks_info" method. Partial-Bug: #1950662 Change-Id: I4ea7b86e3f544d5dddcdac562208bb8afd1fc36a
This commit is contained in:
parent
01debb20bf
commit
5710d3407b
|
@ -105,8 +105,8 @@ class DhcpRpcCallback(object):
|
||||||
LOG.debug("DHCP Agent admin state is down on host %s", host)
|
LOG.debug("DHCP Agent admin state is down on host %s", host)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
filters = dict(admin_state_up=[True])
|
nets = network_obj.Network.get_objects(context,
|
||||||
nets = plugin.get_networks(context, filters=filters)
|
admin_state_up=[True])
|
||||||
return nets
|
return nets
|
||||||
|
|
||||||
def _port_action(self, plugin, context, port, action):
|
def _port_action(self, plugin, context, port, action):
|
||||||
|
@ -159,78 +159,51 @@ class DhcpRpcCallback(object):
|
||||||
LOG.debug('get_active_networks_info from %s', host)
|
LOG.debug('get_active_networks_info from %s', host)
|
||||||
networks = self._get_active_networks(context, **kwargs)
|
networks = self._get_active_networks(context, **kwargs)
|
||||||
plugin = directory.get_plugin()
|
plugin = directory.get_plugin()
|
||||||
filters = {'network_id': [network['id'] for network in networks]}
|
filters = {'network_id': [network.id for network in networks]}
|
||||||
ports = plugin.get_ports(context, filters=filters)
|
ports = plugin.get_ports(context, filters=filters)
|
||||||
# default is to filter subnets based on 'enable_dhcp' flag
|
|
||||||
if kwargs.get('enable_dhcp_filter', True):
|
|
||||||
filters['enable_dhcp'] = [True]
|
|
||||||
# NOTE(kevinbenton): we sort these because the agent builds tags
|
|
||||||
# based on position in the list and has to restart the process if
|
|
||||||
# the order changes.
|
|
||||||
subnets = sorted(plugin.get_subnets(context, filters=filters),
|
|
||||||
key=operator.itemgetter('id'))
|
|
||||||
# Handle the possibility that the dhcp agent(s) only has connectivity
|
|
||||||
# inside a segment. If the segment service plugin is loaded and
|
|
||||||
# there are active dhcp enabled subnets, then filter out the subnets
|
|
||||||
# that are not on the host's segment.
|
|
||||||
seg_plug = directory.get_plugin(
|
|
||||||
segment_ext.SegmentPluginBase.get_plugin_type())
|
|
||||||
seg_subnets = [subnet for subnet in subnets
|
|
||||||
if subnet.get('segment_id')]
|
|
||||||
nonlocal_subnets = []
|
|
||||||
if seg_plug and seg_subnets:
|
|
||||||
host_segment_ids = seg_plug.get_segments_by_hosts(context, [host])
|
|
||||||
# Gather the ids of all the subnets that are on a segment that
|
|
||||||
# this host touches
|
|
||||||
seg_subnet_ids = {subnet['id'] for subnet in seg_subnets
|
|
||||||
if subnet['segment_id'] in host_segment_ids}
|
|
||||||
# Gather the ids of all the networks that are routed
|
|
||||||
routed_net_ids = {seg_subnet['network_id']
|
|
||||||
for seg_subnet in seg_subnets}
|
|
||||||
# Remove the subnets with segments that are not in the same
|
|
||||||
# segments as the host. Do this only for the networks that are
|
|
||||||
# routed because we want non-routed networks to work as
|
|
||||||
# before.
|
|
||||||
nonlocal_subnets = [subnet for subnet in seg_subnets
|
|
||||||
if subnet['id'] not in seg_subnet_ids]
|
|
||||||
subnets = [subnet for subnet in subnets
|
|
||||||
if subnet['network_id'] not in routed_net_ids or
|
|
||||||
subnet['id'] in seg_subnet_ids]
|
|
||||||
|
|
||||||
grouped_subnets = self._group_by_network_id(subnets)
|
|
||||||
grouped_nonlocal_subnets = self._group_by_network_id(nonlocal_subnets)
|
|
||||||
grouped_ports = self._group_by_network_id(ports)
|
grouped_ports = self._group_by_network_id(ports)
|
||||||
|
# default is to filter subnets based on 'enable_dhcp' flag
|
||||||
|
enable_dhcp = True if kwargs.get('enable_dhcp_filter', True) else None
|
||||||
|
ret = []
|
||||||
for network in networks:
|
for network in networks:
|
||||||
network['subnets'] = grouped_subnets.get(network['id'], [])
|
ret.append(self.get_network_info(
|
||||||
network['non_local_subnets'] = (
|
context, network=network, enable_dhcp=enable_dhcp,
|
||||||
grouped_nonlocal_subnets.get(network['id'], []))
|
ports=grouped_ports.get(network.id, [])))
|
||||||
network['ports'] = grouped_ports.get(network['id'], [])
|
|
||||||
|
|
||||||
return networks
|
return ret
|
||||||
|
|
||||||
def get_network_info(self, context, **kwargs):
|
def get_network_info(self, context, **kwargs):
|
||||||
"""Retrieve and return information about a network.
|
"""Retrieve and return information about a network.
|
||||||
|
|
||||||
Retrieve and return extended information about a network
|
Retrieve and return extended information about a network.
|
||||||
with only DHCP enabled subnets.
|
The optional argument "enable_dhcp" can be used to filter the subnets
|
||||||
|
using the same flag (True/False). If None is passed, no filtering is
|
||||||
|
done.
|
||||||
"""
|
"""
|
||||||
network_id = kwargs.get('network_id')
|
enable_dhcp = kwargs.get('enable_dhcp', True)
|
||||||
host = kwargs.get('host')
|
network = kwargs.get('network')
|
||||||
LOG.debug('Network %(network_id)s requested from '
|
plugin = directory.get_plugin()
|
||||||
'%(host)s', {'network_id': network_id,
|
if network:
|
||||||
'host': host})
|
ports = kwargs['ports']
|
||||||
network = network_obj.Network.get_object(context, id=network_id)
|
else:
|
||||||
if not network:
|
network_id = kwargs.get('network_id')
|
||||||
LOG.debug('Network %s could not be found, it might have '
|
host = kwargs.get('host')
|
||||||
'been deleted concurrently.', network_id)
|
LOG.debug('Network %(network_id)s requested from '
|
||||||
return
|
'%(host)s', {'network_id': network_id,
|
||||||
|
'host': host})
|
||||||
|
network = network_obj.Network.get_object(context, id=network_id)
|
||||||
|
if not network:
|
||||||
|
LOG.debug('Network %s could not be found, it might have '
|
||||||
|
'been deleted concurrently.', network_id)
|
||||||
|
return
|
||||||
|
port_filters = {'network_id': [network_id]}
|
||||||
|
ports = plugin.get_ports(context, filters=port_filters)
|
||||||
|
|
||||||
seg_plug = directory.get_plugin(
|
seg_plug = directory.get_plugin(
|
||||||
segment_ext.SegmentPluginBase.get_plugin_type())
|
segment_ext.SegmentPluginBase.get_plugin_type())
|
||||||
plugin = directory.get_plugin()
|
|
||||||
# Only subnets with DHCP enabled.
|
|
||||||
subnets = [plugin._make_subnet_dict(subnet) for subnet in
|
subnets = [plugin._make_subnet_dict(subnet) for subnet in
|
||||||
network.db_obj.subnets if subnet.enable_dhcp]
|
network.db_obj.subnets if
|
||||||
|
(enable_dhcp is None or subnet.enable_dhcp == enable_dhcp)]
|
||||||
seg_subnets = [subnet for subnet in subnets if
|
seg_subnets = [subnet for subnet in subnets if
|
||||||
subnet.get('segment_id')]
|
subnet.get('segment_id')]
|
||||||
nonlocal_subnets = []
|
nonlocal_subnets = []
|
||||||
|
@ -254,7 +227,6 @@ class DhcpRpcCallback(object):
|
||||||
# NOTE(kevinbenton): we sort these because the agent builds tags
|
# NOTE(kevinbenton): we sort these because the agent builds tags
|
||||||
# based on position in the list and has to restart the process if
|
# based on position in the list and has to restart the process if
|
||||||
# the order changes.
|
# the order changes.
|
||||||
port_filters = {'network_id': [network_id]}
|
|
||||||
# TODO(ralonsoh): in Z+, remove "tenant_id" parameter. DHCP agents
|
# TODO(ralonsoh): in Z+, remove "tenant_id" parameter. DHCP agents
|
||||||
# should read only "project_id".
|
# should read only "project_id".
|
||||||
return {'id': network.id,
|
return {'id': network.id,
|
||||||
|
@ -264,7 +236,7 @@ class DhcpRpcCallback(object):
|
||||||
'subnets': sorted(subnets, key=operator.itemgetter('id')),
|
'subnets': sorted(subnets, key=operator.itemgetter('id')),
|
||||||
'non_local_subnets': sorted(nonlocal_subnets,
|
'non_local_subnets': sorted(nonlocal_subnets,
|
||||||
key=operator.itemgetter('id')),
|
key=operator.itemgetter('id')),
|
||||||
'ports': plugin.get_ports(context, filters=port_filters),
|
'ports': ports,
|
||||||
'mtu': network.mtu}
|
'mtu': network.mtu}
|
||||||
|
|
||||||
@db_api.retry_db_errors
|
@db_api.retry_db_errors
|
||||||
|
|
|
@ -463,10 +463,8 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
|
||||||
|
|
||||||
net_ids = [item.network_id for item in query]
|
net_ids = [item.network_id for item in query]
|
||||||
if net_ids:
|
if net_ids:
|
||||||
return self.get_networks(
|
return network.Network.get_objects(context, id=net_ids,
|
||||||
context,
|
admin_state_up=[True])
|
||||||
filters={'id': net_ids, 'admin_state_up': [True]}
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
|
@ -64,64 +64,34 @@ class TestDhcpRpcCallback(base.BaseTestCase):
|
||||||
self.assertEqual(expected, grouped_ports)
|
self.assertEqual(expected, grouped_ports)
|
||||||
|
|
||||||
def test_get_active_networks_info(self):
|
def test_get_active_networks_info(self):
|
||||||
plugin_retval = [{'id': 'a'}, {'id': 'b'}]
|
networks = [mock.Mock(id='net1'), mock.Mock(id='net2'),
|
||||||
self.plugin.get_networks.return_value = plugin_retval
|
mock.Mock(id='net3')]
|
||||||
port = {'network_id': 'a'}
|
ports = [{'id': 'port1', 'network_id': 'net1'},
|
||||||
subnet = {'network_id': 'b', 'id': 'c'}
|
{'id': 'port2', 'network_id': 'net2'},
|
||||||
self.plugin.get_ports.return_value = [port]
|
{'id': 'port3', 'network_id': 'net3'}]
|
||||||
self.plugin.get_subnets.return_value = [subnet]
|
self.plugin.get_ports.return_value = ports
|
||||||
networks = self.callbacks.get_active_networks_info(mock.Mock(),
|
iter_kwargs = iter([{'enable_dhcp_filter': True},
|
||||||
host='host')
|
{'enable_dhcp_filter': False},
|
||||||
expected = [{'id': 'a',
|
{}])
|
||||||
'non_local_subnets': [],
|
with mock.patch.object(self.callbacks, '_get_active_networks') as \
|
||||||
'subnets': [],
|
mock_get_networks, \
|
||||||
'ports': [port]},
|
mock.patch.object(self.callbacks, 'get_network_info') as \
|
||||||
{'id': 'b',
|
mock_get_network_info:
|
||||||
'non_local_subnets': [],
|
mock_get_networks.return_value = networks
|
||||||
'subnets': [subnet],
|
mock_get_network_info.side_effect = networks
|
||||||
'ports': []}]
|
kwargs = next(iter_kwargs)
|
||||||
self.assertEqual(expected, networks)
|
ret = self.callbacks.get_active_networks_info('ctx', **kwargs)
|
||||||
|
self.assertEqual(networks, ret)
|
||||||
def test_get_active_networks_info_with_routed_networks(self):
|
enable_dhcp = (True if kwargs.get('enable_dhcp_filter', True) else
|
||||||
plugin_retval = [{'id': 'a'}, {'id': 'b'}]
|
None)
|
||||||
port = {'network_id': 'a'}
|
mock_get_network_info.assert_has_calls([
|
||||||
subnets = [{'network_id': 'b', 'id': 'c', 'segment_id': '1'},
|
mock.call('ctx', network=networks[0], enable_dhcp=enable_dhcp,
|
||||||
{'network_id': 'a', 'id': 'e'},
|
ports=[ports[0]]),
|
||||||
{'network_id': 'b', 'id': 'd', 'segment_id': '3'}]
|
mock.call('ctx', network=networks[1], enable_dhcp=enable_dhcp,
|
||||||
self.plugin.get_ports.return_value = [port]
|
ports=[ports[1]]),
|
||||||
self.plugin.get_networks.return_value = plugin_retval
|
mock.call('ctx', network=networks[2], enable_dhcp=enable_dhcp,
|
||||||
hostseg_retval = ['1', '2']
|
ports=[ports[2]])
|
||||||
self.segment_plugin.get_segments_by_hosts.return_value = hostseg_retval
|
])
|
||||||
self.plugin.get_subnets.return_value = subnets
|
|
||||||
networks = self.callbacks.get_active_networks_info(mock.Mock(),
|
|
||||||
host='host')
|
|
||||||
expected = [{'id': 'a',
|
|
||||||
'non_local_subnets': [],
|
|
||||||
'subnets': [subnets[1]],
|
|
||||||
'ports': [port]},
|
|
||||||
{'id': 'b',
|
|
||||||
'non_local_subnets': [subnets[2]],
|
|
||||||
'subnets': [subnets[0]],
|
|
||||||
'ports': []}]
|
|
||||||
self.assertEqual(expected, networks)
|
|
||||||
|
|
||||||
def _test_get_active_networks_info_enable_dhcp_filter(self,
|
|
||||||
enable_dhcp_filter):
|
|
||||||
plugin_retval = [{'id': 'a'}, {'id': 'b'}]
|
|
||||||
self.plugin.get_networks.return_value = plugin_retval
|
|
||||||
self.callbacks.get_active_networks_info(mock.Mock(), host='host',
|
|
||||||
enable_dhcp_filter=enable_dhcp_filter)
|
|
||||||
filters = {'network_id': ['a', 'b']}
|
|
||||||
if enable_dhcp_filter:
|
|
||||||
filters['enable_dhcp'] = [True]
|
|
||||||
self.plugin.get_subnets.assert_called_once_with(mock.ANY,
|
|
||||||
filters=filters)
|
|
||||||
|
|
||||||
def test_get_active_networks_info_enable_dhcp_filter_false(self):
|
|
||||||
self._test_get_active_networks_info_enable_dhcp_filter(False)
|
|
||||||
|
|
||||||
def test_get_active_networks_info_enable_dhcp_filter_true(self):
|
|
||||||
self._test_get_active_networks_info_enable_dhcp_filter(True)
|
|
||||||
|
|
||||||
def _test__port_action_with_failures(self, exc=None, action=None):
|
def _test__port_action_with_failures(self, exc=None, action=None):
|
||||||
port = {
|
port = {
|
||||||
|
@ -217,10 +187,17 @@ class TestDhcpRpcCallback(base.BaseTestCase):
|
||||||
|
|
||||||
@mock.patch.object(network_obj.Network, 'get_object')
|
@mock.patch.object(network_obj.Network, 'get_object')
|
||||||
def _test_get_network_info(self, mock_net_get_object,
|
def _test_get_network_info(self, mock_net_get_object,
|
||||||
segmented_network=False, routed_network=False):
|
segmented_network=False, routed_network=False,
|
||||||
def _network_to_dict(network, ports):
|
network_info=False, enable_dhcp=True):
|
||||||
|
def _network_to_dict(network, ports, enable_dhcp):
|
||||||
segment_ids = ['1']
|
segment_ids = ['1']
|
||||||
subnets = [_make_subnet_dict(sn) for sn in network.db_obj.subnets]
|
if enable_dhcp is None:
|
||||||
|
subnets = [_make_subnet_dict(sn) for sn in
|
||||||
|
network.db_obj.subnets]
|
||||||
|
else:
|
||||||
|
subnets = [_make_subnet_dict(sn) for sn in
|
||||||
|
network.db_obj.subnets if
|
||||||
|
sn.enable_dhcp == enable_dhcp]
|
||||||
if routed_network:
|
if routed_network:
|
||||||
non_local_subnets = [subnet for subnet in subnets
|
non_local_subnets = [subnet for subnet in subnets
|
||||||
if subnet.get('segment_id') not in
|
if subnet.get('segment_id') not in
|
||||||
|
@ -246,27 +223,34 @@ class TestDhcpRpcCallback(base.BaseTestCase):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
if not routed_network:
|
if not routed_network:
|
||||||
subnets = [mock.Mock(id='a'), mock.Mock(id='c'), mock.Mock(id='b')]
|
subnets = [mock.Mock(id='a', enable_dhcp=True),
|
||||||
|
mock.Mock(id='c', enable_dhcp=True),
|
||||||
|
mock.Mock(id='b', enable_dhcp=False)]
|
||||||
else:
|
else:
|
||||||
subnets = [mock.Mock(id='a', segment_id='1'),
|
subnets = [mock.Mock(id='a', segment_id='1', enable_dhcp=True),
|
||||||
mock.Mock(id='c', segment_id='2'),
|
mock.Mock(id='c', segment_id='2', enable_dhcp=True),
|
||||||
mock.Mock(id='b', segment_id='1')]
|
mock.Mock(id='b', segment_id='1', enable_dhcp=False)]
|
||||||
db_obj = mock.Mock(subnets=subnets)
|
db_obj = mock.Mock(subnets=subnets)
|
||||||
project_id = uuidutils.generate_uuid()
|
project_id = uuidutils.generate_uuid()
|
||||||
network = mock.Mock(id='a', admin_state_up=True, db_obj=db_obj,
|
network = mock.Mock(id='a', admin_state_up=True, db_obj=db_obj,
|
||||||
project_id=project_id, mtu=1234)
|
project_id=project_id, mtu=1234)
|
||||||
ports = mock.Mock()
|
ports = mock.Mock()
|
||||||
mock_net_get_object.return_value = network
|
if not network_info:
|
||||||
self.plugin.get_ports.return_value = ports
|
mock_net_get_object.return_value = network
|
||||||
|
self.plugin.get_ports.return_value = ports
|
||||||
self.plugin._make_subnet_dict = _make_subnet_dict
|
self.plugin._make_subnet_dict = _make_subnet_dict
|
||||||
|
|
||||||
if segmented_network:
|
if segmented_network:
|
||||||
network.segments = [mock.Mock(id='1', hosts=['host1']),
|
network.segments = [mock.Mock(id='1', hosts=['host1']),
|
||||||
mock.Mock(id='2', hosts=['host2'])]
|
mock.Mock(id='2', hosts=['host2'])]
|
||||||
|
|
||||||
retval = self.callbacks.get_network_info(mock.Mock(), network_id='a',
|
_kwargs = {'network_id': 'a', 'host': 'host1'}
|
||||||
host='host1')
|
if network_info:
|
||||||
reference = _network_to_dict(network, ports)
|
_kwargs.update({'network': network,
|
||||||
|
'enable_dhcp': enable_dhcp,
|
||||||
|
'ports': ports})
|
||||||
|
retval = self.callbacks.get_network_info(mock.Mock(), **_kwargs)
|
||||||
|
reference = _network_to_dict(network, ports, enable_dhcp)
|
||||||
self.assertEqual(reference, retval)
|
self.assertEqual(reference, retval)
|
||||||
|
|
||||||
def test_get_network_info(self):
|
def test_get_network_info(self):
|
||||||
|
@ -282,6 +266,12 @@ class TestDhcpRpcCallback(base.BaseTestCase):
|
||||||
def test_get_network_info_with_non_segmented_network(self):
|
def test_get_network_info_with_non_segmented_network(self):
|
||||||
self._test_get_network_info()
|
self._test_get_network_info()
|
||||||
|
|
||||||
|
def test_get_network_info_with_network_info_provided(self):
|
||||||
|
self._test_get_network_info(network_info=True)
|
||||||
|
self._test_get_network_info(network_info=True, enable_dhcp=True)
|
||||||
|
self._test_get_network_info(network_info=True, enable_dhcp=False)
|
||||||
|
self._test_get_network_info(network_info=True, enable_dhcp=None)
|
||||||
|
|
||||||
def test_update_dhcp_port_verify_port_action_port_dict(self):
|
def test_update_dhcp_port_verify_port_action_port_dict(self):
|
||||||
port = {'port': {'network_id': 'foo_network_id',
|
port = {'port': {'network_id': 'foo_network_id',
|
||||||
'device_owner': constants.DEVICE_OWNER_DHCP,
|
'device_owner': constants.DEVICE_OWNER_DHCP,
|
||||||
|
|
Loading…
Reference in New Issue