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.
Conflicts:
neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py
neutron/db/l3_db.py
neutron/db/l3_dvr_db.py
Local changes:
- in a unit test, moved callback failure setup to after interface is
added to a router, because now we call to register.notify in
add_router_interface too, and the test intends to trigger failure mode
for interface removal only.
Closes-Bug: #1554825
Change-Id: Ifaab163f49e0d1c5cb3eba6efa96214104647e4e
(cherry picked from commit aa00310eef
)
This commit is contained in:
parent
97bb4b0795
commit
59236a0148
|
@ -17,6 +17,9 @@ from oslo_config import cfg
|
|||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
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):
|
||||
|
@ -169,6 +177,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:
|
||||
|
|
|
@ -664,6 +664,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||
)
|
||||
context.session.add(router_port)
|
||||
|
||||
registry.notify(resources.ROUTER_INTERFACE,
|
||||
events.AFTER_CREATE,
|
||||
self,
|
||||
context=context,
|
||||
port=port)
|
||||
|
||||
return self._make_router_interface_info(
|
||||
router.id, port['tenant_id'], port['id'], subnets[-1]['id'],
|
||||
[subnet['id'] for subnet in subnets])
|
||||
|
@ -768,6 +774,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
|
|||
port, subnets = self._remove_interface_by_subnet(
|
||||
context, router_id, subnet_id, device_owner)
|
||||
|
||||
registry.notify(resources.ROUTER_INTERFACE,
|
||||
events.AFTER_DELETE,
|
||||
self,
|
||||
context=context,
|
||||
port=port)
|
||||
return self._make_router_interface_info(router_id, port['tenant_id'],
|
||||
port['id'], subnets[0]['id'],
|
||||
[subnet['id'] for subnet in
|
||||
|
|
|
@ -292,6 +292,20 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
|
|||
[subnet['id'] for subnet in subnets])
|
||||
self.notify_router_interface_action(
|
||||
context, router_interface_info, 'add')
|
||||
if router.gw_port:
|
||||
gw_network_id = router.gw_port.network_id
|
||||
gw_ips = [x['ip_address'] for x in router.gw_port.fixed_ips]
|
||||
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
|
||||
|
||||
def _port_has_ipv6_address(self, port):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1619,6 +1619,10 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
|
|||
with self.router() as r,\
|
||||
self.subnet() as s,\
|
||||
mock.patch.object(registry, 'notify') as notify:
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
s['subnet']['id'],
|
||||
None)
|
||||
errors = [
|
||||
exceptions.NotificationError(
|
||||
'foo_callback_id', n_exc.InUse()),
|
||||
|
@ -1628,10 +1632,6 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
|
|||
notify.side_effect = [
|
||||
exceptions.CallbackFailure(errors=errors), None
|
||||
]
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
s['subnet']['id'],
|
||||
None)
|
||||
self._router_interface_action(
|
||||
'remove',
|
||||
r['router']['id'],
|
||||
|
|
Loading…
Reference in New Issue