Update network object in DHCP agent with router interface changes

In Dnsmasq, the function get_isolated_subnets() returns a list of
subnets in a network if the subnet is not connected to a router.

The implementation of this function checks all the router interface
ports in a cached network object passed from DHCP agent. But the
cached network object is not updated when a subnet is attached to
or detached from a router.

This patch fixes that by adding callback functions in DHCP RPC client
to notify DHCP agent when changes happen on router interfaces.

Closes-Bug: #1554825
Change-Id: Ifaab163f49e0d1c5cb3eba6efa96214104647e4e
This commit is contained in:
Shih-Hao Li 2016-03-11 15:13:34 -08:00
parent f36fe29556
commit aa00310eef
4 changed files with 51 additions and 6 deletions

View File

@ -18,6 +18,9 @@ from oslo_log import log as logging
import oslo_messaging
from neutron._i18n import _LE, _LW
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants
from neutron.common import rpc as n_rpc
from neutron.common import topics
@ -51,6 +54,11 @@ class DhcpAgentNotifyAPI(object):
self._plugin = plugin
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
# register callbacks for router interface changes
registry.subscribe(self._after_router_interface_created,
resources.ROUTER_INTERFACE, events.AFTER_CREATE)
registry.subscribe(self._after_router_interface_deleted,
resources.ROUTER_INTERFACE, events.AFTER_DELETE)
@property
def plugin(self):
@ -171,6 +179,18 @@ class DhcpAgentNotifyAPI(object):
self._cast_message(context, 'agent_updated',
{'admin_state_up': admin_state_up}, host)
def _after_router_interface_created(self, resource, event, trigger,
**kwargs):
self._notify_agents(kwargs['context'], 'port_create_end',
{'port': kwargs['port']},
kwargs['port']['network_id'])
def _after_router_interface_deleted(self, resource, event, trigger,
**kwargs):
self._notify_agents(kwargs['context'], 'port_delete_end',
{'port_id': kwargs['port']['id']},
kwargs['port']['network_id'])
def notify(self, context, data, method_name):
# data is {'key' : 'value'} with only one key
if method_name not in self.VALID_METHOD_NAMES:

View File

@ -712,11 +712,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
registry.notify(resources.ROUTER_INTERFACE,
events.AFTER_CREATE,
self,
context=context,
network_id=gw_network_id,
gateway_ips=gw_ips,
cidrs=[x['cidr'] for x in subnets],
port_id=port['id'],
router_id=router_id,
port=port,
interface_info=interface_info)
return self._make_router_interface_info(
@ -833,9 +835,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
registry.notify(resources.ROUTER_INTERFACE,
events.AFTER_DELETE,
self,
context=context,
cidrs=[x['cidr'] for x in subnets],
network_id=gw_network_id,
gateway_ips=gw_ips)
gateway_ips=gw_ips,
port=port)
return self._make_router_interface_info(router_id, port['tenant_id'],
port['id'], port['network_id'],
subnets[0]['id'],

View File

@ -309,11 +309,13 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
registry.notify(resources.ROUTER_INTERFACE,
events.AFTER_CREATE,
self,
context=context,
network_id=gw_network_id,
gateway_ips=gw_ips,
cidrs=[x['cidr'] for x in subnets],
port_id=port['id'],
router_id=router_id,
port=port,
interface_info=interface_info)
return router_interface_info

View File

@ -138,20 +138,25 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
mock.ANY, 'foo_network_id')
self.assertEqual(1, self.mock_fanout.call_count)
def _test__notify_agents(self, method,
expected_scheduling=0, expected_casts=0):
def _test__notify_agents_with_function(
self, function, expected_scheduling=0, expected_casts=0):
with mock.patch.object(self.notifier, '_schedule_network') as f:
with mock.patch.object(self.notifier, '_get_enabled_agents') as g:
agent = agents_db.Agent()
agent.admin_state_up = True
agent.heartbeat_timestamp = timeutils.utcnow()
g.return_value = [agent]
dummy_payload = {'port': {}}
self.notifier._notify_agents(mock.Mock(), method,
dummy_payload, 'foo_network_id')
function()
self.assertEqual(expected_scheduling, f.call_count)
self.assertEqual(expected_casts, self.mock_cast.call_count)
def _test__notify_agents(self, method,
expected_scheduling=0, expected_casts=0):
self._test__notify_agents_with_function(
lambda: self.notifier._notify_agents(
mock.Mock(), method, {'port': {}}, 'foo_network_id'),
expected_scheduling, expected_casts)
def test__notify_agents_cast_required_with_scheduling(self):
self._test__notify_agents('port_create_end',
expected_scheduling=1, expected_casts=1)
@ -168,6 +173,20 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
self._test__notify_agents('network_create_end',
expected_scheduling=0, expected_casts=0)
def test__notify_agents_with_router_interface_add(self):
self._test__notify_agents_with_function(
lambda: self.notifier._after_router_interface_created(
mock.ANY, mock.ANY, mock.ANY, context=mock.Mock(),
port={'id': 'foo_port_id', 'network_id': 'foo_network_id'}),
expected_scheduling=1, expected_casts=1)
def test__notify_agents_with_router_interface_delete(self):
self._test__notify_agents_with_function(
lambda: self.notifier._after_router_interface_deleted(
mock.ANY, mock.ANY, mock.ANY, context=mock.Mock(),
port={'id': 'foo_port_id', 'network_id': 'foo_network_id'}),
expected_scheduling=0, expected_casts=1)
def test__fanout_message(self):
self.notifier._fanout_message(mock.ANY, mock.ANY, mock.ANY)
self.assertEqual(1, self.mock_fanout.call_count)