diff options
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-x | src/conf_mode/policy.py | 18 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bfd.py | 16 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bgp.py | 12 | ||||
-rwxr-xr-x | src/conf_mode/protocols_isis.py | 24 | ||||
-rwxr-xr-x | src/conf_mode/protocols_ospf.py | 22 | ||||
-rwxr-xr-x | src/conf_mode/protocols_ospfv3.py | 34 | ||||
-rwxr-xr-x | src/conf_mode/protocols_rip.py | 30 | ||||
-rwxr-xr-x | src/conf_mode/protocols_ripng.py | 24 | ||||
-rwxr-xr-x | src/conf_mode/protocols_rpki.py | 20 | ||||
-rwxr-xr-x | src/conf_mode/protocols_static.py | 14 | ||||
-rwxr-xr-x | src/conf_mode/vrf_vni.py | 5 |
11 files changed, 130 insertions, 89 deletions
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py index 1a03d520b..5f9fcab9f 100755 --- a/src/conf_mode/policy.py +++ b/src/conf_mode/policy.py @@ -171,9 +171,7 @@ def verify(policy): def generate(policy): if not policy: - policy['new_frr_config'] = '' return None - policy['new_frr_config'] = render_to_string('frr/policy.frr.tmpl', policy) return None @@ -190,8 +188,9 @@ def apply(policy): frr_cfg.modify_section(r'^bgp community-list .*') frr_cfg.modify_section(r'^bgp extcommunity-list .*') frr_cfg.modify_section(r'^bgp large-community-list .*') - frr_cfg.modify_section(r'^route-map .*') - frr_cfg.add_before('^line vty', policy['new_frr_config']) + frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True) + if 'new_frr_config' in policy: + frr_cfg.add_before(frr.default_add_before, policy['new_frr_config']) frr_cfg.commit_configuration(bgp_daemon) # The route-map used for the FIB (zebra) is part of the zebra daemon @@ -200,16 +199,11 @@ def apply(policy): frr_cfg.modify_section(r'^ipv6 access-list .*') frr_cfg.modify_section(r'^ip prefix-list .*') frr_cfg.modify_section(r'^ipv6 prefix-list .*') - frr_cfg.modify_section(r'^route-map .*') - frr_cfg.add_before('^line vty', policy['new_frr_config']) + frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True) + if 'new_frr_config' in policy: + frr_cfg.add_before(frr.default_add_before, policy['new_frr_config']) frr_cfg.commit_configuration(zebra_daemon) - # If FRR config is blank, rerun the blank commit x times due to frr-reload - # behavior/bug not properly clearing out on one commit. - if policy['new_frr_config'] == '': - for a in range(5): - frr_cfg.commit_configuration(zebra_daemon) - # Save configuration to /run/frr/config/frr.conf frr.save_configuration() diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 539fd7b8e..94825ba10 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -16,8 +16,6 @@ import os -from sys import exit - from vyos.config import Config from vyos.configdict import dict_merge from vyos.template import is_ipv6 @@ -36,7 +34,6 @@ def get_config(config=None): conf = Config() base = ['protocols', 'bfd'] bfd = conf.get_config_dict(base, get_first_key=True) - # Bail out early if configuration tree does not exist if not conf.exists(base): return bfd @@ -89,18 +86,19 @@ def verify(bfd): def generate(bfd): if not bfd: - bfd['new_frr_config'] = '' return None - bfd['new_frr_config'] = render_to_string('frr/bfdd.frr.tmpl', bfd) def apply(bfd): + bfd_daemon = 'bfdd' + # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration() - frr_cfg.modify_section('^bfd', '') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config']) - frr_cfg.commit_configuration() + frr_cfg.load_configuration(bfd_daemon) + frr_cfg.modify_section('^bfd', stop_pattern='^exit', remove_stop_mark=True) + if 'new_frr_config' in bfd: + frr_cfg.add_before(frr.default_add_before, bfd['new_frr_config']) + frr_cfg.commit_configuration(bfd_daemon) return None diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 68284e0f9..359bfff76 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -268,8 +268,6 @@ def verify(bgp): def generate(bgp): if not bgp or 'deleted' in bgp: - bgp['frr_bgpd_config'] = '' - bgp['frr_zebra_config'] = '' return None bgp['protocol'] = 'bgp' # required for frr/vrf.route-map.frr.tmpl @@ -287,8 +285,9 @@ def apply(bgp): # The route-map used for the FIB (zebra) is part of the zebra daemon frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'(\s+)?ip protocol bgp route-map [-a-zA-Z0-9.]+$', '', '(\s|!)') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['frr_zebra_config']) + frr_cfg.modify_section(r'(\s+)?ip protocol bgp route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + if 'frr_zebra_config' in bgp: + frr_cfg.add_before(frr.default_add_before, bgp['frr_zebra_config']) frr_cfg.commit_configuration(zebra_daemon) # Generate empty helper string which can be ammended to FRR commands, it @@ -298,8 +297,9 @@ def apply(bgp): vrf = ' vrf ' + bgp['vrf'] frr_cfg.load_configuration(bgp_daemon) - frr_cfg.modify_section(f'^router bgp \d+{vrf}$', '') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['frr_bgpd_config']) + frr_cfg.modify_section(f'^router bgp \d+{vrf}', stop_pattern='^exit', remove_stop_mark=True) + if 'frr_bgpd_config' in bgp: + frr_cfg.add_before(frr.default_add_before, bgp['frr_bgpd_config']) frr_cfg.commit_configuration(bgp_daemon) # Save configuration to /run/frr/config/frr.conf diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index 4505e2496..0011e6fbf 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -56,10 +56,10 @@ def get_config(config=None): # instead of the VRF instance. if vrf: isis['vrf'] = vrf - # As we no re-use this Python handler for both VRF and non VRF instances for - # IS-IS we need to find out if any interfaces changed so properly adjust - # the FRR configuration and not by acctident change interfaces from a - # different VRF. + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. interfaces_removed = node_changed(conf, base + ['interface']) if interfaces_removed: isis['interface_removed'] = list(interfaces_removed) @@ -196,8 +196,6 @@ def verify(isis): def generate(isis): if not isis or 'deleted' in isis: - isis['frr_isisd_config'] = '' - isis['frr_zebra_config'] = '' return None isis['protocol'] = 'isis' # required for frr/vrf.route-map.frr.tmpl @@ -214,8 +212,9 @@ def apply(isis): # The route-map used for the FIB (zebra) is part of the zebra daemon frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'(\s+)?ip protocol isis route-map [-a-zA-Z0-9.]+$', '', '(\s|!)') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['frr_zebra_config']) + frr_cfg.modify_section('(\s+)?ip protocol isis route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + if 'frr_zebra_config' in isis: + frr_cfg.add_before(frr.default_add_before, isis['frr_zebra_config']) frr_cfg.commit_configuration(zebra_daemon) # Generate empty helper string which can be ammended to FRR commands, it @@ -225,17 +224,18 @@ def apply(isis): vrf = ' vrf ' + isis['vrf'] frr_cfg.load_configuration(isis_daemon) - frr_cfg.modify_section(f'^router isis VyOS{vrf}$', '') + frr_cfg.modify_section(f'^router isis VyOS{vrf}', stop_pattern='^exit', remove_stop_mark=True) for key in ['interface', 'interface_removed']: if key not in isis: continue for interface in isis[key]: - frr_cfg.modify_section(f'^interface {interface}{vrf}$', '') + frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True) - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', isis['frr_isisd_config']) - frr_cfg.commit_configuration(isis_daemon) + if 'frr_isisd_config' in isis: + frr_cfg.add_before(frr.default_add_before, isis['frr_isisd_config']) + frr_cfg.commit_configuration(isis_daemon) # Save configuration to /run/frr/config/frr.conf frr.save_configuration() diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 6ccda2e5a..255560e19 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -56,10 +56,10 @@ def get_config(config=None): # instead of the VRF instance. if vrf: ospf['vrf'] = vrf - # As we no re-use this Python handler for both VRF and non VRF instances for - # OSPF we need to find out if any interfaces changed so properly adjust - # the FRR configuration and not by acctident change interfaces from a - # different VRF. + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. interfaces_removed = node_changed(conf, base + ['interface']) if interfaces_removed: ospf['interface_removed'] = list(interfaces_removed) @@ -191,8 +191,6 @@ def verify(ospf): def generate(ospf): if not ospf or 'deleted' in ospf: - ospf['frr_ospfd_config'] = '' - ospf['frr_zebra_config'] = '' return None ospf['protocol'] = 'ospf' # required for frr/vrf.route-map.frr.tmpl @@ -209,8 +207,9 @@ def apply(ospf): # The route-map used for the FIB (zebra) is part of the zebra daemon frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'(\s+)?ip protocol ospf route-map [-a-zA-Z0-9.]+$', '', '(\s|!)') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['frr_zebra_config']) + frr_cfg.modify_section('(\s+)?ip protocol ospf route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + if 'frr_zebra_config' in ospf: + frr_cfg.add_before(frr.default_add_before, ospf['frr_zebra_config']) frr_cfg.commit_configuration(zebra_daemon) # Generate empty helper string which can be ammended to FRR commands, it @@ -220,15 +219,16 @@ def apply(ospf): vrf = ' vrf ' + ospf['vrf'] frr_cfg.load_configuration(ospf_daemon) - frr_cfg.modify_section(f'^router ospf{vrf}$', '') + frr_cfg.modify_section(f'^router ospf{vrf}', stop_pattern='^exit', remove_stop_mark=True) for key in ['interface', 'interface_removed']: if key not in ospf: continue for interface in ospf[key]: - frr_cfg.modify_section(f'^interface {interface}{vrf}$', '') + frr_cfg.modify_section(f'^interface {interface}{vrf}', stop_pattern='^exit', remove_stop_mark=True) - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['frr_ospfd_config']) + if 'frr_ospfd_config' in ospf: + frr_cfg.add_before(frr.default_add_before, ospf['frr_ospfd_config']) frr_cfg.commit_configuration(ospf_daemon) # Save configuration to /run/frr/config/frr.conf diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index 536ffa690..5d6ca7169 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -20,6 +20,7 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configdict import node_changed from vyos.configverify import verify_common_route_maps from vyos.template import render_to_string from vyos.ifconfig import Interface @@ -29,8 +30,6 @@ from vyos import frr from vyos import airbag airbag.enable() -frr_daemon = 'ospf6d' - def get_config(config=None): if config: conf = config @@ -39,8 +38,17 @@ def get_config(config=None): base = ['protocols', 'ospfv3'] ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + ospfv3['interface_removed'] = list(interfaces_removed) + # Bail out early if configuration tree does not exist if not conf.exists(base): + ospfv3.update({'deleted' : ''}) return ospfv3 # We also need some additional information from the config, prefix-lists @@ -70,22 +78,30 @@ def verify(ospfv3): return None def generate(ospfv3): - if not ospfv3: - ospfv3['new_frr_config'] = '' + if not ospfv3 or 'deleted' in ospfv3: return None ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.tmpl', ospfv3) return None def apply(ospfv3): + ospf6_daemon = 'ospf6d' + # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(r'^interface \S+', '') - frr_cfg.modify_section('^router ospf6$', '') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config']) - frr_cfg.commit_configuration(frr_daemon) + frr_cfg.load_configuration(ospf6_daemon) + frr_cfg.modify_section('^router ospf6', stop_pattern='^exit', remove_stop_mark=True) + + for key in ['interface', 'interface_removed']: + if key not in ospfv3: + continue + for interface in ospfv3[key]: + frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) + + if 'new_frr_config' in ospfv3: + frr_cfg.add_before(frr.default_add_before, ospfv3['new_frr_config']) + frr_cfg.commit_configuration(ospf6_daemon) # Save configuration to /run/frr/config/frr.conf frr.save_configuration() diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py index 6b78f6f2d..96df41bdb 100755 --- a/src/conf_mode/protocols_rip.py +++ b/src/conf_mode/protocols_rip.py @@ -20,6 +20,7 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configdict import node_changed from vyos.configverify import verify_common_route_maps from vyos.configverify import verify_access_list from vyos.configverify import verify_prefix_list @@ -39,8 +40,17 @@ def get_config(config=None): base = ['protocols', 'rip'] rip = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + rip['interface_removed'] = list(interfaces_removed) + # Bail out early if configuration tree does not exist if not conf.exists(base): + rip.update({'deleted' : ''}) return rip # We have gathered the dict representation of the CLI, but there are default @@ -89,12 +99,10 @@ def verify(rip): f'with "split-horizon disable" for "{interface}"!') def generate(rip): - if not rip: - rip['new_frr_config'] = '' + if not rip or 'deleted' in rip: return None rip['new_frr_config'] = render_to_string('frr/ripd.frr.tmpl', rip) - return None def apply(rip): @@ -106,15 +114,21 @@ def apply(rip): # The route-map used for the FIB (zebra) is part of the zebra daemon frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'^ip protocol rip route-map [-a-zA-Z0-9.]+$', '') + frr_cfg.modify_section('^ip protocol rip route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') frr_cfg.commit_configuration(zebra_daemon) frr_cfg.load_configuration(rip_daemon) - frr_cfg.modify_section(r'key chain \S+', '') - frr_cfg.modify_section(r'interface \S+', '') - frr_cfg.modify_section('^router rip$', '') + frr_cfg.modify_section('^key chain \S+', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section('^router rip', stop_pattern='^exit', remove_stop_mark=True) + + for key in ['interface', 'interface_removed']: + if key not in rip: + continue + for interface in rip[key]: + frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rip['new_frr_config']) + if 'new_frr_config' in rip: + frr_cfg.add_before(frr.default_add_before, rip['new_frr_config']) frr_cfg.commit_configuration(rip_daemon) # Save configuration to /run/frr/config/frr.conf diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py index bc4954f63..d46a2216c 100755 --- a/src/conf_mode/protocols_ripng.py +++ b/src/conf_mode/protocols_ripng.py @@ -31,8 +31,6 @@ from vyos import frr from vyos import airbag airbag.enable() -frr_daemon = 'ripngd' - def get_config(config=None): if config: conf = config @@ -99,14 +97,24 @@ def generate(ripng): return None def apply(ripng): + ripng_daemon = 'ripngd' + zebra_daemon = 'zebra' + # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(r'key chain \S+', '') - frr_cfg.modify_section(r'interface \S+', '') - frr_cfg.modify_section('router ripng', '') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ripng['new_frr_config']) - frr_cfg.commit_configuration(frr_daemon) + + # The route-map used for the FIB (zebra) is part of the zebra daemon + frr_cfg.load_configuration(zebra_daemon) + frr_cfg.modify_section('^ipv6 protocol ripng route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + frr_cfg.commit_configuration(zebra_daemon) + + frr_cfg.load_configuration(ripng_daemon) + frr_cfg.modify_section('key chain \S+', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section('interface \S+', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section('^router ripng', stop_pattern='^exit', remove_stop_mark=True) + if 'new_frr_config' in ripng: + frr_cfg.add_before(frr.default_add_before, ripng['new_frr_config']) + frr_cfg.commit_configuration(ripng_daemon) # Save configuration to /run/frr/config/frr.conf frr.save_configuration() diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py index 947c8ab7a..dadd8d6f4 100755 --- a/src/conf_mode/protocols_rpki.py +++ b/src/conf_mode/protocols_rpki.py @@ -28,8 +28,6 @@ from vyos import frr from vyos import airbag airbag.enable() -frr_daemon = 'bgpd' - def get_config(config=None): if config: conf = config @@ -38,7 +36,9 @@ def get_config(config=None): base = ['protocols', 'rpki'] rpki = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # Bail out early if configuration tree does not exist if not conf.exists(base): + rpki.update({'deleted' : ''}) return rpki # We have gathered the dict representation of the CLI, but there are default @@ -79,16 +79,24 @@ def verify(rpki): return None def generate(rpki): + if not rpki: + return rpki['new_frr_config'] = render_to_string('frr/rpki.frr.tmpl', rpki) return None def apply(rpki): + bgp_daemon = 'bgpd' + # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section('rpki', '') - frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rpki['new_frr_config']) - frr_cfg.commit_configuration(frr_daemon) + frr_cfg.load_configuration(bgp_daemon) + frr_cfg.modify_section('^rpki') + if 'new_frr_config' in rpki: + frr_cfg.add_before(frr.default_add_before, rpki['new_frr_config']) + + frr_cfg.commit_configuration(bgp_daemon) + # Save configuration to /run/frr/config/frr.conf + frr.save_configuration() return None diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index f010141e9..5cfe37655 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -85,6 +85,8 @@ def verify(static): return None def generate(static): + if not static: + return None static['new_frr_config'] = render_to_string('frr/staticd.frr.tmpl', static) return None @@ -97,19 +99,19 @@ def apply(static): # The route-map used for the FIB (zebra) is part of the zebra daemon frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'^ip protocol static route-map [-a-zA-Z0-9.]+$', '') + frr_cfg.modify_section(r'^ip protocol static route-map [-a-zA-Z0-9.]+', '') frr_cfg.commit_configuration(zebra_daemon) - frr_cfg.load_configuration(static_daemon) if 'vrf' in static: vrf = static['vrf'] - frr_cfg.modify_section(f'^vrf {vrf}$', '') + frr_cfg.modify_section(f'^vrf {vrf}', stop_pattern='^exit', remove_stop_mark=True) else: - frr_cfg.modify_section(r'^ip route .*', '') - frr_cfg.modify_section(r'^ipv6 route .*', '') + frr_cfg.modify_section(r'^ip route .*') + frr_cfg.modify_section(r'^ipv6 route .*') - frr_cfg.add_before(r'(interface .*|line vty)', static['new_frr_config']) + if 'new_frr_config' in static: + frr_cfg.add_before(frr.default_add_before, static['new_frr_config']) frr_cfg.commit_configuration(static_daemon) # Save configuration to /run/frr/config/frr.conf diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py index 50d60f0dc..a357f30dd 100755 --- a/src/conf_mode/vrf_vni.py +++ b/src/conf_mode/vrf_vni.py @@ -47,8 +47,9 @@ def apply(vrf): # add configuration to FRR frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(f'^vrf .+$', '') - frr_cfg.add_before(r'(interface .*|line vty)', vrf['new_frr_config']) + frr_cfg.modify_section(f'^vrf .+', '') + if 'new_frr_config' in vrf: + frr_cfg.add_before(frr.default_add_before, vrf['new_frr_config']) frr_cfg.commit_configuration(frr_daemon) # Save configuration to /run/frr/config/frr.conf |