LinuxBridge agent's QoS driver bw limit for egress traffic
QoS service plugin provides for now bandwidth limit rules for egress traffic only (from VM point of view). QoS extension driver for Linuxbridge agent now configures limits in proper way on tap interface so limited is traffic which is outgoing from VM. Linuxbridge agent's QoS extension configures egress bandwidth limit and burst value in exactly same way how openvswitch is doing it with tc. Old methods in TcCommand class will stay untouched because they can be used later to implement also ingress bandwidth limits in QoS. Change-Id: Id2b0463f49df52744e5bc3979a4cfd0ff06f9248 Closes-bug: #1563720
This commit is contained in:
parent
e5b9ba1ea9
commit
6dcdb15dce
@ -21,5 +21,8 @@ ip_exec: IpNetnsExecFilter, ip, root
|
|||||||
|
|
||||||
# tc commands needed for QoS support
|
# tc commands needed for QoS support
|
||||||
tc_replace_tbf: RegExpFilter, tc, root, tc, qdisc, replace, dev, .+, root, tbf, rate, .+, latency, .+, burst, .+
|
tc_replace_tbf: RegExpFilter, tc, root, tc, qdisc, replace, dev, .+, root, tbf, rate, .+, latency, .+, burst, .+
|
||||||
tc_delete: RegExpFilter, tc, root, tc, qdisc, del, dev, .+, root
|
tc_add_ingress: RegExpFilter, tc, root, tc, qdisc, add, dev, .+, ingress, handle, .+
|
||||||
tc_show: RegExpFilter, tc, root, tc, qdisc, show, dev, .+
|
tc_delete: RegExpFilter, tc, root, tc, qdisc, del, dev, .+, .+
|
||||||
|
tc_show_qdisc: RegExpFilter, tc, root, tc, qdisc, show, dev, .+
|
||||||
|
tc_show_filters: RegExpFilter, tc, root, tc, filter, show, dev, .+, parent, .+
|
||||||
|
tc_add_filter: RegExpFilter, tc, root, tc, filter, add, dev, .+, parent, .+, protocol, all, prio, .+, basic, police, rate, .+, burst, .+, mtu, .+, drop
|
||||||
|
@ -20,6 +20,9 @@ from neutron.agent.linux import ip_lib
|
|||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
INGRESS_QDISC_ID = "ffff:"
|
||||||
|
MAX_MTU_VALUE = 65535
|
||||||
|
|
||||||
SI_BASE = 1000
|
SI_BASE = 1000
|
||||||
IEC_BASE = 1024
|
IEC_BASE = 1024
|
||||||
|
|
||||||
@ -35,6 +38,10 @@ UNITS = {
|
|||||||
"t": 4
|
"t": 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filters_pattern = re.compile(r"police \w+ rate (\w+) burst (\w+)")
|
||||||
|
tbf_pattern = re.compile(
|
||||||
|
r"qdisc (\w+) \w+: \w+ refcnt \d rate (\w+) burst (\w+) \w*")
|
||||||
|
|
||||||
|
|
||||||
class InvalidKernelHzValue(exceptions.NeutronException):
|
class InvalidKernelHzValue(exceptions.NeutronException):
|
||||||
message = _("Kernel HZ value %(value)s is not valid. This value must be "
|
message = _("Kernel HZ value %(value)s is not valid. This value must be "
|
||||||
@ -94,39 +101,29 @@ class TcCommand(ip_lib.IPDevice):
|
|||||||
ip_wrapper = ip_lib.IPWrapper(self.namespace)
|
ip_wrapper = ip_lib.IPWrapper(self.namespace)
|
||||||
return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs)
|
return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs)
|
||||||
|
|
||||||
def get_bw_limits(self):
|
def get_filters_bw_limits(self, qdisc_id=INGRESS_QDISC_ID):
|
||||||
return self._get_tbf_limits()
|
cmd = ['filter', 'show', 'dev', self.name, 'parent', qdisc_id]
|
||||||
|
cmd_result = self._execute_tc_cmd(cmd)
|
||||||
|
if not cmd_result:
|
||||||
|
return None, None
|
||||||
|
for line in cmd_result.split("\n"):
|
||||||
|
m = filters_pattern.match(line.strip())
|
||||||
|
if m:
|
||||||
|
#NOTE(slaweq): because tc is giving bw limit in SI units
|
||||||
|
# we need to calculate it as 1000bit = 1kbit:
|
||||||
|
bw_limit = convert_to_kilobits(m.group(1), SI_BASE)
|
||||||
|
#NOTE(slaweq): because tc is giving burst limit in IEC units
|
||||||
|
# we need to calculate it as 1024bit = 1kbit:
|
||||||
|
burst_limit = convert_to_kilobits(m.group(2), IEC_BASE)
|
||||||
|
return bw_limit, burst_limit
|
||||||
|
return None, None
|
||||||
|
|
||||||
def set_bw_limit(self, bw_limit, burst_limit, latency_value):
|
def get_tbf_bw_limits(self):
|
||||||
return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value)
|
|
||||||
|
|
||||||
def update_bw_limit(self, bw_limit, burst_limit, latency_value):
|
|
||||||
return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value)
|
|
||||||
|
|
||||||
def delete_bw_limit(self):
|
|
||||||
cmd = ['qdisc', 'del', 'dev', self.name, 'root']
|
|
||||||
# Return_code=2 is fine because it means
|
|
||||||
# "RTNETLINK answers: No such file or directory" what is fine when we
|
|
||||||
# are trying to delete qdisc
|
|
||||||
return self._execute_tc_cmd(cmd, extra_ok_codes=[2])
|
|
||||||
|
|
||||||
def get_burst_value(self, bw_limit, burst_limit):
|
|
||||||
min_burst_value = self._get_min_burst_value(bw_limit)
|
|
||||||
return max(min_burst_value, burst_limit)
|
|
||||||
|
|
||||||
def _get_min_burst_value(self, bw_limit):
|
|
||||||
# bw_limit [kbit] / HZ [1/s] = burst [kbit]
|
|
||||||
return float(bw_limit) / float(self.kernel_hz)
|
|
||||||
|
|
||||||
def _get_tbf_limits(self):
|
|
||||||
cmd = ['qdisc', 'show', 'dev', self.name]
|
cmd = ['qdisc', 'show', 'dev', self.name]
|
||||||
cmd_result = self._execute_tc_cmd(cmd)
|
cmd_result = self._execute_tc_cmd(cmd)
|
||||||
if not cmd_result:
|
if not cmd_result:
|
||||||
return None, None
|
return None, None
|
||||||
pattern = re.compile(
|
m = tbf_pattern.match(cmd_result)
|
||||||
r"qdisc (\w+) \w+: \w+ refcnt \d rate (\w+) burst (\w+) \w*"
|
|
||||||
)
|
|
||||||
m = pattern.match(cmd_result)
|
|
||||||
if not m:
|
if not m:
|
||||||
return None, None
|
return None, None
|
||||||
qdisc_name = m.group(1)
|
qdisc_name = m.group(1)
|
||||||
@ -140,9 +137,73 @@ class TcCommand(ip_lib.IPDevice):
|
|||||||
burst_limit = convert_to_kilobits(m.group(3), IEC_BASE)
|
burst_limit = convert_to_kilobits(m.group(3), IEC_BASE)
|
||||||
return bw_limit, burst_limit
|
return bw_limit, burst_limit
|
||||||
|
|
||||||
|
def set_filters_bw_limit(self, bw_limit, burst_limit):
|
||||||
|
"""Set ingress qdisc and filter for police ingress traffic on device
|
||||||
|
|
||||||
|
This will allow to police traffic incoming to interface. It
|
||||||
|
means that it is fine to limit egress traffic from instance point of
|
||||||
|
view.
|
||||||
|
"""
|
||||||
|
#because replace of tc filters is not working properly and it's adding
|
||||||
|
# new filters each time instead of replacing existing one first old
|
||||||
|
# ingress qdisc should be deleted and then added new one so update will
|
||||||
|
# be called to do that:
|
||||||
|
return self.update_filters_bw_limit(bw_limit, burst_limit)
|
||||||
|
|
||||||
|
def set_tbf_bw_limit(self, bw_limit, burst_limit, latency_value):
|
||||||
|
"""Set token bucket filter qdisc on device
|
||||||
|
|
||||||
|
This will allow to limit speed of packets going out from interface. It
|
||||||
|
means that it is fine to limit ingress traffic from instance point of
|
||||||
|
view.
|
||||||
|
"""
|
||||||
|
return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value)
|
||||||
|
|
||||||
|
def update_filters_bw_limit(self, bw_limit, burst_limit,
|
||||||
|
qdisc_id=INGRESS_QDISC_ID):
|
||||||
|
self.delete_filters_bw_limit()
|
||||||
|
return self._set_filters_bw_limit(bw_limit, burst_limit, qdisc_id)
|
||||||
|
|
||||||
|
def update_tbf_bw_limit(self, bw_limit, burst_limit, latency_value):
|
||||||
|
return self._replace_tbf_qdisc(bw_limit, burst_limit, latency_value)
|
||||||
|
|
||||||
|
def delete_filters_bw_limit(self):
|
||||||
|
#NOTE(slaweq): For limit traffic egress from instance we need to use
|
||||||
|
# qdisc "ingress" because it is ingress traffic from interface POV:
|
||||||
|
self._delete_qdisc("ingress")
|
||||||
|
|
||||||
|
def delete_tbf_bw_limit(self):
|
||||||
|
self._delete_qdisc("root")
|
||||||
|
|
||||||
|
def _set_filters_bw_limit(self, bw_limit, burst_limit,
|
||||||
|
qdisc_id=INGRESS_QDISC_ID):
|
||||||
|
cmd = ['qdisc', 'add', 'dev', self.name, 'ingress',
|
||||||
|
'handle', qdisc_id]
|
||||||
|
self._execute_tc_cmd(cmd)
|
||||||
|
return self._add_policy_filter(bw_limit, burst_limit)
|
||||||
|
|
||||||
|
def _delete_qdisc(self, qdisc_name):
|
||||||
|
cmd = ['qdisc', 'del', 'dev', self.name, qdisc_name]
|
||||||
|
# Return_code=2 is fine because it means
|
||||||
|
# "RTNETLINK answers: No such file or directory" what is fine when we
|
||||||
|
# are trying to delete qdisc
|
||||||
|
return self._execute_tc_cmd(cmd, extra_ok_codes=[2])
|
||||||
|
|
||||||
|
def _get_filters_burst_value(self, bw_limit, burst_limit):
|
||||||
|
if not burst_limit:
|
||||||
|
# NOTE(slaweq): If burst value was not specified by user than it
|
||||||
|
# will be set as 80% of bw_limit to ensure that limit for TCP
|
||||||
|
# traffic will work well:
|
||||||
|
return float(bw_limit) * 0.8
|
||||||
|
return burst_limit
|
||||||
|
|
||||||
|
def _get_tbf_burst_value(self, bw_limit, burst_limit):
|
||||||
|
min_burst_value = float(bw_limit) / float(self.kernel_hz)
|
||||||
|
return max(min_burst_value, burst_limit)
|
||||||
|
|
||||||
def _replace_tbf_qdisc(self, bw_limit, burst_limit, latency_value):
|
def _replace_tbf_qdisc(self, bw_limit, burst_limit, latency_value):
|
||||||
burst = "%s%s" % (
|
burst = "%s%s" % (
|
||||||
self.get_burst_value(bw_limit, burst_limit), BURST_UNIT)
|
self._get_tbf_burst_value(bw_limit, burst_limit), BURST_UNIT)
|
||||||
latency = "%s%s" % (latency_value, LATENCY_UNIT)
|
latency = "%s%s" % (latency_value, LATENCY_UNIT)
|
||||||
rate_limit = "%s%s" % (bw_limit, BW_LIMIT_UNIT)
|
rate_limit = "%s%s" % (bw_limit, BW_LIMIT_UNIT)
|
||||||
cmd = [
|
cmd = [
|
||||||
@ -153,3 +214,21 @@ class TcCommand(ip_lib.IPDevice):
|
|||||||
'burst', burst
|
'burst', burst
|
||||||
]
|
]
|
||||||
return self._execute_tc_cmd(cmd)
|
return self._execute_tc_cmd(cmd)
|
||||||
|
|
||||||
|
def _add_policy_filter(self, bw_limit, burst_limit,
|
||||||
|
qdisc_id=INGRESS_QDISC_ID):
|
||||||
|
rate_limit = "%s%s" % (bw_limit, BW_LIMIT_UNIT)
|
||||||
|
burst = "%s%s" % (
|
||||||
|
self._get_filters_burst_value(bw_limit, burst_limit), BURST_UNIT)
|
||||||
|
#NOTE(slaweq): it is made in exactly same way how openvswitch is doing
|
||||||
|
# it when configuing ingress traffic limit on port. It can be found in
|
||||||
|
# lib/netdev-linux.c#L4698 in openvswitch sources:
|
||||||
|
cmd = [
|
||||||
|
'filter', 'add', 'dev', self.name,
|
||||||
|
'parent', qdisc_id, 'protocol', 'all',
|
||||||
|
'prio', '49', 'basic', 'police',
|
||||||
|
'rate', rate_limit,
|
||||||
|
'burst', burst,
|
||||||
|
'mtu', MAX_MTU_VALUE,
|
||||||
|
'drop']
|
||||||
|
return self._execute_tc_cmd(cmd)
|
||||||
|
@ -37,21 +37,21 @@ class QosLinuxbridgeAgentDriver(qos.QosAgentDriver):
|
|||||||
@log_helpers.log_method_call
|
@log_helpers.log_method_call
|
||||||
def create_bandwidth_limit(self, port, rule):
|
def create_bandwidth_limit(self, port, rule):
|
||||||
tc_wrapper = self._get_tc_wrapper(port)
|
tc_wrapper = self._get_tc_wrapper(port)
|
||||||
tc_wrapper.set_bw_limit(
|
tc_wrapper.set_filters_bw_limit(
|
||||||
rule.max_kbps, rule.max_burst_kbps, cfg.CONF.QOS.tbf_latency
|
rule.max_kbps, rule.max_burst_kbps
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_helpers.log_method_call
|
@log_helpers.log_method_call
|
||||||
def update_bandwidth_limit(self, port, rule):
|
def update_bandwidth_limit(self, port, rule):
|
||||||
tc_wrapper = self._get_tc_wrapper(port)
|
tc_wrapper = self._get_tc_wrapper(port)
|
||||||
tc_wrapper.update_bw_limit(
|
tc_wrapper.update_filters_bw_limit(
|
||||||
rule.max_kbps, rule.max_burst_kbps, cfg.CONF.QOS.tbf_latency
|
rule.max_kbps, rule.max_burst_kbps
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_helpers.log_method_call
|
@log_helpers.log_method_call
|
||||||
def delete_bandwidth_limit(self, port):
|
def delete_bandwidth_limit(self, port):
|
||||||
tc_wrapper = self._get_tc_wrapper(port)
|
tc_wrapper = self._get_tc_wrapper(port)
|
||||||
tc_wrapper.delete_bw_limit()
|
tc_wrapper.delete_filters_bw_limit()
|
||||||
|
|
||||||
def _get_tc_wrapper(self, port):
|
def _get_tc_wrapper(self, port):
|
||||||
return tc_lib.TcCommand(
|
return tc_lib.TcCommand(
|
||||||
|
@ -55,7 +55,7 @@ def _wait_for_rule_applied_linuxbridge_agent(vm, limit, burst):
|
|||||||
namespace=vm.host.host_namespace
|
namespace=vm.host.host_namespace
|
||||||
)
|
)
|
||||||
utils.wait_until_true(
|
utils.wait_until_true(
|
||||||
lambda: tc.get_bw_limits() == (limit, burst))
|
lambda: tc.get_filters_bw_limits() == (limit, burst))
|
||||||
|
|
||||||
|
|
||||||
def _wait_for_rule_applied(vm, limit, burst):
|
def _wait_for_rule_applied(vm, limit, burst):
|
||||||
|
@ -26,44 +26,64 @@ LATENCY = 50
|
|||||||
BW_LIMIT = 1024
|
BW_LIMIT = 1024
|
||||||
BURST = 512
|
BURST = 512
|
||||||
|
|
||||||
DEV_NAME = "test_tap"
|
BASE_DEV_NAME = "test_tap"
|
||||||
MAC_ADDRESS = "fa:16:3e:01:01:01"
|
|
||||||
|
|
||||||
|
|
||||||
class TcLibTestCase(functional_base.BaseSudoTestCase):
|
class TcLibTestCase(functional_base.BaseSudoTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def create_device(self, name):
|
||||||
super(TcLibTestCase, self).setUp()
|
"""Create a tuntap with the specified name.
|
||||||
self.create_device()
|
|
||||||
self.tc = tc_lib.TcCommand(DEV_NAME, TEST_HZ_VALUE)
|
|
||||||
|
|
||||||
def create_device(self):
|
|
||||||
"""Create a tuntap with the specified attributes.
|
|
||||||
|
|
||||||
The device is cleaned up at the end of the test.
|
The device is cleaned up at the end of the test.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ip = ip_lib.IPWrapper()
|
ip = ip_lib.IPWrapper()
|
||||||
tap_device = ip.add_tuntap(DEV_NAME)
|
tap_device = ip.add_tuntap(name)
|
||||||
self.addCleanup(tap_device.link.delete)
|
self.addCleanup(tap_device.link.delete)
|
||||||
tap_device.link.set_address(MAC_ADDRESS)
|
|
||||||
tap_device.link.set_up()
|
tap_device.link.set_up()
|
||||||
|
|
||||||
def test_bandwidth_limit(self):
|
def test_filters_bandwidth_limit(self):
|
||||||
self.tc.set_bw_limit(BW_LIMIT, BURST, LATENCY)
|
device_name = "%s_filters" % BASE_DEV_NAME
|
||||||
bw_limit, burst = self.tc.get_bw_limits()
|
self.create_device(device_name)
|
||||||
|
tc = tc_lib.TcCommand(device_name, TEST_HZ_VALUE)
|
||||||
|
|
||||||
|
tc.set_filters_bw_limit(BW_LIMIT, BURST)
|
||||||
|
bw_limit, burst = tc.get_filters_bw_limits()
|
||||||
self.assertEqual(BW_LIMIT, bw_limit)
|
self.assertEqual(BW_LIMIT, bw_limit)
|
||||||
self.assertEqual(BURST, burst)
|
self.assertEqual(BURST, burst)
|
||||||
|
|
||||||
new_bw_limit = BW_LIMIT + 500
|
new_bw_limit = BW_LIMIT + 500
|
||||||
new_burst = BURST + 50
|
new_burst = BURST + 50
|
||||||
|
|
||||||
self.tc.update_bw_limit(new_bw_limit, new_burst, LATENCY)
|
tc.update_filters_bw_limit(new_bw_limit, new_burst)
|
||||||
bw_limit, burst = self.tc.get_bw_limits()
|
bw_limit, burst = tc.get_filters_bw_limits()
|
||||||
self.assertEqual(new_bw_limit, bw_limit)
|
self.assertEqual(new_bw_limit, bw_limit)
|
||||||
self.assertEqual(new_burst, burst)
|
self.assertEqual(new_burst, burst)
|
||||||
|
|
||||||
self.tc.delete_bw_limit()
|
tc.delete_filters_bw_limit()
|
||||||
bw_limit, burst = self.tc.get_bw_limits()
|
bw_limit, burst = tc.get_filters_bw_limits()
|
||||||
|
self.assertIsNone(bw_limit)
|
||||||
|
self.assertIsNone(burst)
|
||||||
|
|
||||||
|
def test_tbf_bandwidth_limit(self):
|
||||||
|
device_name = "%s_tbf" % BASE_DEV_NAME
|
||||||
|
self.create_device(device_name)
|
||||||
|
tc = tc_lib.TcCommand(device_name, TEST_HZ_VALUE)
|
||||||
|
|
||||||
|
tc.set_tbf_bw_limit(BW_LIMIT, BURST, LATENCY)
|
||||||
|
bw_limit, burst = tc.get_tbf_bw_limits()
|
||||||
|
self.assertEqual(BW_LIMIT, bw_limit)
|
||||||
|
self.assertEqual(BURST, burst)
|
||||||
|
|
||||||
|
new_bw_limit = BW_LIMIT + 500
|
||||||
|
new_burst = BURST + 50
|
||||||
|
|
||||||
|
tc.update_tbf_bw_limit(new_bw_limit, new_burst, LATENCY)
|
||||||
|
bw_limit, burst = tc.get_tbf_bw_limits()
|
||||||
|
self.assertEqual(new_bw_limit, bw_limit)
|
||||||
|
self.assertEqual(new_burst, burst)
|
||||||
|
|
||||||
|
tc.delete_tbf_bw_limit()
|
||||||
|
bw_limit, burst = tc.get_tbf_bw_limits()
|
||||||
self.assertIsNone(bw_limit)
|
self.assertIsNone(bw_limit)
|
||||||
self.assertIsNone(burst)
|
self.assertIsNone(burst)
|
||||||
|
@ -24,10 +24,18 @@ BW_LIMIT = 2000 # [kbps]
|
|||||||
BURST = 100 # [kbit]
|
BURST = 100 # [kbit]
|
||||||
LATENCY = 50 # [ms]
|
LATENCY = 50 # [ms]
|
||||||
|
|
||||||
TC_OUTPUT = (
|
TC_QDISC_OUTPUT = (
|
||||||
'qdisc tbf 8011: root refcnt 2 rate %(bw)skbit burst %(burst)skbit '
|
'qdisc tbf 8011: root refcnt 2 rate %(bw)skbit burst %(burst)skbit '
|
||||||
'lat 50.0ms \n') % {'bw': BW_LIMIT, 'burst': BURST}
|
'lat 50.0ms \n') % {'bw': BW_LIMIT, 'burst': BURST}
|
||||||
|
|
||||||
|
TC_FILTERS_OUTPUT = (
|
||||||
|
'filter protocol all pref 49152 u32 \nfilter protocol all pref '
|
||||||
|
'49152 u32 fh 800: ht divisor 1 \nfilter protocol all pref 49152 u32 fh '
|
||||||
|
'800::800 order 2048 key ht 800 \n match 00000000/00000000 at 0\n '
|
||||||
|
'police 0x1e rate %(bw)skbit burst %(burst)skbit mtu 2Kb action \n'
|
||||||
|
'drop overhead 0b \n ref 1 bind 1'
|
||||||
|
) % {'bw': BW_LIMIT, 'burst': BURST}
|
||||||
|
|
||||||
|
|
||||||
class BaseUnitConversionTest(object):
|
class BaseUnitConversionTest(object):
|
||||||
|
|
||||||
@ -148,26 +156,48 @@ class TestTcCommand(base.BaseTestCase):
|
|||||||
tc_lib.TcCommand, DEVICE_NAME, -100
|
tc_lib.TcCommand, DEVICE_NAME, -100
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_get_bw_limits(self):
|
def test_get_filters_bw_limits(self):
|
||||||
self.execute.return_value = TC_OUTPUT
|
self.execute.return_value = TC_FILTERS_OUTPUT
|
||||||
bw_limit, burst_limit = self.tc.get_bw_limits()
|
bw_limit, burst_limit = self.tc.get_filters_bw_limits()
|
||||||
self.assertEqual(BW_LIMIT, bw_limit)
|
self.assertEqual(BW_LIMIT, bw_limit)
|
||||||
self.assertEqual(BURST, burst_limit)
|
self.assertEqual(BURST, burst_limit)
|
||||||
|
|
||||||
def test_get_bw_limits_when_wrong_qdisc(self):
|
def test_get_filters_bw_limits_when_output_not_match(self):
|
||||||
output = TC_OUTPUT.replace("tbf", "different_qdisc")
|
output = (
|
||||||
|
"Some different "
|
||||||
|
"output from command:"
|
||||||
|
"tc filters show dev XXX parent ffff:"
|
||||||
|
)
|
||||||
self.execute.return_value = output
|
self.execute.return_value = output
|
||||||
bw_limit, burst_limit = self.tc.get_bw_limits()
|
bw_limit, burst_limit = self.tc.get_filters_bw_limits()
|
||||||
self.assertIsNone(bw_limit)
|
self.assertIsNone(bw_limit)
|
||||||
self.assertIsNone(burst_limit)
|
self.assertIsNone(burst_limit)
|
||||||
|
|
||||||
def test_get_bw_limits_when_wrong_units(self):
|
def test_get_filters_bw_limits_when_wrong_units(self):
|
||||||
output = TC_OUTPUT.replace("kbit", "Xbit")
|
output = TC_FILTERS_OUTPUT.replace("kbit", "Xbit")
|
||||||
self.execute.return_value = output
|
self.execute.return_value = output
|
||||||
self.assertRaises(tc_lib.InvalidUnit, self.tc.get_bw_limits)
|
self.assertRaises(tc_lib.InvalidUnit, self.tc.get_filters_bw_limits)
|
||||||
|
|
||||||
def test_set_bw_limit(self):
|
def test_get_tbf_bw_limits(self):
|
||||||
self.tc.set_bw_limit(BW_LIMIT, BURST, LATENCY)
|
self.execute.return_value = TC_QDISC_OUTPUT
|
||||||
|
bw_limit, burst_limit = self.tc.get_tbf_bw_limits()
|
||||||
|
self.assertEqual(BW_LIMIT, bw_limit)
|
||||||
|
self.assertEqual(BURST, burst_limit)
|
||||||
|
|
||||||
|
def test_get_tbf_bw_limits_when_wrong_qdisc(self):
|
||||||
|
output = TC_QDISC_OUTPUT.replace("tbf", "different_qdisc")
|
||||||
|
self.execute.return_value = output
|
||||||
|
bw_limit, burst_limit = self.tc.get_tbf_bw_limits()
|
||||||
|
self.assertIsNone(bw_limit)
|
||||||
|
self.assertIsNone(burst_limit)
|
||||||
|
|
||||||
|
def test_get_tbf_bw_limits_when_wrong_units(self):
|
||||||
|
output = TC_QDISC_OUTPUT.replace("kbit", "Xbit")
|
||||||
|
self.execute.return_value = output
|
||||||
|
self.assertRaises(tc_lib.InvalidUnit, self.tc.get_tbf_bw_limits)
|
||||||
|
|
||||||
|
def test_set_tbf_bw_limit(self):
|
||||||
|
self.tc.set_tbf_bw_limit(BW_LIMIT, BURST, LATENCY)
|
||||||
self.execute.assert_called_once_with(
|
self.execute.assert_called_once_with(
|
||||||
["tc", "qdisc", "replace", "dev", DEVICE_NAME,
|
["tc", "qdisc", "replace", "dev", DEVICE_NAME,
|
||||||
"root", "tbf", "rate", self.bw_limit,
|
"root", "tbf", "rate", self.bw_limit,
|
||||||
@ -179,8 +209,41 @@ class TestTcCommand(base.BaseTestCase):
|
|||||||
extra_ok_codes=None
|
extra_ok_codes=None
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_update_bw_limit(self):
|
def test_update_filters_bw_limit(self):
|
||||||
self.tc.update_bw_limit(BW_LIMIT, BURST, LATENCY)
|
self.tc.update_filters_bw_limit(BW_LIMIT, BURST)
|
||||||
|
self.execute.assert_has_calls([
|
||||||
|
mock.call(
|
||||||
|
["tc", "qdisc", "del", "dev", DEVICE_NAME, "ingress"],
|
||||||
|
run_as_root=True,
|
||||||
|
check_exit_code=True,
|
||||||
|
log_fail_as_error=True,
|
||||||
|
extra_ok_codes=[2]
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
['tc', 'qdisc', 'add', 'dev', DEVICE_NAME, "ingress",
|
||||||
|
"handle", tc_lib.INGRESS_QDISC_ID],
|
||||||
|
run_as_root=True,
|
||||||
|
check_exit_code=True,
|
||||||
|
log_fail_as_error=True,
|
||||||
|
extra_ok_codes=None
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
['tc', 'filter', 'add', 'dev', DEVICE_NAME,
|
||||||
|
'parent', tc_lib.INGRESS_QDISC_ID, 'protocol', 'all',
|
||||||
|
'prio', '49', 'basic', 'police',
|
||||||
|
'rate', self.bw_limit,
|
||||||
|
'burst', self.burst,
|
||||||
|
'mtu', tc_lib.MAX_MTU_VALUE,
|
||||||
|
'drop'],
|
||||||
|
run_as_root=True,
|
||||||
|
check_exit_code=True,
|
||||||
|
log_fail_as_error=True,
|
||||||
|
extra_ok_codes=None
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_update_tbf_bw_limit(self):
|
||||||
|
self.tc.update_tbf_bw_limit(BW_LIMIT, BURST, LATENCY)
|
||||||
self.execute.assert_called_once_with(
|
self.execute.assert_called_once_with(
|
||||||
["tc", "qdisc", "replace", "dev", DEVICE_NAME,
|
["tc", "qdisc", "replace", "dev", DEVICE_NAME,
|
||||||
"root", "tbf", "rate", self.bw_limit,
|
"root", "tbf", "rate", self.bw_limit,
|
||||||
@ -192,8 +255,18 @@ class TestTcCommand(base.BaseTestCase):
|
|||||||
extra_ok_codes=None
|
extra_ok_codes=None
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delete_bw_limit(self):
|
def test_delete_filters_bw_limit(self):
|
||||||
self.tc.delete_bw_limit()
|
self.tc.delete_filters_bw_limit()
|
||||||
|
self.execute.assert_called_once_with(
|
||||||
|
["tc", "qdisc", "del", "dev", DEVICE_NAME, "ingress"],
|
||||||
|
run_as_root=True,
|
||||||
|
check_exit_code=True,
|
||||||
|
log_fail_as_error=True,
|
||||||
|
extra_ok_codes=[2]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delete_tbf_bw_limit(self):
|
||||||
|
self.tc.delete_tbf_bw_limit()
|
||||||
self.execute.assert_called_once_with(
|
self.execute.assert_called_once_with(
|
||||||
["tc", "qdisc", "del", "dev", DEVICE_NAME, "root"],
|
["tc", "qdisc", "del", "dev", DEVICE_NAME, "root"],
|
||||||
run_as_root=True,
|
run_as_root=True,
|
||||||
@ -202,16 +275,29 @@ class TestTcCommand(base.BaseTestCase):
|
|||||||
extra_ok_codes=[2]
|
extra_ok_codes=[2]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_burst_value_when_burst_bigger_then_minimal(self):
|
def test__get_filters_burst_value_burst_not_none(self):
|
||||||
result = self.tc.get_burst_value(BW_LIMIT, BURST)
|
self.assertEqual(
|
||||||
|
BURST, self.tc._get_filters_burst_value(BW_LIMIT, BURST)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test__get_filters_burst_no_burst_value_given(self):
|
||||||
|
expected_burst = BW_LIMIT * 0.8
|
||||||
|
self.assertEqual(
|
||||||
|
expected_burst,
|
||||||
|
self.tc._get_filters_burst_value(BW_LIMIT, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test__get_filters_burst_burst_value_zero(self):
|
||||||
|
expected_burst = BW_LIMIT * 0.8
|
||||||
|
self.assertEqual(
|
||||||
|
expected_burst,
|
||||||
|
self.tc._get_filters_burst_value(BW_LIMIT, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test__get_tbf_burst_value_when_burst_bigger_then_minimal(self):
|
||||||
|
result = self.tc._get_tbf_burst_value(BW_LIMIT, BURST)
|
||||||
self.assertEqual(BURST, result)
|
self.assertEqual(BURST, result)
|
||||||
|
|
||||||
def test_burst_value_when_burst_smaller_then_minimal(self):
|
def test__get_tbf_burst_value_when_burst_smaller_then_minimal(self):
|
||||||
result = self.tc.get_burst_value(BW_LIMIT, 0)
|
result = self.tc._get_tbf_burst_value(BW_LIMIT, 0)
|
||||||
self.assertEqual(2, result)
|
|
||||||
|
|
||||||
def test__get_min_burst_value_in_bits(self):
|
|
||||||
result = self.tc._get_min_burst_value(BW_LIMIT)
|
|
||||||
#if input is 2000kbit and kernel_hz is configured to 1000 then
|
|
||||||
# min_burst should be 2 kbit
|
|
||||||
self.assertEqual(2, result)
|
self.assertEqual(2, result)
|
||||||
|
@ -53,27 +53,25 @@ class QosLinuxbridgeAgentDriverTestCase(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_create_rule(self):
|
def test_create_rule(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
tc_lib.TcCommand, "set_bw_limit"
|
tc_lib.TcCommand, "set_filters_bw_limit"
|
||||||
) as set_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)
|
||||||
set_bw_limit.assert_called_once_with(
|
set_bw_limit.assert_called_once_with(
|
||||||
self.rule.max_kbps, self.rule.max_burst_kbps,
|
self.rule.max_kbps, self.rule.max_burst_kbps,
|
||||||
TEST_LATENCY_VALUE
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_update_rule(self):
|
def test_update_rule(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
tc_lib.TcCommand, "update_bw_limit"
|
tc_lib.TcCommand, "update_filters_bw_limit"
|
||||||
) as update_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)
|
||||||
update_bw_limit.assert_called_once_with(
|
update_bw_limit.assert_called_once_with(
|
||||||
self.rule.max_kbps, self.rule.max_burst_kbps,
|
self.rule.max_kbps, self.rule.max_burst_kbps,
|
||||||
TEST_LATENCY_VALUE
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delete_rule(self):
|
def test_delete_rule(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
tc_lib.TcCommand, "delete_bw_limit"
|
tc_lib.TcCommand, "delete_filters_bw_limit"
|
||||||
) as delete_bw_limit:
|
) as delete_bw_limit:
|
||||||
self.qos_driver.delete_bandwidth_limit(self.port)
|
self.qos_driver.delete_bandwidth_limit(self.port)
|
||||||
delete_bw_limit.assert_called_once_with()
|
delete_bw_limit.assert_called_once_with()
|
||||||
|
Loading…
Reference in New Issue
Block a user