diff options
| author | Daniil Baturin <daniil@vyos.io> | 2022-05-25 15:35:29 +0300 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-25 15:35:29 +0300 | 
| commit | a943c7f36ffdd1e92070f5fcc94854b6b00f25b3 (patch) | |
| tree | 0de9be22f4d671397d6a0db1d6038f0c76671d9b | |
| parent | 0640a863255ef8f3d5b9d778fa0b6bff9922087e (diff) | |
| parent | 373132a899cd53eaebedd23bd44702d245ce8165 (diff) | |
| download | vyos-1x-a943c7f36ffdd1e92070f5fcc94854b6b00f25b3.tar.gz vyos-1x-a943c7f36ffdd1e92070f5fcc94854b6b00f25b3.zip | |
Merge pull request #1088 from zdc/T4020-sagitta
FRR: T4020: Added CLI options for FRR daemons
| -rw-r--r-- | data/templates/frr/daemons.frr.tmpl | 54 | ||||
| -rw-r--r-- | interface-definitions/system-frr.xml.in | 77 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_system_frr.py | 146 | ||||
| -rwxr-xr-x | src/conf_mode/snmp.py | 10 | ||||
| -rwxr-xr-x | src/conf_mode/system_frr.py | 91 | 
5 files changed, 377 insertions, 1 deletions
| diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl new file mode 100644 index 000000000..ab7b14d6b --- /dev/null +++ b/data/templates/frr/daemons.frr.tmpl @@ -0,0 +1,54 @@ +zebra=yes +bgpd=yes +ospfd=yes +ospf6d=yes +ripd=yes +ripngd=yes +isisd=yes +pimd=no +ldpd=yes +nhrpd=no +eigrpd=no +babeld=no +sharpd=no +pbrd=no +bfdd=yes +staticd=yes + +vtysh_enable=yes +zebra_options="  -s 90000000 --daemon -A 127.0.0.1 +{%- if irdp is defined %} -M irdp{% endif -%} +{%- if snmp is defined and snmp.zebra is defined %} -M snmp{% endif -%} +" +bgpd_options="   --daemon -A 127.0.0.1 +{%- if bmp is defined %} -M bmp{% endif -%} +{%- if snmp is defined and snmp.bgpd is defined %} -M snmp{% endif -%} +" +ospfd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ospfd is defined %} -M snmp{% endif -%} +" +ospf6d_options=" --daemon -A ::1 +{%- if snmp is defined and snmp.ospf6d is defined %} -M snmp{% endif -%} +" +ripd_options="   --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ripd is defined %} -M snmp{% endif -%} +" +ripngd_options=" --daemon -A ::1" +isisd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.isisd is defined %} -M snmp{% endif -%} +" +pimd_options="  --daemon -A 127.0.0.1" +ldpd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%} +" +nhrpd_options="  --daemon -A 127.0.0.1" +eigrpd_options="  --daemon -A 127.0.0.1" +babeld_options="  --daemon -A 127.0.0.1" +sharpd_options="  --daemon -A 127.0.0.1" +pbrd_options="  --daemon -A 127.0.0.1" +staticd_options="  --daemon -A 127.0.0.1" +bfdd_options="  --daemon -A 127.0.0.1" + +watchfrr_enable=no +valgrind_enable=no + diff --git a/interface-definitions/system-frr.xml.in b/interface-definitions/system-frr.xml.in new file mode 100644 index 000000000..9fe23ed75 --- /dev/null +++ b/interface-definitions/system-frr.xml.in @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interfaceDefinition> +  <node name="system"> +    <children> +      <node name="frr" owner="${vyos_conf_scripts_dir}/system_frr.py"> +        <properties> +          <help>Configure FRR parameters</help> +          <!-- Before components that use FRR --> +          <priority>150</priority> +        </properties> +        <children> +          <leafNode name="bmp"> +            <properties> +              <help>Enable BGP Monitoring Protocol support</help> +              <valueless/> +            </properties> +          </leafNode> +          <leafNode name="irdp"> +            <properties> +              <help>Enable ICMP Router Discovery Protocol support</help> +              <valueless/> +            </properties> +          </leafNode> +          <node name="snmp"> +            <properties> +              <help>Enable SNMP integration for next daemons</help> +            </properties> +            <children> +              <leafNode name="bgpd"> +                <properties> +                  <help>BGP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="isisd"> +                <properties> +                  <help>IS-IS</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ldpd"> +                <properties> +                  <help>LDP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ospf6d"> +                <properties> +                  <help>OSPFv3</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ospfd"> +                <properties> +                  <help>OSPFv2</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ripd"> +                <properties> +                  <help>RIP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="zebra"> +                <properties> +                  <help>Zebra (IP routing manager)</help> +                  <valueless/> +                </properties> +              </leafNode> +            </children> +          </node> +        </children> +      </node> +    </children> +  </node> +</interfaceDefinition> diff --git a/smoketest/scripts/cli/test_system_frr.py b/smoketest/scripts/cli/test_system_frr.py new file mode 100755 index 000000000..331133ed4 --- /dev/null +++ b/smoketest/scripts/cli/test_system_frr.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-2020 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 re +import unittest +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.util import read_file + +config_file = '/etc/frr/daemons' +base_path = ['system', 'frr'] + + +def daemons_config_parse(daemons_config): +    # create regex for parsing daemons options +    regex_daemon_config = re.compile( +        r'^(?P<daemon_name>\w+)_options="(?P<daemon_options>.*)"$', re.M) +    # create empty dict for config +    daemons_config_dict = {} +    # fill dictionary with actual config +    for daemon in regex_daemon_config.finditer(daemons_config): +        daemon_name = daemon.group('daemon_name') +        daemon_options = daemon.group('daemon_options') +        daemons_config_dict[daemon_name] = daemon_options + +    # return daemons config +    return (daemons_config_dict) + + +class TestSystemFRR(VyOSUnitTestSHIM.TestCase): + +    def tearDown(self): +        self.cli_delete(base_path) +        self.cli_commit() + +    def test_frr_snmp_multipledaemons(self): +        # test SNMP integration for multiple daemons +        test_daemon_names = ['ospfd', 'bgpd'] +        for test_daemon_name in test_daemon_names: +            self.cli_set(base_path + ['snmp', test_daemon_name]) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for (daemon_name, daemon_options) in daemons_config_dict.items(): +            snmp_enabled = regex_snmp.match(daemon_options) +            if daemon_name in test_daemon_names: +                self.assertTrue(snmp_enabled) +            else: +                self.assertFalse(snmp_enabled) + +    def test_frr_snmp_addandremove(self): +        # test enabling and disabling of SNMP integration +        test_daemon_names = ['ospfd', 'bgpd'] +        for test_daemon_name in test_daemon_names: +            self.cli_set(base_path + ['snmp', test_daemon_name]) +        self.cli_commit() + +        self.cli_delete(base_path) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for test_daemon_name in test_daemon_names: +            snmp_enabled = regex_snmp.match( +                daemons_config_dict[test_daemon_name]) +            self.assertFalse(snmp_enabled) + +    def test_frr_snmp_empty(self): +        # test empty config section +        self.cli_set(base_path + ['snmp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for daemon_options in daemons_config_dict.values(): +            snmp_enabled = regex_snmp.match(daemon_options) +            self.assertFalse(snmp_enabled) + +    def test_frr_bmp(self): +        # test BMP +        self.cli_set(base_path + ['bmp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_bmp = re.compile(r'^.* -M bmp.*$') +        bmp_enabled = regex_bmp.match(daemons_config_dict['bgpd']) +        self.assertTrue(bmp_enabled) + +    def test_frr_irdp(self): +        # test IRDP +        self.cli_set(base_path + ['irdp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_irdp = re.compile(r'^.* -M irdp.*$') +        irdp_enabled = regex_irdp.match(daemons_config_dict['zebra']) +        self.assertTrue(irdp_enabled) + +    def test_frr_bmpandsnmp(self): +        # test empty config section +        self.cli_set(base_path + ['bmp']) +        self.cli_set(base_path + ['snmp', 'bgpd']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_snmp = re.compile(r'^.* -M bmp.*$') +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        bmp_enabled = regex_snmp.match(daemons_config_dict['bgpd']) +        snmp_enabled = regex_snmp.match(daemons_config_dict['bgpd']) +        self.assertTrue(bmp_enabled) +        self.assertTrue(snmp_enabled) + + +if __name__ == '__main__': +    unittest.main(verbosity=2, failfast=True) diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index ae060580d..5cd24db32 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -293,7 +293,15 @@ def apply(snmp):      call(f'systemctl restart {systemd_service}')      # Enable AgentX in FRR -    call('vtysh -c "configure terminal" -c "agentx" >/dev/null') +    # This should be done for each daemon individually because common command +    # works only if all the daemons started with SNMP support +    frr_daemons_list = [ +        'bgpd', 'ospf6d', 'ospfd', 'ripd', 'ripngd', 'isisd', 'ldpd', 'zebra' +    ] +    for frr_daemon in frr_daemons_list: +        call( +            f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null' +        )      return None diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py new file mode 100755 index 000000000..1af0055f6 --- /dev/null +++ b/src/conf_mode/system_frr.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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/>. + +from pathlib import Path +from sys import exit + +from vyos import ConfigError +from vyos import airbag +from vyos.config import Config +from vyos.logger import syslog +from vyos.template import render_to_string +from vyos.util import read_file, write_file, run +airbag.enable() + +# path to daemons config and config status files +config_file = '/etc/frr/daemons' +vyos_status_file = '/tmp/vyos-config-status' +# path to watchfrr for FRR control +watchfrr = '/usr/lib/frr/watchfrr.sh' + + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() + +    base = ['system', 'frr'] +    frr_config = conf.get_config_dict(base, get_first_key=True) + +    return frr_config + + +def verify(frr_config): +    # Nothing to verify here +    pass + + +def generate(frr_config): +    # read daemons config file +    daemons_config_current = read_file(config_file) +    # generate new config file +    daemons_config_new = render_to_string('frr/daemons.frr.tmpl', frr_config) +    # update configuration file if this is necessary +    if daemons_config_new != daemons_config_current: +        syslog.warning('FRR daemons configuration file need to be changed') +        write_file(config_file, daemons_config_new) +        frr_config['config_file_changed'] = True + + +def apply(frr_config): +    # check if this is initial commit during boot or intiated by CLI +    # if the file exists, this must be CLI commit +    commit_type_cli = Path(vyos_status_file).exists() +    # display warning to user +    if commit_type_cli and frr_config.get('config_file_changed'): +        # Since FRR restart is not safe thing, better to give +        # control over this to users +        print(''' +        You need to reboot a router (preferred) or restart FRR +        to apply changes in modules settings +        ''') +    # restart FRR automatically. DUring the initial boot this should be +    # safe in most cases +    if not commit_type_cli and frr_config.get('config_file_changed'): +        syslog.warning('Restarting FRR to apply changes in modules') +        run(f'{watchfrr} restart') + + +if __name__ == '__main__': +    try: +        c = get_config() +        verify(c) +        generate(c) +        apply(c) +    except ConfigError as e: +        print(e) +        exit(1) | 
