DSCP packet marking support in Linuxbridge agent
Linuxbridge agent uses iptable rules in POSTROUTING chain in the mangle table to mark outgoing packets with the DSCP mark value configured by the user in QoS policy. DocImpact: DSCP Marking rule support is extended to the Linuxbridge L2 agent Closes-Bug: #1644369 Change-Id: I47e44cb2e67ab73bd5ee0aa4cca47cb3d07e43f3
This commit is contained in:
parent
b9d0a5b885
commit
fd3bf3327c
@ -18,7 +18,9 @@ from oslo_log import log
|
||||
|
||||
from neutron._i18n import _LI
|
||||
from neutron.agent.l2.extensions import qos
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.agent.linux import tc_lib
|
||||
import neutron.common.constants as const
|
||||
from neutron.plugins.ml2.drivers.linuxbridge.mech_driver import (
|
||||
mech_linuxbridge)
|
||||
|
||||
@ -31,8 +33,28 @@ class QosLinuxbridgeAgentDriver(qos.QosAgentDriver):
|
||||
mech_linuxbridge.LinuxbridgeMechanismDriver.supported_qos_rule_types
|
||||
)
|
||||
|
||||
IPTABLES_DIRECTION = {const.INGRESS_DIRECTION: 'physdev-out',
|
||||
const.EGRESS_DIRECTION: 'physdev-in'}
|
||||
IPTABLES_DIRECTION_PREFIX = {const.INGRESS_DIRECTION: "i",
|
||||
const.EGRESS_DIRECTION: "o"}
|
||||
|
||||
def initialize(self):
|
||||
LOG.info(_LI("Initializing Linux bridge QoS extension"))
|
||||
self.iptables_manager = iptables_manager.IptablesManager(use_ipv6=True)
|
||||
|
||||
def _dscp_chain_name(self, direction, device):
|
||||
return iptables_manager.get_chain_name(
|
||||
"qos-%s%s" % (self.IPTABLES_DIRECTION_PREFIX[direction],
|
||||
device[3:]))
|
||||
|
||||
def _dscp_rule(self, direction, device):
|
||||
return ('-m physdev --%s %s --physdev-is-bridged '
|
||||
'-j $%s') % (self.IPTABLES_DIRECTION[direction],
|
||||
device,
|
||||
self._dscp_chain_name(direction, device))
|
||||
|
||||
def _dscp_rule_tag(self, device):
|
||||
return "dscp-%s" % device
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_bandwidth_limit(self, port, rule):
|
||||
@ -53,6 +75,74 @@ class QosLinuxbridgeAgentDriver(qos.QosAgentDriver):
|
||||
tc_wrapper = self._get_tc_wrapper(port)
|
||||
tc_wrapper.delete_filters_bw_limit()
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_dscp_marking(self, port, rule):
|
||||
with self.iptables_manager.defer_apply():
|
||||
self._set_outgoing_qos_chain_for_port(port)
|
||||
self._set_dscp_mark_rule(port, rule.dscp_mark)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_dscp_marking(self, port, rule):
|
||||
with self.iptables_manager.defer_apply():
|
||||
self._delete_dscp_mark_rule(port)
|
||||
self._set_outgoing_qos_chain_for_port(port)
|
||||
self._set_dscp_mark_rule(port, rule.dscp_mark)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_dscp_marking(self, port):
|
||||
with self.iptables_manager.defer_apply():
|
||||
self._delete_dscp_mark_rule(port)
|
||||
self._delete_outgoing_qos_chain_for_port(port)
|
||||
|
||||
def _set_outgoing_qos_chain_for_port(self, port):
|
||||
chain_name = self._dscp_chain_name(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
chain_rule = self._dscp_rule(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
self.iptables_manager.ipv4['mangle'].add_chain(chain_name)
|
||||
self.iptables_manager.ipv6['mangle'].add_chain(chain_name)
|
||||
|
||||
self.iptables_manager.ipv4['mangle'].add_rule('POSTROUTING',
|
||||
chain_rule)
|
||||
self.iptables_manager.ipv6['mangle'].add_rule('POSTROUTING',
|
||||
chain_rule)
|
||||
|
||||
def _delete_outgoing_qos_chain_for_port(self, port):
|
||||
chain_name = self._dscp_chain_name(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
chain_rule = self._dscp_rule(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
if self._qos_chain_is_empty(port, 4):
|
||||
self.iptables_manager.ipv4['mangle'].remove_chain(chain_name)
|
||||
self.iptables_manager.ipv4['mangle'].remove_rule('POSTROUTING',
|
||||
chain_rule)
|
||||
if self._qos_chain_is_empty(port, 6):
|
||||
self.iptables_manager.ipv6['mangle'].remove_chain(chain_name)
|
||||
self.iptables_manager.ipv6['mangle'].remove_rule('POSTROUTING',
|
||||
chain_rule)
|
||||
|
||||
def _set_dscp_mark_rule(self, port, dscp_value):
|
||||
chain_name = self._dscp_chain_name(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
rule = "-j DSCP --set-dscp %s" % dscp_value
|
||||
self.iptables_manager.ipv4['mangle'].add_rule(
|
||||
chain_name, rule, tag=self._dscp_rule_tag(port['device']))
|
||||
self.iptables_manager.ipv6['mangle'].add_rule(
|
||||
chain_name, rule, tag=self._dscp_rule_tag(port['device']))
|
||||
|
||||
def _delete_dscp_mark_rule(self, port):
|
||||
self.iptables_manager.ipv4['mangle'].clear_rules_by_tag(
|
||||
self._dscp_rule_tag(port['device']))
|
||||
self.iptables_manager.ipv6['mangle'].clear_rules_by_tag(
|
||||
self._dscp_rule_tag(port['device']))
|
||||
|
||||
def _qos_chain_is_empty(self, port, ip_version=4):
|
||||
chain_name = self._dscp_chain_name(
|
||||
const.EGRESS_DIRECTION, port['device'])
|
||||
rules_in_chain = self.iptables_manager.get_chain(
|
||||
"mangle", chain_name, ip_version=ip_version)
|
||||
return len(rules_in_chain) == 0
|
||||
|
||||
def _get_tc_wrapper(self, port):
|
||||
return tc_lib.TcCommand(
|
||||
port['device'],
|
||||
|
@ -32,7 +32,8 @@ class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
network.
|
||||
"""
|
||||
|
||||
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]
|
||||
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING]
|
||||
|
||||
def __init__(self):
|
||||
sg_enabled = securitygroups_rpc.is_firewall_enabled()
|
||||
|
@ -16,6 +16,7 @@
|
||||
import re
|
||||
|
||||
from neutron.agent.linux import async_process
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.common import utils as common_utils
|
||||
|
||||
|
||||
@ -38,6 +39,15 @@ def extract_mod_nw_tos_action(flows):
|
||||
return tos_mark
|
||||
|
||||
|
||||
def extract_dscp_value_from_iptables_rules(rules):
|
||||
pattern = (r"^-A neutron-linuxbri-qos-.* -j DSCP "
|
||||
"--set-dscp (?P<dscp_value>0x[A-Fa-f0-9]+)$")
|
||||
for rule in rules:
|
||||
m = re.match(pattern, rule)
|
||||
if m:
|
||||
return int(m.group("dscp_value"), 16)
|
||||
|
||||
|
||||
def wait_until_bandwidth_limit_rule_applied(bridge, port_vif, rule):
|
||||
def _bandwidth_limit_rule_applied():
|
||||
bw_rule = bridge.get_egress_bw_limit_for_port(port_vif)
|
||||
@ -49,7 +59,7 @@ def wait_until_bandwidth_limit_rule_applied(bridge, port_vif, rule):
|
||||
common_utils.wait_until_true(_bandwidth_limit_rule_applied)
|
||||
|
||||
|
||||
def wait_until_dscp_marking_rule_applied(bridge, port_vif, rule):
|
||||
def wait_until_dscp_marking_rule_applied_ovs(bridge, port_vif, rule):
|
||||
def _dscp_marking_rule_applied():
|
||||
port_num = bridge.get_port_ofport(port_vif)
|
||||
|
||||
@ -64,6 +74,20 @@ def wait_until_dscp_marking_rule_applied(bridge, port_vif, rule):
|
||||
common_utils.wait_until_true(_dscp_marking_rule_applied)
|
||||
|
||||
|
||||
def wait_until_dscp_marking_rule_applied_linuxbridge(
|
||||
namespace, port_vif, expected_rule):
|
||||
|
||||
iptables = iptables_manager.IptablesManager(
|
||||
namespace=namespace)
|
||||
|
||||
def _dscp_marking_rule_applied():
|
||||
mangle_rules = iptables.get_rules_for_table("mangle")
|
||||
dscp_mark = extract_dscp_value_from_iptables_rules(mangle_rules)
|
||||
return dscp_mark == expected_rule
|
||||
|
||||
common_utils.wait_until_true(_dscp_marking_rule_applied)
|
||||
|
||||
|
||||
def wait_for_dscp_marked_packet(sender_vm, receiver_vm, dscp_mark):
|
||||
cmd = ["tcpdump", "-i", receiver_vm.port.name, "-nlt"]
|
||||
if dscp_mark:
|
||||
|
@ -45,14 +45,18 @@ DSCP_MARK = 16
|
||||
class BaseQoSRuleTestCase(object):
|
||||
of_interface = None
|
||||
ovsdb_interface = None
|
||||
number_of_hosts = 1
|
||||
|
||||
def setUp(self):
|
||||
host_desc = [environment.HostDescription(
|
||||
l3_agent=False,
|
||||
of_interface=self.of_interface,
|
||||
ovsdb_interface=self.ovsdb_interface,
|
||||
l2_agent_type=self.l2_agent_type)]
|
||||
env_desc = environment.EnvironmentDescription(qos=True)
|
||||
host_desc = [
|
||||
environment.HostDescription(
|
||||
l3_agent=False,
|
||||
of_interface=self.of_interface,
|
||||
ovsdb_interface=self.ovsdb_interface,
|
||||
l2_agent_type=self.l2_agent_type
|
||||
) for _ in range(self.number_of_hosts)]
|
||||
env_desc = environment.EnvironmentDescription(
|
||||
qos=True)
|
||||
env = environment.Environment(env_desc, host_desc)
|
||||
super(BaseQoSRuleTestCase, self).setUp(env)
|
||||
|
||||
@ -95,6 +99,9 @@ class BaseQoSRuleTestCase(object):
|
||||
|
||||
|
||||
class _TestBwLimitQoS(BaseQoSRuleTestCase):
|
||||
|
||||
number_of_hosts = 1
|
||||
|
||||
def _wait_for_bw_rule_removed(self, vm):
|
||||
# No values are provided when port doesn't have qos policy
|
||||
self._wait_for_bw_rule_applied(vm, None, None)
|
||||
@ -172,36 +179,9 @@ class TestBwLimitQoSLinuxbridge(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
lambda: tc.get_filters_bw_limits() == (limit, burst))
|
||||
|
||||
|
||||
class TestDscpMarkingQoSOvs(BaseQoSRuleTestCase, base.BaseFullStackTestCase):
|
||||
scenarios = fullstack_utils.get_ovs_interface_scenarios()
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
class _TestDscpMarkingQoS(BaseQoSRuleTestCase):
|
||||
|
||||
def setUp(self):
|
||||
host_desc = [
|
||||
environment.HostDescription(
|
||||
l3_agent=False,
|
||||
of_interface=self.of_interface,
|
||||
ovsdb_interface=self.ovsdb_interface,
|
||||
l2_agent_type=self.l2_agent_type
|
||||
) for _ in range(2)]
|
||||
env_desc = environment.EnvironmentDescription(
|
||||
qos=True)
|
||||
env = environment.Environment(env_desc, host_desc)
|
||||
super(BaseQoSRuleTestCase, self).setUp(env)
|
||||
|
||||
self.tenant_id = uuidutils.generate_uuid()
|
||||
self.network = self.safe_client.create_network(self.tenant_id,
|
||||
'network-test')
|
||||
self.subnet = self.safe_client.create_subnet(
|
||||
self.tenant_id, self.network['id'],
|
||||
cidr='10.0.0.0/24',
|
||||
gateway_ip='10.0.0.1',
|
||||
name='subnet-test',
|
||||
enable_dhcp=False)
|
||||
|
||||
def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark):
|
||||
l2_extensions.wait_until_dscp_marking_rule_applied(
|
||||
vm.bridge, vm.port.name, dscp_mark)
|
||||
number_of_hosts = 2
|
||||
|
||||
def _wait_for_dscp_marking_rule_removed(self, vm):
|
||||
self._wait_for_dscp_marking_rule_applied(vm, None)
|
||||
@ -272,19 +252,29 @@ class TestDscpMarkingQoSOvs(BaseQoSRuleTestCase, base.BaseFullStackTestCase):
|
||||
sender, receiver, DSCP_MARK)
|
||||
|
||||
|
||||
class TestDscpMarkingQoSOvs(_TestDscpMarkingQoS, base.BaseFullStackTestCase):
|
||||
scenarios = fullstack_utils.get_ovs_interface_scenarios()
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
|
||||
def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark):
|
||||
l2_extensions.wait_until_dscp_marking_rule_applied_ovs(
|
||||
vm.bridge, vm.port.name, dscp_mark)
|
||||
|
||||
|
||||
class TestDscpMarkingQoSLinuxbridge(_TestDscpMarkingQoS,
|
||||
base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE
|
||||
|
||||
def _wait_for_dscp_marking_rule_applied(self, vm, dscp_mark):
|
||||
l2_extensions.wait_until_dscp_marking_rule_applied_linuxbridge(
|
||||
vm.host.host_namespace, vm.port.name, dscp_mark)
|
||||
|
||||
|
||||
class TestQoSWithL2Population(base.BaseFullStackTestCase):
|
||||
|
||||
def setUp(self):
|
||||
# We limit this test to using the openvswitch mech driver, because DSCP
|
||||
# is presently not implemented for Linux Bridge. The 'rule_types' API
|
||||
# call only returns rule types that are supported by all configured
|
||||
# mech drivers. So in a fullstack scenario, where both the OVS and the
|
||||
# Linux Bridge mech drivers are configured, the DSCP rule type will be
|
||||
# unavailable since it is not implemented in Linux Bridge.
|
||||
mech_driver = 'openvswitch'
|
||||
host_desc = [] # No need to register agents for this test case
|
||||
env_desc = environment.EnvironmentDescription(qos=True, l2_pop=True,
|
||||
mech_drivers=mech_driver)
|
||||
env_desc = environment.EnvironmentDescription(qos=True, l2_pop=True)
|
||||
env = environment.Environment(env_desc, host_desc)
|
||||
super(TestQoSWithL2Population, self).setUp(env)
|
||||
|
||||
|
@ -141,7 +141,7 @@ class OVSAgentQoSExtensionTestFramework(base.OVSAgentTestFramework):
|
||||
self.assertIsNone(tos_mark)
|
||||
|
||||
def wait_until_dscp_marking_rule_applied(self, port, dscp_mark):
|
||||
l2_extensions.wait_until_dscp_marking_rule_applied(
|
||||
l2_extensions.wait_until_dscp_marking_rule_applied_ovs(
|
||||
self.agent.int_br, port['vif_name'], dscp_mark)
|
||||
|
||||
def _create_port_with_qos(self):
|
||||
|
@ -26,6 +26,7 @@ from neutron.tests import base
|
||||
|
||||
|
||||
TEST_LATENCY_VALUE = 100
|
||||
DSCP_VALUE = 32
|
||||
|
||||
|
||||
class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase):
|
||||
@ -35,7 +36,8 @@ class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase):
|
||||
cfg.CONF.set_override("tbf_latency", TEST_LATENCY_VALUE, "QOS")
|
||||
self.qos_driver = qos_driver.QosLinuxbridgeAgentDriver()
|
||||
self.qos_driver.initialize()
|
||||
self.rule = self._create_bw_limit_rule_obj()
|
||||
self.rule_bw_limit = self._create_bw_limit_rule_obj()
|
||||
self.rule_dscp_marking = self._create_dscp_marking_rule_obj()
|
||||
self.port = self._create_fake_port(uuidutils.generate_uuid())
|
||||
|
||||
def _create_bw_limit_rule_obj(self):
|
||||
@ -46,32 +48,150 @@ class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase):
|
||||
rule_obj.obj_reset_changes()
|
||||
return rule_obj
|
||||
|
||||
def _create_dscp_marking_rule_obj(self):
|
||||
rule_obj = rule.QosDscpMarkingRule()
|
||||
rule_obj.id = uuidutils.generate_uuid()
|
||||
rule_obj.dscp_mark = DSCP_VALUE
|
||||
rule_obj.obj_reset_changes()
|
||||
return rule_obj
|
||||
|
||||
def _create_fake_port(self, policy_id):
|
||||
return {'qos_policy_id': policy_id,
|
||||
'network_qos_policy_id': None,
|
||||
'device': 'fake_tap'}
|
||||
|
||||
def test_create_rule(self):
|
||||
def _dscp_mark_chain_name(self, device):
|
||||
return "qos-o%s" % device[3:]
|
||||
|
||||
def _dscp_postrouting_rule(self, device):
|
||||
return ("-m physdev --physdev-in %s --physdev-is-bridged "
|
||||
"-j $qos-o%s") % (device, device[3:])
|
||||
|
||||
def _dscp_rule(self, dscp_mark_value):
|
||||
return "-j DSCP --set-dscp %s" % dscp_mark_value
|
||||
|
||||
def _dscp_rule_tag(self, device):
|
||||
return "dscp-%s" % device
|
||||
|
||||
def test_create_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "set_filters_bw_limit"
|
||||
) as set_bw_limit:
|
||||
self.qos_driver.create_bandwidth_limit(self.port, self.rule)
|
||||
self.qos_driver.create_bandwidth_limit(self.port,
|
||||
self.rule_bw_limit)
|
||||
set_bw_limit.assert_called_once_with(
|
||||
self.rule.max_kbps, self.rule.max_burst_kbps,
|
||||
self.rule_bw_limit.max_kbps, self.rule_bw_limit.max_burst_kbps,
|
||||
)
|
||||
|
||||
def test_update_rule(self):
|
||||
def test_update_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "update_filters_bw_limit"
|
||||
) as update_bw_limit:
|
||||
self.qos_driver.update_bandwidth_limit(self.port, self.rule)
|
||||
self.qos_driver.update_bandwidth_limit(self.port,
|
||||
self.rule_bw_limit)
|
||||
update_bw_limit.assert_called_once_with(
|
||||
self.rule.max_kbps, self.rule.max_burst_kbps,
|
||||
self.rule_bw_limit.max_kbps, self.rule_bw_limit.max_burst_kbps,
|
||||
)
|
||||
|
||||
def test_delete_rule(self):
|
||||
def test_delete_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "delete_filters_bw_limit"
|
||||
) as delete_bw_limit:
|
||||
self.qos_driver.delete_bandwidth_limit(self.port)
|
||||
delete_bw_limit.assert_called_once_with()
|
||||
|
||||
def test_create_dscp_marking(self):
|
||||
expected_calls = [
|
||||
mock.call.add_chain(
|
||||
self._dscp_mark_chain_name(self.port['device'])),
|
||||
mock.call.add_rule(
|
||||
"POSTROUTING",
|
||||
self._dscp_postrouting_rule(self.port['device'])),
|
||||
mock.call.add_rule(
|
||||
self._dscp_mark_chain_name(self.port['device']),
|
||||
self._dscp_rule(DSCP_VALUE),
|
||||
tag=self._dscp_rule_tag(self.port['device'])
|
||||
)
|
||||
]
|
||||
with mock.patch.object(
|
||||
self.qos_driver, "iptables_manager") as iptables_manager:
|
||||
|
||||
iptables_manager.ip4['mangle'] = mock.Mock()
|
||||
iptables_manager.ip6['mangle'] = mock.Mock()
|
||||
self.qos_driver.create_dscp_marking(
|
||||
self.port, self.rule_dscp_marking)
|
||||
iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls)
|
||||
|
||||
def test_update_dscp_marking(self):
|
||||
expected_calls = [
|
||||
mock.call.clear_rules_by_tag(
|
||||
self._dscp_rule_tag(self.port['device'])),
|
||||
mock.call.add_chain(
|
||||
self._dscp_mark_chain_name(self.port['device'])),
|
||||
mock.call.add_rule(
|
||||
"POSTROUTING",
|
||||
self._dscp_postrouting_rule(self.port['device'])),
|
||||
mock.call.add_rule(
|
||||
self._dscp_mark_chain_name(self.port['device']),
|
||||
self._dscp_rule(DSCP_VALUE),
|
||||
tag=self._dscp_rule_tag(self.port['device'])
|
||||
)
|
||||
]
|
||||
with mock.patch.object(
|
||||
self.qos_driver, "iptables_manager") as iptables_manager:
|
||||
|
||||
iptables_manager.ip4['mangle'] = mock.Mock()
|
||||
iptables_manager.ip6['mangle'] = mock.Mock()
|
||||
self.qos_driver.update_dscp_marking(
|
||||
self.port, self.rule_dscp_marking)
|
||||
iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls)
|
||||
|
||||
def test_delete_dscp_marking_chain_empty(self):
|
||||
dscp_chain_name = self._dscp_mark_chain_name(self.port['device'])
|
||||
expected_calls = [
|
||||
mock.call.clear_rules_by_tag(
|
||||
self._dscp_rule_tag(self.port['device'])),
|
||||
mock.call.remove_chain(
|
||||
dscp_chain_name),
|
||||
mock.call.remove_rule(
|
||||
"POSTROUTING",
|
||||
self._dscp_postrouting_rule(self.port['device']))
|
||||
]
|
||||
with mock.patch.object(
|
||||
self.qos_driver, "iptables_manager") as iptables_manager:
|
||||
|
||||
iptables_manager.ip4['mangle'] = mock.Mock()
|
||||
iptables_manager.ip6['mangle'] = mock.Mock()
|
||||
iptables_manager.get_chain = mock.Mock(return_value=[])
|
||||
self.qos_driver.delete_dscp_marking(self.port)
|
||||
iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.get_chain.assert_has_calls([
|
||||
mock.call("mangle", dscp_chain_name, ip_version=4),
|
||||
mock.call("mangle", dscp_chain_name, ip_version=6)
|
||||
])
|
||||
|
||||
def test_delete_dscp_marking_chain_not_empty(self):
|
||||
dscp_chain_name = self._dscp_mark_chain_name(self.port['device'])
|
||||
expected_calls = [
|
||||
mock.call.clear_rules_by_tag(
|
||||
self._dscp_rule_tag(self.port['device'])),
|
||||
]
|
||||
with mock.patch.object(
|
||||
self.qos_driver, "iptables_manager") as iptables_manager:
|
||||
|
||||
iptables_manager.ip4['mangle'] = mock.Mock()
|
||||
iptables_manager.ip6['mangle'] = mock.Mock()
|
||||
iptables_manager.get_chain = mock.Mock(
|
||||
return_value=["some other rule"])
|
||||
self.qos_driver.delete_dscp_marking(self.port)
|
||||
iptables_manager.ipv4['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.ipv6['mangle'].assert_has_calls(expected_calls)
|
||||
iptables_manager.get_chain.assert_has_calls([
|
||||
mock.call("mangle", dscp_chain_name, ip_version=4),
|
||||
mock.call("mangle", dscp_chain_name, ip_version=6)
|
||||
])
|
||||
iptables_manager.ipv4['mangle'].remove_chain.assert_not_called()
|
||||
iptables_manager.ipv4['mangle'].remove_rule.assert_not_called()
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
prelude: >
|
||||
The LinuxBridge agent now supports QoS DSCP marking.
|
||||
features:
|
||||
- The LinuxBridge agent can now configure DSCP marking for packets outgoing
|
||||
for ports with QoS policy.
|
Loading…
Reference in New Issue
Block a user