[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:
parent
805359d9a2
commit
35365ead42
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
Loading…
Reference in New Issue