diff options
| author | Christian Breunig <christian@breunig.cc> | 2023-12-10 21:13:54 +0100 | 
|---|---|---|
| committer | Christian Breunig <christian@breunig.cc> | 2023-12-11 18:21:26 +0100 | 
| commit | ca301cdd4746187f96ff84e411fda6a84e33f237 (patch) | |
| tree | 9cc422258208424d18b1f8fb19f3b1ba9f3d2ef2 | |
| parent | af46fe54e56cf85d13b62ee771bec3d80f225ac5 (diff) | |
| download | vyos-1x-ca301cdd4746187f96ff84e411fda6a84e33f237.tar.gz vyos-1x-ca301cdd4746187f96ff84e411fda6a84e33f237.zip | |
srv6: T591: initial implementation to support locator definition
VyOS CLI
set protocols segment-routing srv6 locator bar prefix '2001:b::/64'
set protocols segment-routing srv6 locator foo behavior-usid
set protocols segment-routing srv6 locator foo prefix '2001:a::/64'
Will generate in FRR
segment-routing
 srv6
  locators
   locator bar
    prefix 2001:b::/64 block-len 40 node-len 24 func-bits 16
   exit
   !
   locator foo
    prefix 2001:a::/64 block-len 40 node-len 24 func-bits 16
    behavior usid
   exit
   !
  exit
  !
 exit
 !
exit
| -rw-r--r-- | data/configd-include.json | 1 | ||||
| -rw-r--r-- | data/templates/frr/zebra.segment_routing.frr.j2 | 23 | ||||
| -rw-r--r-- | interface-definitions/protocols-segment-routing.xml.in | 89 | ||||
| -rw-r--r-- | op-mode-definitions/show-segment-routing.xml.in | 27 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_protocols_segment_routing.py | 70 | ||||
| -rwxr-xr-x | src/conf_mode/protocols_segment_routing.py | 74 | 
6 files changed, 284 insertions, 0 deletions
| diff --git a/data/configd-include.json b/data/configd-include.json index a762a6d4c..92d3863ce 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -53,6 +53,7 @@  "protocols_rip.py",  "protocols_ripng.py",  "protocols_rpki.py", +"protocols_segment_routing.py",  "protocols_static.py",  "protocols_static_multicast.py",  "qos.py", diff --git a/data/templates/frr/zebra.segment_routing.frr.j2 b/data/templates/frr/zebra.segment_routing.frr.j2 new file mode 100644 index 000000000..7b12fcdd0 --- /dev/null +++ b/data/templates/frr/zebra.segment_routing.frr.j2 @@ -0,0 +1,23 @@ +! +{% if srv6.locator is vyos_defined %} +segment-routing + srv6 +  locators +{%     for locator, locator_config in srv6.locator.items() %} +   locator {{ locator }} +{%         if locator_config.prefix is vyos_defined %} +    prefix {{ locator_config.prefix }} block-len {{ locator_config.block_len }} node-len {{ locator_config.node_len }} func-bits {{ locator_config.func_bits }} +{%         endif %} +{%         if locator_config.behavior_usid is vyos_defined %} +    behavior usid +{%         endif %} +    exit +    ! +{%     endfor %} +  exit +  ! +exit +! +exit +! +{% endif %} diff --git a/interface-definitions/protocols-segment-routing.xml.in b/interface-definitions/protocols-segment-routing.xml.in new file mode 100644 index 000000000..d461e9c5d --- /dev/null +++ b/interface-definitions/protocols-segment-routing.xml.in @@ -0,0 +1,89 @@ +<?xml version="1.0"?> +<interfaceDefinition> +  <node name="protocols"> +    <children> +       <node name="segment-routing" owner="${vyos_conf_scripts_dir}/protocols_segment_routing.py"> +        <properties> +          <help>Segment Routing</help> +          <priority>900</priority> +        </properties> +        <children> +          <node name="srv6"> +            <properties> +              <help>Segment-Routing SRv6 configuration</help> +            </properties> +            <children> +              <tagNode name="locator"> +                <properties> +                  <help>Segment Routing SRv6 locator</help> +                  <constraint> +                    #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> +                  </constraint> +                </properties> +                <children> +                  <leafNode name="behavior-usid"> +                    <properties> +                      <help>Set SRv6 behavior uSID</help> +                      <valueless/> +                    </properties> +                  </leafNode> +                  <leafNode name="prefix"> +                    <properties> +                      <help>SRv6 locator prefix</help> +                      <valueHelp> +                        <format>ipv6net</format> +                        <description>SRv6 locator prefix</description> +                      </valueHelp> +                      <constraint> +                        <validator name="ipv6-prefix"/> +                      </constraint> +                    </properties> +                  </leafNode> +                  <leafNode name="block-len"> +                    <properties> +                      <help>Configure SRv6 locator block length in bits</help> +                      <valueHelp> +                        <format>u32:16-64</format> +                        <description>Specify SRv6 locator block length in bits</description> +                      </valueHelp> +                      <constraint> +                        <validator name="numeric" argument="--range 16-64"/> +                      </constraint> +                    </properties> +                    <defaultValue>40</defaultValue> +                  </leafNode> +                  <leafNode name="func-bits"> +                    <properties> +                      <help>Configure SRv6 locator function length in bits</help> +                      <valueHelp> +                        <format>u32:0-64</format> +                        <description>Specify SRv6 locator function length in bits</description> +                      </valueHelp> +                      <constraint> +                        <validator name="numeric" argument="--range 0-64"/> +                      </constraint> +                    </properties> +                    <defaultValue>16</defaultValue> +                  </leafNode> +                  <leafNode name="node-len"> +                    <properties> +                      <help>Configure SRv6 locator node length in bits</help> +                      <valueHelp> +                        <format>u32:16-64</format> +                        <description>Configure SRv6 locator node length in bits</description> +                      </valueHelp> +                      <constraint> +                        <validator name="numeric" argument="--range 16-64"/> +                      </constraint> +                    </properties> +                    <defaultValue>24</defaultValue> +                  </leafNode> +                </children> +              </tagNode> +            </children> +          </node> +        </children> +      </node> +    </children> +  </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-segment-routing.xml.in b/op-mode-definitions/show-segment-routing.xml.in new file mode 100644 index 000000000..ebdb51a61 --- /dev/null +++ b/op-mode-definitions/show-segment-routing.xml.in @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<interfaceDefinition> +  <node name="show"> +    <children> +      <node name="segment-routing"> +        <properties> +          <help>Show Segment Routing</help> +        </properties> +        <children> +          <node name="srv6"> +            <properties> +              <help>Segment Routing SRv6</help> +            </properties> +            <children> +              <node name="locator"> +                <properties> +                  <help>Locator Information</help> +                </properties> +                <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +              </node> +            </children> +          </node> +        </children> +      </node> +    </children> +  </node> +</interfaceDefinition> diff --git a/smoketest/scripts/cli/test_protocols_segment_routing.py b/smoketest/scripts/cli/test_protocols_segment_routing.py new file mode 100755 index 000000000..81d42b925 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_segment_routing.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSessionError +from vyos.utils.process import cmd +from vyos.utils.process import process_named_running + +base_path = ['protocols', 'segment-routing'] +PROCESS_NAME = 'zebra' + +class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): +    @classmethod +    def setUpClass(cls): +        # call base-classes classmethod +        super(TestProtocolsSegmentRouting, cls).setUpClass() +        # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same +        cls.daemon_pid = process_named_running(PROCESS_NAME) +        # ensure we can also run this test on a live system - so lets clean +        # out the current configuration :) +        cls.cli_delete(cls, base_path) + +    def tearDown(self): +        self.cli_delete(base_path) +        self.cli_commit() + +        # check process health and continuity +        self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME)) + +    def test_srv6(self): +        locators = { +            'foo' : { 'prefix' : '2001:a::/64' }, +            'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} }, +        } + +        for locator, locator_config in locators.items(): +            self.cli_set(base_path + ['srv6', 'locator', locator, 'prefix', locator_config['prefix']]) +            if 'usid' in locator_config: +                self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid']) + +        self.cli_commit() + +        frrconfig = self.getFRRconfig(f'segment-routing', daemon='zebra') +        self.assertIn(f'segment-routing', frrconfig) +        self.assertIn(f' srv6', frrconfig) +        self.assertIn(f'  locators', frrconfig) +        for locator, locator_config in locators.items(): +            self.assertIn(f'   locator {locator}', frrconfig) +            self.assertIn(f'    prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', frrconfig) + + +if __name__ == '__main__': +    unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_segment_routing.py b/src/conf_mode/protocols_segment_routing.py new file mode 100755 index 000000000..eb1653212 --- /dev/null +++ b/src/conf_mode/protocols_segment_routing.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() + +    base = ['protocols', 'segment-routing'] +    sr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + +    # We have gathered the dict representation of the CLI, but there are default +    # options which we need to update into the dictionary retrived. +    sr = conf.merge_defaults(sr, recursive=True) + +    return sr + +def verify(static): +    return None + +def generate(static): +    if not static: +        return None + +    static['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', static) +    return None + +def apply(static): +    zebra_daemon = 'zebra' + +    # Save original configuration prior to starting any commit actions +    frr_cfg = frr.FRRConfig() +    frr_cfg.load_configuration(zebra_daemon) +    frr_cfg.modify_section(r'^segment-routing') +    if 'new_frr_config' in static: +        frr_cfg.add_before(frr.default_add_before, static['new_frr_config']) +    frr_cfg.commit_configuration(zebra_daemon) + +    return None + +if __name__ == '__main__': +    try: +        c = get_config() +        verify(c) +        generate(c) +        apply(c) +    except ConfigError as e: +        print(e) +        exit(1) | 
