[L3][QoS] Cover mixed dvr_snat and compute node dvr router

The dvr_snat node can also run the nova-compute which will
make the fip_qos l3 agent extension failed to install the
qos tc rules for the dvr router floating IPs.

To cover such mixed deployment, the tc rules for floating IP
qos will be processed again in qrouter-namespace of the
dvr_snat node, and the device is rfp-device.

Change-Id: I9eca25201e0647d4da77b325fba03cca1db5bb4d
Closes-Bug: #1758316
This commit is contained in:
LIU Yulong 2018-04-04 15:33:00 +08:00
parent 805359d9a2
commit 35365ead42
3 changed files with 117 additions and 9 deletions

View File

@ -158,12 +158,17 @@ class FipQosAgentExtension(l3_extension.L3AgentExtension):
if not router_info:
continue
device = self._get_rate_limit_ip_device(router_info)
if not device:
dvr_fip_device = self._get_dvr_fip_device(router_info)
if not device and not dvr_fip_device:
LOG.debug("Router %s does not have a floating IP "
"related device, skipping.", router_id)
continue
rates = self.get_policy_rates(qos_policy)
self.process_ip_rates(fip, device, rates)
if device:
self.process_ip_rates(fip, device, rates)
if dvr_fip_device:
self.process_ip_rates(
fip, dvr_fip_device, rates, with_cache=False)
self.fip_qos_map.update_policy(qos_policy)
def _process_reset_fip(self, fip):
@ -281,12 +286,30 @@ class FipQosAgentExtension(l3_extension.L3AgentExtension):
"burst": FIP_DEFAULT_BURST}
return rates
def process_ip_rates(self, fip, device, rates):
def process_ip_rates(self, fip, device, rates, with_cache=True):
for direction in constants.VALID_DIRECTIONS:
rate = rates.get(direction)
self.process_ip_rate_limit(
fip, direction, device,
rate['rate'], rate['burst'])
if with_cache:
self.process_ip_rate_limit(
fip, direction, device,
rate['rate'], rate['burst'])
else:
tc_wrapper = self._get_tc_wrapper(device)
tc_wrapper.set_ip_rate_limit(direction, fip,
rate['rate'], rate['burst'])
def _get_dvr_fip_device(self, router_info):
is_distributed_router = router_info.router.get('distributed')
agent_mode = router_info.agent_conf.agent_mode
if is_distributed_router and agent_mode == (
constants.L3_AGENT_MODE_DVR_SNAT):
gw_port = router_info.get_ex_gw_port()
if gw_port and router_info.fip_ns:
rfp_dev_name = router_info.get_external_device_interface_name(
gw_port)
if router_info.router_namespace.exists() and rfp_dev_name:
return ip_lib.IPDevice(
rfp_dev_name, namespace=router_info.ns_name)
def process_floating_ip_addresses(self, context, router_info):
# Loop all the router floating ips, the corresponding floating IP tc
@ -302,15 +325,30 @@ class FipQosAgentExtension(l3_extension.L3AgentExtension):
# the namespace is snat-x, and the device is qg-device.
# 3. for dvr local router, if agent_mod is dvr_no_external, no
# floating IP rules will be configured.
# 4. for dvr router in snat node, we should process the floating
# IP QoS again in qrouter-namespace to cover the mixed deployment
# with nova-compute scenario.
is_distributed_router = router_info.router.get('distributed')
agent_mode = router_info.agent_conf.agent_mode
LOG.debug("Start processing floating IP QoS for "
"router %(router_id)s, router "
"distributed: %(distributed)s, "
"agent mode: %(agent_mode)s",
{"router_id": router_info.router_id,
"distributed": is_distributed_router,
"agent_mode": agent_mode})
if is_distributed_router and agent_mode == (
constants.L3_AGENT_MODE_DVR_NO_EXTERNAL):
# condition 3: dvr local router and dvr_no_external agent
return
device = self._get_rate_limit_ip_device(router_info)
if not device:
dvr_fip_device = self._get_dvr_fip_device(router_info)
if not device and not dvr_fip_device:
LOG.debug("No relevant QoS device found "
"for router: %s", router_info.router_id)
return
floating_ips = router_info.get_floating_ips()
current_fips = self.fip_qos_map.router_floating_ips.get(
router_info.router_id, set())
@ -321,12 +359,25 @@ class FipQosAgentExtension(l3_extension.L3AgentExtension):
rates = self.get_fip_qos_rates(context,
fip_addr,
fip.get(qos_consts.QOS_POLICY_ID))
self.process_ip_rates(fip_addr, device, rates)
if device:
self.process_ip_rates(fip_addr, device, rates)
if dvr_fip_device:
# NOTE(liuyulong): for scenario 4 (mixed dvr_snat and compute
# node), because floating IP qos rates may have been
# processed in dvr snat-namespace, so here the cache was
# already set. We just install the rules to the device in
# qrouter-namesapce.
self.process_ip_rates(
fip_addr, dvr_fip_device, rates, with_cache=False)
self.fip_qos_map.router_floating_ips[router_info.router_id] = new_fips
fips_removed = current_fips - new_fips
for fip in fips_removed:
self._remove_fip_rate_limit(device, fip)
if device:
self._remove_fip_rate_limit(device, fip)
if dvr_fip_device:
self._remove_fip_rate_limit(dvr_fip_device, fip)
self._process_reset_fip(fip)
def _get_router_info(self, router_id):

View File

@ -19,6 +19,7 @@ from oslo_utils import uuidutils
from neutron.agent.l3 import agent as neutron_l3_agent
from neutron.agent.l3.extensions import fip_qos
from neutron.agent.linux import ip_lib
from neutron.common import exceptions
from neutron.common import utils as common_utils
from neutron.objects.qos import policy
@ -245,6 +246,36 @@ class TestL3AgentFipQosExtensionDVR(
def test_ha_dvr_dvr_fip_snat_qos(self):
self._test_dvr_fip_snat_bound_agent_mode_dvr_snat(enable_ha=True)
def _assert_dvr_snat_qrouter_ns_rule_is_set(self, device, ip, rule):
tc_wrapper = self.fip_qos_ext._get_tc_wrapper(device)
def get_filter_id():
try:
return tc_wrapper.get_filter_id_for_ip(rule.direction, ip)
except exceptions.FilterIDForIPNotFound:
pass
common_utils.wait_until_true(get_filter_id)
def test_dvr_snat_qos_rules_set_in_qrouter(self):
self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR_SNAT
router_info = self.generate_dvr_router_info(
enable_snat=True,
enable_gw=True,
enable_floating_ip=True,
qos_policy_id=TEST_POLICY_ID1)
ri = self.manage_router(self.agent, router_info)
gw_port = ri.get_ex_gw_port()
rfp_dev_name = ri.get_external_device_interface_name(gw_port)
if ri.router_namespace.exists():
dvr_fip_device = ip_lib.IPDevice(
rfp_dev_name, namespace=ri.ns_name)
self._assert_dvr_snat_qrouter_ns_rule_is_set(
dvr_fip_device, '19.4.4.2', self.test_bw_limit_rule_1)
self._assert_dvr_snat_qrouter_ns_rule_is_set(
dvr_fip_device, '19.4.4.2', self.test_bw_limit_rule_2)
class LinuxBridgeL3AgentFipQosExtensionTestCase(TestL3AgentFipQosExtension):
INTERFACE_DRIVER = 'neutron.agent.linux.interface.BridgeInterfaceDriver'

View File

@ -349,6 +349,32 @@ class FipQosExtensionTestCase(QosExtensionBaseTestCase):
self.fip_qos_ext.add_router(self.context, self.router)
tc_wrapper.set_ip_rate_limit.assert_not_called()
def _test_process_ip_rates(self, with_cache):
rates = {'egress': {'rate': 333, 'burst': 444},
'ingress': {'rate': 111, 'burst': 222}}
fip = '123.123.123.123'
device = mock.Mock()
tc_wrapper = mock.Mock()
with mock.patch.object(
self.fip_qos_ext, '_get_tc_wrapper',
return_value=tc_wrapper) as get_tc_wrapper:
with mock.patch.object(
self.fip_qos_ext, 'process_ip_rate_limit') as process_ip:
self.fip_qos_ext.process_ip_rates(
fip, device, rates, with_cache=with_cache)
if with_cache:
self.assertEqual(2, process_ip.call_count)
else:
self.assertEqual(2, get_tc_wrapper.call_count)
self.assertEqual(
2, tc_wrapper.set_ip_rate_limit.call_count)
def test_process_ip_rates_with_cache(self):
self._test_process_ip_rates(with_cache=True)
def test_process_ip_rates_without_cache(self):
self._test_process_ip_rates(with_cache=False)
class RouterFipRateLimitMapsTestCase(base.BaseTestCase):