Skip to content

Commit 33f1763

Browse files
authored
Merge pull request #4912 from hedrok/T8046-add-link-params-cli
T8046: traffic-engineering: support link-params
2 parents f80aae1 + a4b3cd3 commit 33f1763

File tree

7 files changed

+418
-3
lines changed

7 files changed

+418
-3
lines changed

data/templates/frr/isisd.frr.j2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ advertise-passive-only
153153
{% endif %}
154154
mpls-te inter-as{{ level }}
155155
{% endif %}
156+
{% if traffic_engineering.export is vyos_defined %}
157+
mpls-te export
158+
{% endif %}
156159
{% if segment_routing is vyos_defined %}
157160
{% if segment_routing.maximum_label_depth is vyos_defined %}
158161
segment-routing node-msd {{ segment_routing.maximum_label_depth }}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{% if interface is vyos_defined %}
2+
!
3+
{% set ns = namespace(mask_by_admin_group=dict()) %}
4+
{% if admin_group is vyos_defined %}
5+
{% for ag in admin_group %}
6+
{% set _ = ns.mask_by_admin_group.update({ag: 1.__lshift__(admin_group[ag]['bit_position'] | int)}) %}
7+
{% endfor %}
8+
{% endif %}
9+
{% set mbit_per_second_2_byte_per_second = 1024 * 1024 / 8 %}
10+
{% for ifname, data in interface.items() %}
11+
interface {{ ifname }}
12+
link-params
13+
{% if data.metric is vyos_defined %}
14+
metric {{ data.metric }}
15+
{% endif %}
16+
{% if data.admin_group is vyos_defined %}
17+
{% set val = namespace(x=0) %}
18+
{% for ag in data.admin_group %}
19+
{% set val.x = (val.x | int).__or__(ns.mask_by_admin_group[ag]) %}
20+
{% endfor %}
21+
admin-grp {{ '0x{:x}'.format(val.x) }}
22+
{% endif %}
23+
{% if data.max_bandwidth is vyos_defined %}
24+
max-bw {{ (data.max_bandwidth | int) * mbit_per_second_2_byte_per_second }}
25+
{% endif %}
26+
{% if data.max_reservable_bandwidth is vyos_defined %}
27+
{% set v = (data.max_reservable_bandwidth | int) * mbit_per_second_2_byte_per_second %}
28+
max-rsv-bw {{ v }}
29+
{% for i in range(8) %}
30+
unrsv-bw {{ i }} {{ v }}
31+
{% endfor %}
32+
{% endif %}
33+
exit-link-params
34+
exit
35+
!
36+
{% endfor %}
37+
{% endif %}

interface-definitions/include/isis/protocol-common-config.xml.i

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@
266266
</constraint>
267267
</properties>
268268
</leafNode>
269+
<leafNode name="export">
270+
<properties>
271+
<help>Export Traffic Engineering Database, see options under protocols traffic-engineering</help>
272+
<valueless/>
273+
</properties>
274+
</leafNode>
269275
</children>
270276
</node>
271277
<node name="segment-routing">
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<interfaceDefinition>
3+
<node name="protocols">
4+
<children>
5+
<node name="traffic-engineering" owner="${vyos_conf_scripts_dir}/protocols_traffic_engineering.py">
6+
<properties>
7+
<help>Traffic Engineering link parameters</help>
8+
<priority>605</priority>
9+
</properties>
10+
<children>
11+
<tagNode name="admin-group">
12+
<properties>
13+
<help>Configure administrative groups that can be used in interface configuration</help>
14+
<valueHelp>
15+
<format>txt</format>
16+
<description>Administrative group name</description>
17+
</valueHelp>
18+
<constraint>
19+
<regex>[-a-zA-Z0-9]+</regex>
20+
</constraint>
21+
<constraintErrorMessage>Administrative group must be alphanumeric and can contain hyphens</constraintErrorMessage>
22+
</properties>
23+
<children>
24+
<leafNode name="bit-position">
25+
<properties>
26+
<help>Specify bit position of the admin group</help>
27+
<valueHelp>
28+
<format>u8:0-31</format>
29+
<description>Admin group bit position</description>
30+
</valueHelp>
31+
<constraint>
32+
<validator name="numeric" argument="--range 0-31"/>
33+
</constraint>
34+
</properties>
35+
</leafNode>
36+
</children>
37+
</tagNode>
38+
<tagNode name="interface">
39+
<properties>
40+
<help>Traffic engineering parameters for interface</help>
41+
<completionHelp>
42+
<script>${vyos_completion_dir}/list_interfaces</script>
43+
</completionHelp>
44+
<valueHelp>
45+
<format>txt</format>
46+
<description>Interface to configure name</description>
47+
</valueHelp>
48+
<constraint>
49+
#include <include/constraint/interface-name.xml.i>
50+
</constraint>
51+
</properties>
52+
<children>
53+
<leafNode name="metric">
54+
<properties>
55+
<help>TE metric</help>
56+
<valueHelp>
57+
<format>u32:1-4294967295</format>
58+
<description>TE Metric (different from the OSPF or ISIS metric)</description>
59+
</valueHelp>
60+
<constraint>
61+
<validator name="numeric" argument="--range 1-4294967295"/>
62+
</constraint>
63+
</properties>
64+
</leafNode>
65+
<leafNode name="max-bandwidth">
66+
<properties>
67+
<help>Maximum bandwidth (interface speed by default)</help>
68+
<valueHelp>
69+
<format>u32:1-4294967295</format>
70+
<description>Maximum bandwidth in Mbits/sec</description>
71+
</valueHelp>
72+
<constraint>
73+
<validator name="numeric" argument="--range 1-4294967295"/>
74+
</constraint>
75+
</properties>
76+
</leafNode>
77+
<leafNode name="max-reservable-bandwidth">
78+
<properties>
79+
<help>Maximum reservable bandwidth</help>
80+
<valueHelp>
81+
<format>u32:1-4294967295</format>
82+
<description>Maximum reservable bandwidth in Mbits/sec</description>
83+
</valueHelp>
84+
<constraint>
85+
<validator name="numeric" argument="--range 1-4294967295"/>
86+
</constraint>
87+
</properties>
88+
</leafNode>
89+
<leafNode name="admin-group">
90+
<properties>
91+
<help>Admin groups of interface</help>
92+
<valueHelp>
93+
<format>txt</format>
94+
<description>Admin group of interface</description>
95+
</valueHelp>
96+
<completionHelp>
97+
<path>protocols traffic-engineering admin-group</path>
98+
</completionHelp>
99+
<multi/>
100+
</properties>
101+
</leafNode>
102+
</children>
103+
</tagNode>
104+
</children>
105+
</node>
106+
</children>
107+
</node>
108+
</interfaceDefinition>

python/vyos/frrender.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,26 @@ def debug(message):
5151
'new routing daemon configuration. To ensure network stability and avoid ' \
5252
'potential connectivity disruptions, the configuration was not applied!'
5353

54-
frr_protocols = ['babel', 'bfd', 'bgp', 'eigrp', 'isis', 'mpls', 'nhrp',
55-
'openfabric', 'ospf', 'ospfv3', 'pim', 'pim6', 'rip',
56-
'ripng', 'rpki', 'segment_routing', 'static']
54+
frr_protocols = [
55+
'babel',
56+
'bfd',
57+
'bgp',
58+
'eigrp',
59+
'isis',
60+
'mpls',
61+
'nhrp',
62+
'openfabric',
63+
'ospf',
64+
'ospfv3',
65+
'pim',
66+
'pim6',
67+
'rip',
68+
'ripng',
69+
'rpki',
70+
'segment_routing',
71+
'static',
72+
'traffic_engineering',
73+
]
5774

5875
babel_daemon = 'babeld'
5976
bfd_daemon = 'bfdd'
@@ -426,6 +443,21 @@ def dict_helper_nhrp_defaults(nhrp):
426443
elif conf.exists_effective(sr_cli_path):
427444
dict.update({'segment_routing' : deleted_protocol})
428445

446+
# We need to check the CLI if the Traffic Engineering node is present and thus load in
447+
# all the default values present on the CLI - that's why we have if conf.exists()
448+
te_cli_path = ['protocols', 'traffic-engineering']
449+
if conf.exists(te_cli_path):
450+
te = conf.get_config_dict(
451+
te_cli_path,
452+
key_mangling=('-', '_'),
453+
get_first_key=True,
454+
no_tag_node_value_mangle=True,
455+
with_recursive_defaults=True,
456+
)
457+
dict.update({'traffic_engineering': te})
458+
elif conf.exists_effective(te_cli_path):
459+
dict.update({'traffic_engineering': deleted_protocol})
460+
429461
# We need to check the CLI if the static node is present and thus load in
430462
# all the default values present on the CLI - that's why we have if conf.exists()
431463
static_cli_path = ['protocols', 'static']
@@ -722,6 +754,15 @@ def inline_helper(config_dict) -> str:
722754
if 'segment_routing' in config_dict and 'deleted' not in config_dict['segment_routing']:
723755
output += render_to_string('frr/zebra.segment_routing.frr.j2', config_dict['segment_routing'])
724756
output += '\n'
757+
if (
758+
'traffic_engineering' in config_dict
759+
and 'deleted' not in config_dict['traffic_engineering']
760+
):
761+
output += render_to_string(
762+
'frr/zebra.traffic_engineering.frr.j2',
763+
config_dict['traffic_engineering'],
764+
)
765+
output += '\n'
725766
if 'static' in config_dict and 'deleted' not in config_dict['static']:
726767
output += render_to_string('frr/staticd.frr.j2', config_dict['static'])
727768
output += '\n'
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
4+
#
5+
# This program is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License version 2 or later as
7+
# published by the Free Software Foundation.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import unittest
18+
19+
from base_vyostest_shim import VyOSUnitTestSHIM
20+
21+
from vyos.configsession import ConfigSessionError
22+
from vyos.frrender import zebra_daemon
23+
from vyos.utils.process import process_named_running
24+
25+
base_path = ['protocols', 'traffic-engineering']
26+
27+
dummy_if1 = 'dum2191'
28+
dummy_if2 = 'dum2192'
29+
30+
31+
class TestProtocolsTrafficEngineering(VyOSUnitTestSHIM.TestCase):
32+
@classmethod
33+
def setUpClass(cls):
34+
# call base-classes classmethod
35+
super(TestProtocolsTrafficEngineering, cls).setUpClass()
36+
# Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
37+
cls.daemon_pid = process_named_running(zebra_daemon)
38+
# ensure we can also run this test on a live system - so lets clean
39+
# out the current configuration :)
40+
cls.cli_delete(cls, base_path)
41+
42+
cls.cli_set(cls, ['interfaces', 'dummy', dummy_if1])
43+
cls.cli_set(cls, ['interfaces', 'dummy', dummy_if2])
44+
cls.cli_commit(cls)
45+
46+
@classmethod
47+
def tearDownClass(cls):
48+
cls.cli_delete(cls, ['interfaces', 'dummy', dummy_if2])
49+
cls.cli_delete(cls, ['interfaces', 'dummy', dummy_if1])
50+
cls.cli_commit(cls)
51+
52+
super(TestProtocolsTrafficEngineering, cls).tearDownClass()
53+
54+
def tearDown(self):
55+
self.cli_delete(base_path)
56+
self.cli_commit()
57+
58+
# check process health and continuity
59+
self.assertEqual(self.daemon_pid, process_named_running(zebra_daemon))
60+
# always forward to base class
61+
super().tearDown()
62+
63+
def test_te_normal(self):
64+
self.cli_set(base_path + ['admin-group', 'cyan', 'bit-position', '1'])
65+
self.cli_set(base_path + ['admin-group', 'magenta', 'bit-position', '3'])
66+
67+
self.cli_set(base_path + ['interface', dummy_if1, 'admin-group', 'magenta'])
68+
self.cli_set(base_path + ['interface', dummy_if1, 'max-bandwidth', '1024'])
69+
self.cli_set(
70+
base_path + ['interface', dummy_if1, 'max-reservable-bandwidth', '2048']
71+
)
72+
self.cli_set(base_path + ['interface', dummy_if1, 'metric', '74837'])
73+
74+
self.cli_set(base_path + ['interface', dummy_if2, 'admin-group', 'cyan'])
75+
self.cli_set(base_path + ['interface', dummy_if2, 'admin-group', 'magenta'])
76+
77+
self.cli_commit()
78+
79+
frrconfig = self.getFRRconfig(f'^interface {dummy_if1}', stop_section='^exit')
80+
self.assertIn('link-params', frrconfig)
81+
self.assertIn('metric 74837', frrconfig)
82+
self.assertIn('admin-grp 0x8', frrconfig)
83+
self.assertIn('max-bw 1.34218e+08', frrconfig)
84+
self.assertIn('max-rsv-bw 2.68435e+08', frrconfig)
85+
self.assertIn('unrsv-bw 0 2.68435e+08', frrconfig)
86+
self.assertIn('unrsv-bw 1 2.68435e+08', frrconfig)
87+
self.assertIn('unrsv-bw 2 2.68435e+08', frrconfig)
88+
self.assertIn('unrsv-bw 3 2.68435e+08', frrconfig)
89+
self.assertIn('unrsv-bw 4 2.68435e+08', frrconfig)
90+
self.assertIn('unrsv-bw 5 2.68435e+08', frrconfig)
91+
self.assertIn('unrsv-bw 6 2.68435e+08', frrconfig)
92+
self.assertIn('unrsv-bw 7 2.68435e+08', frrconfig)
93+
94+
frrconfig = self.getFRRconfig(f'^interface {dummy_if2}', stop_section='^exit')
95+
self.assertIn('link-params', frrconfig)
96+
self.assertIn('admin-grp 0xa', frrconfig)
97+
98+
def test_te_verify(self):
99+
self.cli_set(base_path + ['interface', dummy_if1, 'admin-group', 'cyan'])
100+
101+
# Unknown group
102+
with self.assertRaises(ConfigSessionError):
103+
self.cli_commit()
104+
105+
self.cli_set(base_path + ['admin-group', 'cyan', 'bit-position', '0'])
106+
self.cli_set(base_path + ['admin-group', 'magenta', 'bit-position', '4'])
107+
108+
# Now group is known
109+
self.cli_commit()
110+
111+
self.cli_set(base_path + ['admin-group', 'red', 'bit-position', '4'])
112+
113+
# Same bit position as other group
114+
with self.assertRaises(ConfigSessionError):
115+
self.cli_commit()
116+
117+
self.cli_set(base_path + ['admin-group', 'red', 'bit-position', '2'])
118+
# Now should be ok
119+
self.cli_commit()
120+
121+
122+
if __name__ == '__main__':
123+
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())

0 commit comments

Comments
 (0)