Merge "Ingress bandwidth limit rule in Linuxbridge agent"
This commit is contained in:
commit
2ce4f19afd
@ -283,7 +283,7 @@ point of view)
|
||||
+----------------------+----------------+----------------+----------------+
|
||||
| Rule \ Backend | Open vSwitch | SR-IOV | Linux Bridge |
|
||||
+----------------------+----------------+----------------+----------------+
|
||||
| Bandwidth Limit | Egress/Ingress | Egress (1) | Egress |
|
||||
| Bandwidth Limit | Egress/Ingress | Egress (1) | Egress/Ingress |
|
||||
+----------------------+----------------+----------------+----------------+
|
||||
| Minimum Bandwidth | - | Egress | - |
|
||||
+----------------------+----------------+----------------+----------------+
|
||||
@ -348,11 +348,19 @@ value.
|
||||
Linux bridge
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The Linux bridge implementation relies on the new tc_lib functions:
|
||||
The Linux bridge implementation relies on the new tc_lib functions.
|
||||
|
||||
* set_bw_limit
|
||||
* update_bw_limit
|
||||
* delete_bw_limit
|
||||
For egress bandwidth limit rule:
|
||||
|
||||
* set_filters_bw_limit
|
||||
* update_filters_bw_limit
|
||||
* delete_filters_bw_limit
|
||||
|
||||
For ingress bandwidth limit rule:
|
||||
|
||||
* set_tbf_bw_limit
|
||||
* update_tbf_bw_limit
|
||||
* delete_tbf_bw_limit
|
||||
|
||||
The ingress bandwidth limit is configured on the tap port by setting a simple
|
||||
`tc-tbf <http://linux.die.net/man/8/tc-tbf>`_ queueing discipline (qdisc) on the
|
||||
|
@ -43,6 +43,7 @@ class QosLinuxbridgeAgentDriver(qos.QosLinuxAgentDriver):
|
||||
def initialize(self):
|
||||
LOG.info(_LI("Initializing Linux bridge QoS extension"))
|
||||
self.iptables_manager = iptables_manager.IptablesManager(use_ipv6=True)
|
||||
self.tbf_latency = cfg.CONF.QOS.tbf_latency
|
||||
|
||||
def _dscp_chain_name(self, direction, device):
|
||||
return iptables_manager.get_chain_name(
|
||||
@ -61,22 +62,35 @@ class QosLinuxbridgeAgentDriver(qos.QosLinuxAgentDriver):
|
||||
@log_helpers.log_method_call
|
||||
def create_bandwidth_limit(self, port, rule):
|
||||
tc_wrapper = self._get_tc_wrapper(port)
|
||||
tc_wrapper.set_filters_bw_limit(
|
||||
rule.max_kbps, self._get_egress_burst_value(rule)
|
||||
)
|
||||
if rule.direction == const.INGRESS_DIRECTION:
|
||||
tc_wrapper.set_tbf_bw_limit(
|
||||
rule.max_kbps, rule.max_burst_kbps, self.tbf_latency)
|
||||
else:
|
||||
tc_wrapper.set_filters_bw_limit(
|
||||
rule.max_kbps, self._get_egress_burst_value(rule)
|
||||
)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_bandwidth_limit(self, port, rule):
|
||||
tc_wrapper = self._get_tc_wrapper(port)
|
||||
tc_wrapper.update_filters_bw_limit(
|
||||
rule.max_kbps, self._get_egress_burst_value(rule)
|
||||
)
|
||||
if rule.direction == const.INGRESS_DIRECTION:
|
||||
tc_wrapper.update_tbf_bw_limit(
|
||||
rule.max_kbps, rule.max_burst_kbps, self.tbf_latency)
|
||||
else:
|
||||
tc_wrapper.update_filters_bw_limit(
|
||||
rule.max_kbps, self._get_egress_burst_value(rule)
|
||||
)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_bandwidth_limit(self, port):
|
||||
tc_wrapper = self._get_tc_wrapper(port)
|
||||
tc_wrapper.delete_filters_bw_limit()
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_bandwidth_limit_ingress(self, port):
|
||||
tc_wrapper = self._get_tc_wrapper(port)
|
||||
tc_wrapper.delete_tbf_bw_limit()
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_dscp_marking(self, port, rule):
|
||||
with self.iptables_manager.defer_apply():
|
||||
|
@ -31,7 +31,7 @@ SUPPORTED_RULES = {
|
||||
qos_consts.MAX_BURST: {
|
||||
'type:range': [0, constants.DB_INTEGER_MAX_VALUE]},
|
||||
qos_consts.DIRECTION: {
|
||||
'type:values': [constants.EGRESS_DIRECTION]}
|
||||
'type:values': constants.VALID_DIRECTIONS}
|
||||
},
|
||||
qos_consts.RULE_TYPE_DSCP_MARKING: {
|
||||
qos_consts.DSCP_MARK: {'type:values': constants.VALID_DSCP_MARKS}
|
||||
|
@ -112,15 +112,10 @@ class _TestBwLimitQoS(BaseQoSRuleTestCase):
|
||||
number_of_hosts = 1
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_burst_value(limit, direction):
|
||||
# For egress bandwidth limit this value should be calculated as
|
||||
# bandwidth_limit * qos_consts.DEFAULT_BURST_RATE
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
return int(
|
||||
limit * qos_consts.DEFAULT_BURST_RATE
|
||||
)
|
||||
else:
|
||||
return 0
|
||||
def _get_expected_egress_burst_value(limit):
|
||||
return int(
|
||||
limit * qos_consts.DEFAULT_BURST_RATE
|
||||
)
|
||||
|
||||
def _wait_for_bw_rule_removed(self, vm, direction):
|
||||
# No values are provided when port doesn't have qos policy
|
||||
@ -176,26 +171,6 @@ class _TestBwLimitQoS(BaseQoSRuleTestCase):
|
||||
body={'port': {'qos_policy_id': None}})
|
||||
self._wait_for_bw_rule_removed(vm, self.direction)
|
||||
|
||||
|
||||
class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
direction_scenarios = [
|
||||
('ingress', {'direction': common_constants.INGRESS_DIRECTION}),
|
||||
('egress', {'direction': common_constants.EGRESS_DIRECTION})
|
||||
]
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
direction_scenarios, fullstack_utils.get_ovs_interface_scenarios())
|
||||
|
||||
def _wait_for_bw_rule_applied(self, vm, limit, burst, direction):
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: vm.bridge.get_egress_bw_limit_for_port(
|
||||
vm.port.name) == (limit, burst))
|
||||
elif direction == common_constants.INGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: vm.bridge.get_ingress_bw_limit_for_port(
|
||||
vm.port.name) == (limit, burst))
|
||||
|
||||
def test_bw_limit_direction_change(self):
|
||||
# Create port with qos policy attached, with rule self.direction
|
||||
vm, qos_policy = self._prepare_vm_with_qos_policy(
|
||||
@ -217,12 +192,65 @@ class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST, self.reverse_direction)
|
||||
|
||||
|
||||
class TestBwLimitQoSOvs(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_OVS
|
||||
direction_scenarios = [
|
||||
('ingress', {'direction': common_constants.INGRESS_DIRECTION}),
|
||||
('egress', {'direction': common_constants.EGRESS_DIRECTION})
|
||||
]
|
||||
scenarios = testscenarios.multiply_scenarios(
|
||||
direction_scenarios, fullstack_utils.get_ovs_interface_scenarios())
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_burst_value(limit, direction):
|
||||
# For egress bandwidth limit this value should be calculated as
|
||||
# bandwidth_limit * qos_consts.DEFAULT_BURST_RATE
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
return TestBwLimitQoSOvs._get_expected_egress_burst_value(limit)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def _wait_for_bw_rule_applied(self, vm, limit, burst, direction):
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: vm.bridge.get_egress_bw_limit_for_port(
|
||||
vm.port.name) == (limit, burst))
|
||||
elif direction == common_constants.INGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: vm.bridge.get_ingress_bw_limit_for_port(
|
||||
vm.port.name) == (limit, burst))
|
||||
|
||||
|
||||
class TestBwLimitQoSLinuxbridge(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
l2_agent_type = constants.AGENT_TYPE_LINUXBRIDGE
|
||||
scenarios = [
|
||||
('egress', {'direction': common_constants.EGRESS_DIRECTION})
|
||||
('egress', {'direction': common_constants.EGRESS_DIRECTION}),
|
||||
('ingress', {'direction': common_constants.INGRESS_DIRECTION}),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_burst_value(limit, direction):
|
||||
# For egress bandwidth limit this value should be calculated as
|
||||
# bandwidth_limit * qos_consts.DEFAULT_BURST_RATE
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
return TestBwLimitQoSLinuxbridge._get_expected_egress_burst_value(
|
||||
limit)
|
||||
else:
|
||||
return TestBwLimitQoSLinuxbridge._get_expected_ingress_burst_value(
|
||||
limit)
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_ingress_burst_value(limit):
|
||||
# calculate expected burst in same way as it's done in tc_lib but
|
||||
# burst value = 0 so it's always value calculated from kernel's hz
|
||||
# value
|
||||
# as in tc_lib.bits_to_kilobits result is rounded up that even
|
||||
# 1 bit gives 1 kbit same should be added here to expected burst
|
||||
# value
|
||||
return int(
|
||||
float(limit) /
|
||||
float(linuxbridge_agent_config.DEFAULT_KERNEL_HZ_VALUE) + 1)
|
||||
|
||||
def _wait_for_bw_rule_applied(self, vm, limit, burst, direction):
|
||||
port_name = linuxbridge_agent.LinuxBridgeManager.get_tap_device_name(
|
||||
vm.neutron_port['id'])
|
||||
@ -231,8 +259,12 @@ class TestBwLimitQoSLinuxbridge(_TestBwLimitQoS, base.BaseFullStackTestCase):
|
||||
linuxbridge_agent_config.DEFAULT_KERNEL_HZ_VALUE,
|
||||
namespace=vm.host.host_namespace
|
||||
)
|
||||
utils.wait_until_true(
|
||||
lambda: tc.get_filters_bw_limits() == (limit, burst))
|
||||
if direction == common_constants.EGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: tc.get_filters_bw_limits() == (limit, burst))
|
||||
elif direction == common_constants.INGRESS_DIRECTION:
|
||||
utils.wait_until_true(
|
||||
lambda: tc.get_tbf_bw_limits() == (limit, burst))
|
||||
|
||||
|
||||
class _TestDscpMarkingQoS(BaseQoSRuleTestCase):
|
||||
|
@ -18,6 +18,7 @@ from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.agent.linux import tc_lib
|
||||
from neutron.common import constants
|
||||
from neutron.objects.qos import rule
|
||||
from neutron.plugins.ml2.drivers.linuxbridge.agent.common import config # noqa
|
||||
from neutron.plugins.ml2.drivers.linuxbridge.agent.extension_drivers import (
|
||||
@ -36,15 +37,19 @@ 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_bw_limit = self._create_bw_limit_rule_obj()
|
||||
self.rule_egress_bw_limit = self._create_bw_limit_rule_obj(
|
||||
constants.EGRESS_DIRECTION)
|
||||
self.rule_ingress_bw_limit = self._create_bw_limit_rule_obj(
|
||||
constants.INGRESS_DIRECTION)
|
||||
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):
|
||||
def _create_bw_limit_rule_obj(self, direction):
|
||||
rule_obj = rule.QosBandwidthLimitRule()
|
||||
rule_obj.id = uuidutils.generate_uuid()
|
||||
rule_obj.max_kbps = 2
|
||||
rule_obj.max_burst_kbps = 200
|
||||
rule_obj.direction = direction
|
||||
rule_obj.obj_reset_changes()
|
||||
return rule_obj
|
||||
|
||||
@ -73,32 +78,77 @@ class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase):
|
||||
def _dscp_rule_tag(self, device):
|
||||
return "dscp-%s" % device
|
||||
|
||||
def test_create_bandwidth_limit(self):
|
||||
def test_create_egress_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "set_filters_bw_limit"
|
||||
) as set_bw_limit:
|
||||
) as set_filters_bw_limit, mock.patch.object(
|
||||
tc_lib.TcCommand, "set_tbf_bw_limit"
|
||||
) as set_tbf_limit:
|
||||
self.qos_driver.create_bandwidth_limit(self.port,
|
||||
self.rule_bw_limit)
|
||||
set_bw_limit.assert_called_once_with(
|
||||
self.rule_bw_limit.max_kbps, self.rule_bw_limit.max_burst_kbps,
|
||||
self.rule_egress_bw_limit)
|
||||
set_filters_bw_limit.assert_called_once_with(
|
||||
self.rule_egress_bw_limit.max_kbps,
|
||||
self.rule_egress_bw_limit.max_burst_kbps,
|
||||
)
|
||||
set_tbf_limit.assert_not_called()
|
||||
|
||||
def test_create_ingress_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "set_filters_bw_limit"
|
||||
) as set_filters_bw_limit, mock.patch.object(
|
||||
tc_lib.TcCommand, "set_tbf_bw_limit"
|
||||
) as set_tbf_limit:
|
||||
self.qos_driver.create_bandwidth_limit(self.port,
|
||||
self.rule_ingress_bw_limit)
|
||||
set_filters_bw_limit.assert_not_called()
|
||||
set_tbf_limit.assert_called_once_with(
|
||||
self.rule_ingress_bw_limit.max_kbps,
|
||||
self.rule_ingress_bw_limit.max_burst_kbps,
|
||||
TEST_LATENCY_VALUE
|
||||
)
|
||||
|
||||
def test_update_bandwidth_limit(self):
|
||||
def test_update_egress_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "update_filters_bw_limit"
|
||||
) as update_bw_limit:
|
||||
) as update_filters_bw_limit, mock.patch.object(
|
||||
tc_lib.TcCommand, "update_tbf_bw_limit"
|
||||
) as update_tbf_bw_limit:
|
||||
self.qos_driver.update_bandwidth_limit(self.port,
|
||||
self.rule_bw_limit)
|
||||
update_bw_limit.assert_called_once_with(
|
||||
self.rule_bw_limit.max_kbps, self.rule_bw_limit.max_burst_kbps,
|
||||
self.rule_egress_bw_limit)
|
||||
update_filters_bw_limit.assert_called_once_with(
|
||||
self.rule_egress_bw_limit.max_kbps,
|
||||
self.rule_egress_bw_limit.max_burst_kbps,
|
||||
)
|
||||
update_tbf_bw_limit.assert_not_called()
|
||||
|
||||
def test_update_ingress_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "update_filters_bw_limit"
|
||||
) as update_filters_bw_limit, mock.patch.object(
|
||||
tc_lib.TcCommand, "update_tbf_bw_limit"
|
||||
) as update_tbf_bw_limit:
|
||||
self.qos_driver.update_bandwidth_limit(self.port,
|
||||
self.rule_ingress_bw_limit)
|
||||
update_filters_bw_limit.assert_not_called()
|
||||
update_tbf_bw_limit.assert_called_once_with(
|
||||
self.rule_egress_bw_limit.max_kbps,
|
||||
self.rule_egress_bw_limit.max_burst_kbps,
|
||||
TEST_LATENCY_VALUE
|
||||
)
|
||||
|
||||
def test_delete_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "delete_filters_bw_limit"
|
||||
) as delete_bw_limit:
|
||||
) as delete_filters_bw_limit:
|
||||
self.qos_driver.delete_bandwidth_limit(self.port)
|
||||
delete_bw_limit.assert_called_once_with()
|
||||
delete_filters_bw_limit.assert_called_once_with()
|
||||
|
||||
def test_delete_ingress_bandwidth_limit(self):
|
||||
with mock.patch.object(
|
||||
tc_lib.TcCommand, "delete_tbf_bw_limit"
|
||||
) as delete_tbf_bw_limit:
|
||||
self.qos_driver.delete_bandwidth_limit_ingress(self.port)
|
||||
delete_tbf_bw_limit.assert_called_once_with()
|
||||
|
||||
def test_create_dscp_marking(self):
|
||||
expected_calls = [
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
prelude: >
|
||||
Linuxbridge L2 agent supports ingress bandwidth limit.
|
||||
features:
|
||||
- The linuxbridge L2 agent now supports bi-directional bandwidth limiting.
|
Loading…
Reference in New Issue
Block a user