diff options
-rw-r--r-- | interface-definitions/include/qos/queue-average-packet.xml.i | 16 | ||||
-rw-r--r-- | interface-definitions/include/qos/queue-mark-probability.xml.i | 16 | ||||
-rw-r--r-- | interface-definitions/include/qos/queue-maximum-threshold.xml.i | 16 | ||||
-rw-r--r-- | interface-definitions/include/qos/queue-minimum-threshold.xml.i | 15 | ||||
-rw-r--r-- | interface-definitions/qos.xml.in | 67 | ||||
-rw-r--r-- | python/vyos/qos/base.py | 29 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_qos.py | 64 |
7 files changed, 167 insertions, 56 deletions
diff --git a/interface-definitions/include/qos/queue-average-packet.xml.i b/interface-definitions/include/qos/queue-average-packet.xml.i new file mode 100644 index 000000000..2f8bfe266 --- /dev/null +++ b/interface-definitions/include/qos/queue-average-packet.xml.i @@ -0,0 +1,16 @@ +<!-- include start from qos/queue-average-packet.xml.i --> +<leafNode name="average-packet"> + <properties> + <help>Average packet size (bytes)</help> + <valueHelp> + <format>u32:16-10240</format> + <description>Average packet size in bytes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 16-10240"/> + </constraint> + <constraintErrorMessage>Average packet size must be between 16 and 10240</constraintErrorMessage> + </properties> + <defaultValue>1024</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/qos/queue-mark-probability.xml.i b/interface-definitions/include/qos/queue-mark-probability.xml.i new file mode 100644 index 000000000..1a2862845 --- /dev/null +++ b/interface-definitions/include/qos/queue-mark-probability.xml.i @@ -0,0 +1,16 @@ +<!-- include start from qos/queue-mark-probability.xml.i --> +<leafNode name="mark-probability"> + <properties> + <help>Mark probability for random detection</help> + <valueHelp> + <format>u32</format> + <description>Numeric value (1/N)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--positive"/> + </constraint> + <constraintErrorMessage>Mark probability must be greater than 0</constraintErrorMessage> + </properties> + <defaultValue>10</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/qos/queue-maximum-threshold.xml.i b/interface-definitions/include/qos/queue-maximum-threshold.xml.i new file mode 100644 index 000000000..66d17ccc4 --- /dev/null +++ b/interface-definitions/include/qos/queue-maximum-threshold.xml.i @@ -0,0 +1,16 @@ +<!-- include start from qos/queue-maximum-threshold.xml.i --> +<leafNode name="maximum-threshold"> + <properties> + <help>Maximum threshold for random detection</help> + <valueHelp> + <format>u32:0-4096</format> + <description>Maximum threshold in packets</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4096"/> + </constraint> + <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage> + </properties> + <defaultValue>18</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/qos/queue-minimum-threshold.xml.i b/interface-definitions/include/qos/queue-minimum-threshold.xml.i new file mode 100644 index 000000000..81e12d6e2 --- /dev/null +++ b/interface-definitions/include/qos/queue-minimum-threshold.xml.i @@ -0,0 +1,15 @@ +<!-- include start from qos/queue-minimum-threshold.xml.i --> +<leafNode name="minimum-threshold"> + <properties> + <help>Minimum threshold for random detection</help> + <valueHelp> + <format>u32:0-4096</format> + <description>Minimum threshold in packets</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4096"/> + </constraint> + <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in index 7618c3027..8f9ae3fa6 100644 --- a/interface-definitions/qos.xml.in +++ b/interface-definitions/qos.xml.in @@ -470,61 +470,10 @@ </properties> <children> #include <include/qos/queue-limit-1-4294967295.xml.i> - <leafNode name="average-packet"> - <properties> - <help>Average packet size (bytes)</help> - <valueHelp> - <format>u32:16-10240</format> - <description>Average packet size in bytes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-100"/> - </constraint> - <constraintErrorMessage>Average packet size must be between 16 and 10240</constraintErrorMessage> - </properties> - <defaultValue>1024</defaultValue> - </leafNode> - <leafNode name="mark-probability"> - <properties> - <help>Mark probability for this precedence</help> - <valueHelp> - <format><number></format> - <description>Numeric value (1/N)</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--positive"/> - </constraint> - <constraintErrorMessage>Mark probability must be greater than 0</constraintErrorMessage> - </properties> - <defaultValue>10</defaultValue> - </leafNode> - <leafNode name="maximum-threshold"> - <properties> - <help>Maximum threshold for random detection</help> - <valueHelp> - <format>u32:0-4096</format> - <description>Maximum Threshold in packets</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4096"/> - </constraint> - <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage> - </properties> - <defaultValue>18</defaultValue> - </leafNode> - <leafNode name="minimum-threshold"> - <properties> - <help>Minimum threshold for random detection</help> - <valueHelp> - <format>u32:0-4096</format> - <description>Maximum Threshold in packets</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4096"/> - </constraint> - <constraintErrorMessage>Threshold must be between 0 and 4096</constraintErrorMessage> - </properties> - </leafNode> + #include <include/qos/queue-average-packet.xml.i> + #include <include/qos/queue-maximum-threshold.xml.i> + #include <include/qos/queue-minimum-threshold.xml.i> + #include <include/qos/queue-mark-probability.xml.i> </children> </tagNode> </children> @@ -697,6 +646,10 @@ #include <include/qos/interval.xml.i> #include <include/qos/class-match.xml.i> #include <include/qos/class-priority.xml.i> + #include <include/qos/queue-average-packet.xml.i> + #include <include/qos/queue-maximum-threshold.xml.i> + #include <include/qos/queue-minimum-threshold.xml.i> + #include <include/qos/queue-mark-probability.xml.i> #include <include/qos/queue-limit-1-4294967295.xml.i> #include <include/qos/queue-type.xml.i> <leafNode name="queue-type"> @@ -759,6 +712,10 @@ </properties> <defaultValue>20</defaultValue> </leafNode> + #include <include/qos/queue-average-packet.xml.i> + #include <include/qos/queue-maximum-threshold.xml.i> + #include <include/qos/queue-minimum-threshold.xml.i> + #include <include/qos/queue-mark-probability.xml.i> #include <include/qos/queue-limit-1-4294967295.xml.i> #include <include/qos/queue-type.xml.i> <leafNode name="queue-type"> diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py index c8e881ee2..f9366c6b1 100644 --- a/python/vyos/qos/base.py +++ b/python/vyos/qos/base.py @@ -90,6 +90,23 @@ class QoSBase: else: return value + def _calc_random_detect_queue_params(self, avg_pkt, max_thr, limit=None, min_thr=None, mark_probability=None): + params = dict() + avg_pkt = int(avg_pkt) + max_thr = int(max_thr) + mark_probability = int(mark_probability) + limit = int(limit) if limit else 4 * max_thr + min_thr = int(min_thr) if min_thr else (9 * max_thr) // 18 + + params['avg_pkt'] = avg_pkt + params['limit'] = limit * avg_pkt + params['min_val'] = min_thr * avg_pkt + params['max_val'] = max_thr * avg_pkt + params['burst'] = (2 * min_thr + max_thr) // 3 + params['probability'] = 1 / mark_probability + + return params + def _build_base_qdisc(self, config : dict, cls_id : int): """ Add/replace qdisc for every class (also default is a class). This is @@ -144,6 +161,18 @@ class QoSBase: elif queue_type == 'random-detect': default_tc += f' red' + qparams = self._calc_random_detect_queue_params( + avg_pkt=dict_search('average_packet', config), + max_thr=dict_search('maximum_threshold', config), + limit=dict_search('queue_limit', config), + min_thr=dict_search('minimum_threshold', config), + mark_probability=dict_search('mark_probability', config) + ) + + default_tc += f' limit {qparams["limit"]} avpkt {qparams["avg_pkt"]}' + default_tc += f' max {qparams["max_val"]} min {qparams["min_val"]}' + default_tc += f' burst {qparams["burst"]} probability {qparams["probability"]}' + self._cmd(default_tc) elif queue_type == 'drop-tail': diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py index 1eb4b5cfb..4f41e36cd 100755 --- a/smoketest/scripts/cli/test_qos.py +++ b/smoketest/scripts/cli/test_qos.py @@ -581,7 +581,6 @@ class TestQoS(VyOSUnitTestSHIM.TestCase): dport = int(match_config['dport']) self.assertEqual(f'{dport:x}', filter['options']['match']['value']) - def test_11_shaper(self): bandwidth = 250 default_bandwidth = 20 @@ -635,6 +634,69 @@ class TestQoS(VyOSUnitTestSHIM.TestCase): class_bandwidth += 1 class_ceil += 1 + def test_12_shaper_with_red_queue(self): + bandwidth = 100 + default_bandwidth = 100 + default_burst = 100 + interface = self._interfaces[0] + class_bandwidth = 50 + dst_address = '192.0.2.8/32' + + shaper_name = f'qos-shaper-{interface}' + self.cli_set(base_path + ['interface', interface, 'egress', shaper_name]) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'bandwidth', f'{bandwidth}mbit']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'bandwidth', f'{default_bandwidth}%']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'burst', f'{default_burst}']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'queue-type', 'random-detect']) + + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'bandwidth', f'{class_bandwidth}mbit']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'match', '10', 'ip', 'destination', 'address', dst_address]) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'queue-type', 'random-detect']) + + # commit changes + self.cli_commit() + + # check root htb config + output = cmd(f'tc class show dev {interface}') + + config_entries = ( + f'prio 0 rate {class_bandwidth}Mbit ceil 50Mbit burst 15Kb', # specified class + f'prio 7 rate {default_bandwidth}Mbit ceil 100Mbit burst {default_burst}b', # default class + ) + for config_entry in config_entries: + self.assertIn(config_entry, output) + + output = cmd(f'tc -d qdisc show dev {interface}') + config_entries = ( + 'qdisc red', # use random detect + 'limit 72Kb min 9Kb max 18Kb ewma 3 probability 0.1', # default config for random detect + ) + for config_entry in config_entries: + self.assertIn(config_entry, output) + + # test random detect queue params + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'queue-limit', '1024']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'average-packet', '1024']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'maximum-threshold', '32']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'minimum-threshold', '16']) + + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'queue-limit', '1024']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'average-packet', '512']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'maximum-threshold', '32']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'minimum-threshold', '16']) + self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '2', 'mark-probability', '20']) + + self.cli_commit() + + output = cmd(f'tc -d qdisc show dev {interface}') + config_entries = ( + 'qdisc red', # use random detect + 'limit 1Mb min 16Kb max 32Kb ewma 3 probability 0.1', # default config for random detect + 'limit 512Kb min 8Kb max 16Kb ewma 3 probability 0.05', # class config for random detect + ) + for config_entry in config_entries: + self.assertIn(config_entry, output) + if __name__ == '__main__': unittest.main(verbosity=2) |