summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/frr/daemons.frr.tmpl4
-rw-r--r--data/templates/frr/zebra.segment_routing.frr.j239
-rw-r--r--interface-definitions/include/segment-routing/algorithm.xml.i20
-rw-r--r--interface-definitions/protocols_segment-routing.xml.in200
-rw-r--r--op-mode-definitions/show-segment-routing.xml.in17
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment-routing.py97
-rwxr-xr-xsrc/conf_mode/protocols_segment-routing.py68
7 files changed, 434 insertions, 11 deletions
diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl
index fa39ae9d0..32ef1079b 100644
--- a/data/templates/frr/daemons.frr.tmpl
+++ b/data/templates/frr/daemons.frr.tmpl
@@ -12,7 +12,7 @@
# eigrp
# sharpd
# fabricd
-# pathd
+#
#
# The zebra, mgmtd and staticd daemons are always started and can not be disabled
#
@@ -37,7 +37,7 @@ pbrd=no
bfdd=yes
fabricd=yes
vrrpd=no
-pathd=no
+pathd=yes
#
# Define defaults for all services even those who shall be kept disabled.
diff --git a/data/templates/frr/zebra.segment_routing.frr.j2 b/data/templates/frr/zebra.segment_routing.frr.j2
index a0bcc9210..b3f2a1fbb 100644
--- a/data/templates/frr/zebra.segment_routing.frr.j2
+++ b/data/templates/frr/zebra.segment_routing.frr.j2
@@ -1,6 +1,43 @@
!
-{% if srv6 is vyos_defined %}
segment-routing
+{% if traffic_engineering is vyos_defined %}
+ traffic-eng
+ mpls-te on
+{% if traffic_engineering.database_import_protocol is vyos_defined %}
+{% for protocol, protocol_config in traffic_engineering.database_import_protocol.items() %}
+ mpls-te import {{ protocol | replace('ospf', 'ospfv2') }}
+{% endfor %}
+{% endif %}
+{% if traffic_engineering.segment_list is vyos_defined %}
+{% for segment_list, segment_list_config in traffic_engineering.segment_list.items() %}
+ segment-list {{ segment_list }}
+{% if segment_list_config.index.items() is vyos_defined %}
+{% for index, index_config in segment_list_config.index.items() %}
+{% if index_config.mpls.label is vyos_defined %}
+ index {{ index }} mpls label {{ index_config.mpls.label }}
+{% endif %}
+{% if index_config.nai is vyos_defined %}
+{% if index_config.nai.adjacency is vyos_defined %}
+{% for address_family, address_family_options in index_config.nai.adjacency.items() %}
+ index {{ index }} nai adjacency {{ address_family_options.source_identifier }} {{ address_family_options.destination_identifier }}
+{% endfor %}
+{% endif %}
+{% if index_config.nai.prefix is vyos_defined %}
+{% for address_family, address_family_options in index_config.nai.prefix.items() %}
+{% for prefix, prefix_options in address_family_options.prefix_identifier.items() %}
+ index {{ index }} nai prefix {{ prefix }} {{ 'algorithm 0' if prefix_options.algorithm.spf is vyos_defined }} {{ 'algorithm 1' if prefix_options.algorithm.strict_spf is vyos_defined }}
+{% endfor %}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ exit
+{% endif %}
+!
+{% if srv6 is vyos_defined %}
srv6
{% if srv6.encapsulation is vyos_defined %}
encapsulation
diff --git a/interface-definitions/include/segment-routing/algorithm.xml.i b/interface-definitions/include/segment-routing/algorithm.xml.i
new file mode 100644
index 000000000..dae019b22
--- /dev/null
+++ b/interface-definitions/include/segment-routing/algorithm.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from segment-routing/algorithm.xml.i -->
+<node name="algorithm">
+ <properties>
+ <help>IGP prefix algorithm style</help>
+ </properties>
+ <children>
+ <leafNode name="spf">
+ <properties>
+ <help>Shortest Path First (SPF)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="strict-spf">
+ <properties>
+ <help>Strict Shortest Path First (SPF) - ignore any possible local policy overriding the SPF along the path</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node> \ No newline at end of file
diff --git a/interface-definitions/protocols_segment-routing.xml.in b/interface-definitions/protocols_segment-routing.xml.in
index 32e61ebfc..cab1e6945 100644
--- a/interface-definitions/protocols_segment-routing.xml.in
+++ b/interface-definitions/protocols_segment-routing.xml.in
@@ -157,6 +157,206 @@
</tagNode>
</children>
</node>
+ <node name="traffic-engineering">
+ <properties>
+ <help>SR Traffic Engineering (TE) configuration</help>
+ </properties>
+ <children>
+ <node name="database-import-protocol">
+ <properties>
+ <help>Traffic Engineering Database (TED) IGP import protocol</help>
+ </properties>
+ <children>
+ <leafNode name="isis">
+ <properties>
+ <help>IS-IS originated Traffic Engineering (TE) database</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ospf">
+ <properties>
+ <help>OSPF originated Traffic Engineering (TE) database</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="segment-list">
+ <properties>
+ <help>Segment List</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Segment List Name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i>
+ </constraint>
+ </properties>
+ <children>
+ <tagNode name="index">
+ <properties>
+ <help>Traffic engineering index value for segment list</help>
+ <valueHelp>
+ <format>u32</format>
+ <description>Segment list index value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967295"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="mpls">
+ <properties>
+ <help>MPLS label for index</help>
+ </properties>
+ <children>
+ <leafNode name="label">
+ <properties>
+ <help>MPLS label value for index</help>
+ <valueHelp>
+ <format>u32:16-1048575</format>
+ <description>MPLS label value for index</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 16-1048575"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="nai">
+ <properties>
+ <help>Node or Adjacency identifier (NAI) for index</help>
+ </properties>
+ <children>
+ <node name="adjacency">
+ <properties>
+ <help>Adjacency identifier for index</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>IPv4 address</help>
+ </properties>
+ <children>
+ <leafNode name="source-identifier">
+ <properties>
+ <help>Adjacency source address identifier for index</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 adjacency source address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="destination-identifier">
+ <properties>
+ <help>Adjacency destination address identifier for index</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 adjacency destination address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>IPv6 address</help>
+ </properties>
+ <children>
+ <leafNode name="source-identifier">
+ <properties>
+ <help>Adjacency source address identifier for index</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 adjacency source address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="destination-identifier">
+ <properties>
+ <help>Adjacency destination address identifier for index</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 adjacency destination address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="prefix">
+ <properties>
+ <help>IGP prefix identifier for index</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>IPv4 address</help>
+ </properties>
+ <children>
+ <tagNode name="prefix-identifier">
+ <properties>
+ <help>IPv4 IGP prefix address identifier for index</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 adjacency source address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/segment-routing/algorithm.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>IPv6 address</help>
+ </properties>
+ <children>
+ <tagNode name="prefix-identifier">
+ <properties>
+ <help>IPv6 IGP prefix address identifier for index</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 adjacency source address identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/segment-routing/algorithm.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/op-mode-definitions/show-segment-routing.xml.in b/op-mode-definitions/show-segment-routing.xml.in
index fc17e2f39..0f7fa5d76 100644
--- a/op-mode-definitions/show-segment-routing.xml.in
+++ b/op-mode-definitions/show-segment-routing.xml.in
@@ -4,12 +4,12 @@
<children>
<node name="segment-routing">
<properties>
- <help>Show Segment Routing</help>
+ <help>Show Segment Routing Information</help>
</properties>
<children>
<node name="srv6">
<properties>
- <help>Segment Routing SRv6</help>
+ <help>Segment Routing over IPv6 information</help>
</properties>
<children>
<node name="locator">
@@ -26,6 +26,19 @@
</node>
</children>
</node>
+ <node name="traffic-engineering">
+ <properties>
+ <help>Traffic Engineering information</help>
+ </properties>
+ <children>
+ <leafNode name="database">
+ <properties>
+ <help>Show Traffic Engineering database</help>
+ </properties>
+ <command>vtysh -c "show pathd ted database verbose"</command>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
index 7be76ee0c..09d32445a 100755
--- a/smoketest/scripts/cli/test_protocols_segment-routing.py
+++ b/smoketest/scripts/cli/test_protocols_segment-routing.py
@@ -220,5 +220,102 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
self.assertEqual(sysctl_read(['net', 'ipv6', 'conf', first_if, 'seg6_enabled']), '0')
+ def test_srte_database(self):
+ for protocol in ['isis', 'ospf']:
+ self.cli_set(base_path + ['traffic-engineering', 'database-import-protocol', protocol])
+ # IS-IS and OSPF are mutually exclusive
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Add database-import-protocol for isis and check the config
+ for protocol in ['isis', 'ospf']:
+ self.cli_delete(base_path)
+ self.cli_set(base_path + ['traffic-engineering', 'database-import-protocol', protocol])
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'segment-routing', stop_section='^exit')
+ self.assertIn('segment-routing', frrconfig)
+ self.assertIn(' traffic-eng', frrconfig)
+ self.assertIn(' mpls-te on', frrconfig)
+ self.assertIn(f' mpls-te import {protocol}', frrconfig)
+
+ def test_srte_mpls_label(self):
+ # Add segment-list with an mpls value
+ mpls_label = '500'
+ segment_list = 'smoketest-mpls-only-segment-list'
+ index_value = '0'
+
+ self.cli_set(base_path + ['traffic-engineering', 'segment-list', segment_list,
+ 'index', index_value, 'mpls', 'label', mpls_label])
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'segment-routing', stop_section='^exit')
+ self.assertIn('segment-routing', frrconfig)
+ self.assertIn(' traffic-eng', frrconfig)
+ self.assertIn(' mpls-te on', frrconfig)
+ self.assertIn(f' segment-list {segment_list}', frrconfig)
+ self.assertIn(f' index {index_value} mpls label {mpls_label}', frrconfig)
+
+ def test_srte_mpls_label_and_adjacency(self):
+ # Add segment-list with an mpls value and adjacency
+ mpls_label = '1000'
+ segment_list = 'smoketest-mpls-with-adjacency-segment-list'
+ index_value = '0'
+
+ addresses = {'ipv4' : {'source_identifier' : '192.168.255.1', 'destination_identifier' : '192.168.255.2'},
+ 'ipv6' : {'source_identifier' : '2003::1', 'destination_identifier' : '2003::2'}}
+
+ test_path = base_path + ['traffic-engineering', 'segment-list', segment_list, 'index', index_value]
+ for address_family in ['ipv4', 'ipv6']:
+ source_identifier = addresses[address_family]['source_identifier']
+ destination_identifier = addresses[address_family]['destination_identifier']
+ # Make testcase re-entrant for next for loop
+ self.cli_delete(test_path)
+
+ self.cli_set(test_path + ['mpls', 'label', mpls_label])
+ self.cli_set(test_path + ['nai', 'adjacency', address_family, 'source-identifier', source_identifier])
+ self.cli_set(test_path + ['nai', 'adjacency', address_family, 'destination-identifier', destination_identifier])
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'segment-routing', stop_section='^exit')
+ self.assertIn(f'segment-routing', frrconfig)
+ self.assertIn(f' traffic-eng', frrconfig)
+ self.assertIn(f' mpls-te on', frrconfig)
+ self.assertIn(f' segment-list {segment_list}', frrconfig)
+ self.assertIn(f' index {index_value} mpls label {mpls_label}', frrconfig)
+ self.assertIn(f' index {index_value} nai adjacency {source_identifier} {destination_identifier}', frrconfig)
+
+ def test_srte_mpls_label_and_prefix(self):
+ # Add segment-list with an mpls value and prefix
+ mpls_label = '1500'
+ segment_list = 'smoketest-mpls-with-prefix-segment-list'
+ index_value = '0'
+
+ prefixes = {'ipv4' : {'prefix' : '192.168.255.0/24'},
+ 'ipv6' : {'prefix' : '2003::/120'}}
+
+ test_path = base_path + ['traffic-engineering', 'segment-list', segment_list, 'index', index_value]
+ for address_family in ['ipv4', 'ipv6']:
+ for algorithm_type in ['spf', 'strict-spf']:
+ prefix = prefixes[address_family]['prefix']
+ # Make testcase re-entrant for next for loop
+ self.cli_delete(test_path)
+
+ self.cli_set(test_path + ['mpls', 'label', mpls_label])
+ self.cli_set(test_path + ['nai', 'prefix', address_family, 'prefix-identifier', prefix, 'algorithm', algorithm_type])
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'segment-routing', stop_section='^exit')
+ self.assertIn(f'segment-routing', frrconfig)
+ self.assertIn(f' traffic-eng', frrconfig)
+ self.assertIn(f' mpls-te on', frrconfig)
+ self.assertIn(f' segment-list {segment_list}', frrconfig)
+ if algorithm_type == 'spf':
+ self.assertIn(f' index {index_value} mpls label {mpls_label}', frrconfig)
+ self.assertIn(f' index {index_value} nai prefix {prefix} algorithm 0', frrconfig)
+ elif algorithm_type == 'strict-spf':
+ self.assertIn(f' index {index_value} mpls label {mpls_label}', frrconfig)
+ self.assertIn(f' index {index_value} nai prefix {prefix} algorithm 1', frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())
diff --git a/src/conf_mode/protocols_segment-routing.py b/src/conf_mode/protocols_segment-routing.py
index 8a4d12bd8..b9689557e 100755
--- a/src/conf_mode/protocols_segment-routing.py
+++ b/src/conf_mode/protocols_segment-routing.py
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sys import exit
+from sys import argv
from vyos.config import Config
from vyos.configdict import list_diff
@@ -35,7 +36,7 @@ def get_config(config=None):
else:
conf = Config()
- return get_frrender_dict(conf)
+ return get_frrender_dict(conf, argv)
def verify(config_dict):
if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
@@ -45,13 +46,68 @@ def verify(config_dict):
if 'srv6' in sr:
srv6_enable = False
- if 'interface' in sr:
- for interface, interface_config in sr['interface'].items():
- if 'srv6' in interface_config:
- srv6_enable = True
- break
+ for _, interface_config in dict_search('interface', sr, {}).items():
+ if 'srv6' in interface_config:
+ srv6_enable = True
+ break
if not srv6_enable:
raise ConfigError('SRv6 should be enabled on at least one interface!')
+
+ # Check for database import having more than one protocol
+ if tmp := dict_search('traffic_engineering.database_import_protocol', sr):
+ if {'isis', 'ospf'} <= set(tmp.keys()):
+ raise ConfigError('SR-TE database import: IS-IS and OSPF are mutually exclusive!')
+
+ for segment_list in dict_search('traffic_engineering.segment_list', sr, []):
+ sl_data = dict_search(f'traffic_engineering.segment_list.{segment_list}', sr)
+ indices = sl_data.get('index') if sl_data else None
+
+ if indices is None:
+ raise ConfigError(f'SR-TE segment list "{segment_list}": '\
+ 'at least one index is required!')
+
+ for index, index_data in indices.items():
+ error_msg = f'SR-TE segment list "{segment_list}", index "{index}"'
+ nai = index_data.get('nai')
+ mpls = index_data.get('mpls')
+ if not nai and not mpls:
+ raise ConfigError(f'{error_msg}: "mpls" or "nai" is required!')
+
+ if nai:
+ if 'adjacency' in nai and 'prefix' in nai:
+ raise ConfigError(f'{error_msg}: "prefix" and "adjacency" are mutually exclusive!')
+
+ for nai_type in ('adjacency', 'prefix'):
+ nai_data = nai.get(nai_type)
+ if not nai_data:
+ continue
+
+ if 'ipv4' in nai_data and 'ipv6' in nai_data:
+ raise ConfigError(f'{error_msg}, nai {nai_type}: "ipv4" and "ipv6" are '
+ 'mutually exclusive!')
+
+ for af, af_config in nai_data.items():
+ af_ctx = f'{error_msg}, nai {nai_type} {af}'
+ if nai_type == 'adjacency':
+ has_src = 'source_identifier' in af_config
+ has_dst = 'destination_identifier' in af_config
+ if has_src != has_dst:
+ missing = 'destination-identifier' if has_src else 'source-identifier'
+ raise ConfigError(f'{af_ctx}: "{missing}" is required!')
+ else:
+ if 'prefix_identifier' not in af_config:
+ raise ConfigError(f'{af_ctx}: "prefix-identifier" is required!')
+
+ for pfx, pfx_data in af_config['prefix_identifier'].items():
+ pfx_ctx = f'{af_ctx}, prefix "{pfx}"'
+ if 'algorithm' not in pfx_data:
+ raise ConfigError(f'{pfx_ctx}: "algorithm" is required!')
+
+ if alg := pfx_data.get('algorithm'):
+ if {'spf', 'strict_spf'} <= set(alg.keys()):
+ raise ConfigError(f'{pfx_ctx}: "spf" and "strict-spf" '
+ 'are mutually exclusive!')
+
return None
def generate(config_dict):