Fix AttributeError with third-party L3 service plugins

A recent patch fix for the Rocky branch from
commit 20fd026116 have introduced an
AttributeError on Third Party Plugins that does not implement
certain extensions.

This is just related to FWaaSv1 and only applicable to Rocky and below
releases.

This change fixes an issue where in the course of sending firewall
update notifications to the appropriate agents an AttributeError
is encountered due to unsafe assumption of the availability of L3
extensions on the l3_plugin.

This change puts protections in place to avoid this issue.

Change-Id: If5fde4fc9475f468860b0c1fb29db66f1c44f74e
Closes-Bug: #1847019
This commit is contained in:
Ryan Tidwell 2019-10-07 10:46:53 -05:00 committed by Swaminathan Vasudevan
parent 20fd026116
commit fc59b08ff7
2 changed files with 29 additions and 5 deletions

View File

@ -174,6 +174,14 @@ class FirewallPlugin(
f_const.FIREWALL_PLUGIN, self.endpoints, fanout=False)
return self.conn.consume_in_threads()
def _check_dvr_extensions(self, l3plugin):
return (
extensions.is_extension_supported(
l3plugin, nl_constants.L3_AGENT_SCHEDULER_EXT_ALIAS) and
extensions.is_extension_supported(
l3plugin, nl_constants.L3_DISTRIBUTED_EXT_ALIAS) and
getattr(l3plugin, '_get_dvr_hosts_for_router', False))
def _get_hosts_to_notify(self, context, router_ids):
"""Returns all hosts to send notification about firewall update"""
l3_plugin = directory.get_plugin(plugin_constants.L3)
@ -181,19 +189,24 @@ class FirewallPlugin(
extensions.is_extension_supported(
l3_plugin, nl_constants.L3_AGENT_SCHEDULER_EXT_ALIAS) and
getattr(l3_plugin, 'get_l3_agents_hosting_routers', False))
scheduled_hosts = set()
if no_broadcast:
# This call checks for all scheduled routers to the network node
agents = l3_plugin.get_l3_agents_hosting_routers(
context, router_ids, admin_state_up=True, active=True)
scheduled_rtr_hosts = set([a.host for a in agents])
# Now check for unscheduled DVR router on distributed compute hosts
unscheduled_dvr_hosts = set()
scheduled_hosts = set([a.host for a in agents])
# Now check for unscheduled DVR router on distributed compute hosts
unscheduled_dvr_hosts = set()
dvr_broadcast = self._check_dvr_extensions(l3_plugin)
if (dvr_broadcast):
for router_id in router_ids:
hosts = set(l3_plugin._get_dvr_hosts_for_router(
context, router_id))
unscheduled_dvr_hosts |= hosts
total_hosts = scheduled_rtr_hosts.union(unscheduled_dvr_hosts)
return total_hosts
if no_broadcast or dvr_broadcast:
scheduled_hosts = scheduled_hosts.union(unscheduled_dvr_hosts)
return scheduled_hosts
# NOTE(blallau): default: FirewallAgentAPI performs RPC broadcast
return [None]

View File

@ -380,6 +380,13 @@ class TestFirewallPluginBase(TestFirewallRouterInsertionBase,
ctx = context.get_admin_context()
name = "user_fw"
attrs = self._get_test_firewall_attrs(name)
check_attr1 = getattr(self.l3_plugin,
"get_l3_agents_hosting_routers", False)
check_attr2 = getattr(self.l3_plugin,
"_get_dvr_hosts_for_router", False)
# For third-party L3-service plugins do not run this test
if check_attr1 is False or check_attr2 is False:
return
with self.router(name='router1', admin_state_up=True,
tenant_id=self._tenant_id) as router1:
with self.firewall_policy() as fwp:
@ -396,10 +403,14 @@ class TestFirewallPluginBase(TestFirewallRouterInsertionBase,
with mock.patch.object(
self.l3_plugin,
'get_l3_agents_hosting_routers') as s_hosts, \
mock.patch.object(
self.plugin,
'_check_dvr_extensions') as dvr_exts, \
mock.patch.object(
self.l3_plugin,
'_get_dvr_hosts_for_router') as u_hosts:
self.plugin.update_firewall(ctx, fw_id, firewall)
dvr_exts.return_value = True
self.assertTrue(u_hosts.called)
self.assertTrue(s_hosts.called)