summaryrefslogtreecommitdiff
path: root/src/conf_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode')
-rwxr-xr-xsrc/conf_mode/interfaces_bonding.py34
-rwxr-xr-xsrc/conf_mode/interfaces_ethernet.py32
-rwxr-xr-xsrc/conf_mode/policy.py129
-rwxr-xr-xsrc/conf_mode/protocols_babel.py79
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py40
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py136
-rwxr-xr-xsrc/conf_mode/protocols_eigrp.py81
-rwxr-xr-xsrc/conf_mode/protocols_isis.py142
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py42
-rw-r--r--src/conf_mode/protocols_openfabric.py63
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py133
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py124
-rwxr-xr-xsrc/conf_mode/protocols_rip.py78
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py63
-rwxr-xr-xsrc/conf_mode/protocols_rpki.py49
-rwxr-xr-xsrc/conf_mode/protocols_segment-routing.py92
-rwxr-xr-xsrc/conf_mode/protocols_static.py82
-rwxr-xr-xsrc/conf_mode/protocols_static_multicast.py133
-rwxr-xr-xsrc/conf_mode/service_snmp.py11
-rwxr-xr-xsrc/conf_mode/vrf.py23
20 files changed, 417 insertions, 1149 deletions
diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py
index 633fb797c..adea8fc63 100755
--- a/src/conf_mode/interfaces_bonding.py
+++ b/src/conf_mode/interfaces_bonding.py
@@ -17,6 +17,7 @@
from sys import exit
from vyos.config import Config
+from vyos.configdict import get_frrender_dict
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
from vyos.configdict import leaf_node_changed
@@ -30,10 +31,10 @@ from vyos.configverify import verify_mirror_redirect
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
+from vyos.frrender import FRRender
from vyos.ifconfig import BondIf
from vyos.ifconfig.ethernet import EthernetIf
from vyos.ifconfig import Section
-from vyos.template import render_to_string
from vyos.utils.assertion import assert_mac
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_to_paths_values
@@ -42,9 +43,9 @@ from vyos.configdict import has_address_configured
from vyos.configdict import has_vrf_configured
from vyos.configdep import set_dependents, call_dependents
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
def get_bond_mode(mode):
if mode == 'round-robin':
@@ -87,10 +88,13 @@ def get_config(config=None):
bond['mode'] = get_bond_mode(bond['mode'])
tmp = is_node_changed(conf, base + [ifname, 'mode'])
- if tmp: bond['shutdown_required'] = {}
+ if tmp: bond.update({'shutdown_required' : {}})
tmp = is_node_changed(conf, base + [ifname, 'lacp-rate'])
- if tmp: bond['shutdown_required'] = {}
+ if tmp: bond.update({'shutdown_required' : {}})
+
+ tmp = is_node_changed(conf, base + [ifname, 'evpn'])
+ if tmp: bond.update({'frrender' : get_frrender_dict(conf)})
# determine which members have been removed
interfaces_removed = leaf_node_changed(conf, base + [ifname, 'member', 'interface'])
@@ -260,16 +264,16 @@ def verify(bond):
return None
def generate(bond):
- bond['frr_zebra_config'] = ''
- if 'deleted' not in bond:
- bond['frr_zebra_config'] = render_to_string('frr/evpn.mh.frr.j2', bond)
+ if 'frrender' in bond:
+ frrender.generate(bond['frrender'])
return None
def apply(bond):
- ifname = bond['ifname']
- b = BondIf(ifname)
+ if 'frrender' in bond:
+ frrender.apply()
+
+ b = BondIf(bond['ifname'])
if 'deleted' in bond:
- # delete interface
b.remove()
else:
b.update(bond)
@@ -281,16 +285,6 @@ def apply(bond):
raise ConfigError('Error in updating ethernet interface '
'after deleting it from bond')
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(frr.mgmt_daemon)
- frr_cfg.modify_section(f'^interface {ifname}', stop_pattern='^exit', remove_stop_mark=True)
- if 'frr_zebra_config' in bond:
- frr_cfg.add_before(frr.default_add_before, bond['frr_zebra_config'])
- frr_cfg.commit_configuration()
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index edbbb00c9..6a035e9e9 100755
--- a/src/conf_mode/interfaces_ethernet.py
+++ b/src/conf_mode/interfaces_ethernet.py
@@ -20,6 +20,7 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdict import get_frrender_dict
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
from vyos.configverify import verify_address
@@ -33,17 +34,17 @@ from vyos.configverify import verify_vrf
from vyos.configverify import verify_bond_bridge_member
from vyos.configverify import verify_eapol
from vyos.ethtool import Ethtool
+from vyos.frrender import FRRender
from vyos.ifconfig import EthernetIf
from vyos.ifconfig import BondIf
-from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_to_paths_values
from vyos.utils.dict import dict_set
from vyos.utils.dict import dict_delete
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
def update_bond_options(conf: Config, eth_conf: dict) -> list:
"""
@@ -164,6 +165,9 @@ def get_config(config=None):
tmp = is_node_changed(conf, base + [ifname, 'duplex'])
if tmp: ethernet.update({'speed_duplex_changed': {}})
+ tmp = is_node_changed(conf, base + [ifname, 'evpn'])
+ if tmp: ethernet.update({'frrender' : get_frrender_dict(conf)})
+
return ethernet
def verify_speed_duplex(ethernet: dict, ethtool: Ethtool):
@@ -318,32 +322,20 @@ def verify_ethernet(ethernet):
return None
def generate(ethernet):
- if 'deleted' in ethernet:
- return None
-
- ethernet['frr_zebra_config'] = ''
- if 'deleted' not in ethernet:
- ethernet['frr_zebra_config'] = render_to_string('frr/evpn.mh.frr.j2', ethernet)
-
+ if 'frrender' in ethernet:
+ frrender.generate(ethernet['frrender'])
return None
def apply(ethernet):
- ifname = ethernet['ifname']
+ if 'frrender' in ethernet:
+ frrender.apply()
- e = EthernetIf(ifname)
+ e = EthernetIf(ethernet['ifname'])
if 'deleted' in ethernet:
- # delete interface
e.remove()
else:
e.update(ethernet)
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.mgmt_daemon)
- frr_cfg.modify_section(f'^interface {ifname}', stop_pattern='^exit', remove_stop_mark=True)
- if 'frr_zebra_config' in ethernet:
- frr_cfg.add_before(frr.default_add_before, ethernet['frr_zebra_config'])
- frr_cfg.commit_configuration()
+ return None
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index aef9b96c4..e6b6d474a 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 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
@@ -17,15 +17,16 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.template import render_to_string
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
+from vyos.frrender import frr_protocols
from vyos.utils.dict import dict_search
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
-
airbag.enable()
+frrender = FRRender()
def community_action_compatibility(actions: dict) -> bool:
"""
@@ -87,31 +88,27 @@ def get_config(config=None):
else:
conf = Config()
- base = ['policy']
- policy = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['protocols'], key_mangling=('-', '_'),
- no_tag_node_value_mangle=True)
- # Merge policy dict into "regular" config dict
- policy = dict_merge(tmp, policy)
- return policy
-
-
-def verify(policy):
- if not policy:
+ return get_frrender_dict(conf)
+
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'policy'):
return None
- for policy_type in ['access_list', 'access_list6', 'as_path_list',
- 'community_list', 'extcommunity_list',
- 'large_community_list',
- 'prefix_list', 'prefix_list6', 'route_map']:
+ policy_types = ['access_list', 'access_list6', 'as_path_list',
+ 'community_list', 'extcommunity_list',
+ 'large_community_list', 'prefix_list',
+ 'prefix_list6', 'route_map']
+
+ policy = config_dict['policy']
+ for protocol in frr_protocols:
+ if protocol not in config_dict:
+ continue
+ if 'protocol' not in policy:
+ policy.update({'protocol': {}})
+ policy['protocol'].update({protocol : config_dict[protocol]})
+
+ for policy_type in policy_types:
# Bail out early and continue with next policy type
if policy_type not in policy:
continue
@@ -246,69 +243,33 @@ def verify(policy):
# When the "routing policy" changes and policies, route-maps etc. are deleted,
# it is our responsibility to verify that the policy can not be deleted if it
# is used by any routing protocol
- if 'protocols' in policy:
- for policy_type in ['access_list', 'access_list6', 'as_path_list',
- 'community_list',
- 'extcommunity_list', 'large_community_list',
- 'prefix_list', 'route_map']:
- if policy_type in policy:
- for policy_name in list(set(routing_policy_find(policy_type,
- policy[
- 'protocols']))):
- found = False
- if policy_name in policy[policy_type]:
- found = True
- # BGP uses prefix-list for selecting both an IPv4 or IPv6 AFI related
- # list - we need to go the extra mile here and check both prefix-lists
- if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in \
- policy['prefix_list6']:
- found = True
- if not found:
- tmp = policy_type.replace('_', '-')
- raise ConfigError(
- f'Can not delete {tmp} "{policy_name}", still in use!')
-
- return None
-
+ # Check if any routing protocol is activated
+ if 'protocol' in policy:
+ for policy_type in policy_types:
+ for policy_name in list(set(routing_policy_find(policy_type, policy['protocol']))):
+ found = False
+ if policy_type in policy and policy_name in policy[policy_type]:
+ found = True
+ # BGP uses prefix-list for selecting both an IPv4 or IPv6 AFI related
+ # list - we need to go the extra mile here and check both prefix-lists
+ if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in \
+ policy['prefix_list6']:
+ found = True
+ if not found:
+ tmp = policy_type.replace('_', '-')
+ raise ConfigError(
+ f'Can not delete {tmp} "{policy_name}", still in use!')
-def generate(policy):
- if not policy:
- return None
- policy['new_frr_config'] = render_to_string('frr/policy.frr.j2', policy)
return None
-def apply(policy):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(frr.bgp_daemon)
- frr_cfg.modify_section(r'^bgp as-path access-list .*')
- 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 .*', 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(frr.bgp_daemon)
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(frr.zebra_daemon)
- frr_cfg.modify_section(r'^access-list .*')
- 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 .*', 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(frr.zebra_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
-
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/protocols_babel.py b/src/conf_mode/protocols_babel.py
index 06fd9b9b6..f9d5e45a0 100755
--- a/src/conf_mode/protocols_babel.py
+++ b/src/conf_mode/protocols_babel.py
@@ -17,63 +17,33 @@
from sys import exit
from vyos.config import Config
-from vyos.config import config_dict_merge
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'babel']
- babel = 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:
- babel['interface_removed'] = list(interfaces_removed)
-
- # Bail out early if configuration tree does not exist
- if not conf.exists(base):
- babel.update({'deleted' : ''})
- return babel
-
- # We have gathered the dict representation of the CLI, but there are default
- # values which we need to update into the dictionary retrieved.
- default_values = conf.get_config_defaults(base, key_mangling=('-', '_'),
- get_first_key=True,
- recursive=True)
- # merge in default values
- babel = config_dict_merge(default_values, babel)
+ return get_frrender_dict(conf)
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- babel = dict_merge(tmp, babel)
- return babel
-
-def verify(babel):
- if not babel:
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'babel'):
return None
+ babel = config_dict['babel']
+ babel['policy'] = config_dict['policy']
+
# verify distribute_list
if "distribute_list" in babel:
acl_keys = {
@@ -120,30 +90,11 @@ def verify(babel):
verify_prefix_list(prefix_list, babel, version='6' if address_family == 'ipv6' else '')
-def generate(babel):
- if not babel or 'deleted' in babel:
- return None
-
- babel['new_frr_config'] = render_to_string('frr/babeld.frr.j2', babel)
- return None
-
-def apply(babel):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(frr.babel_daemon)
- frr_cfg.modify_section('^router babel', stop_pattern='^exit', remove_stop_mark=True)
-
- for key in ['interface', 'interface_removed']:
- if key not in babel:
- continue
- for interface in babel[key]:
- frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'new_frr_config' in babel:
- frr_cfg.add_before(frr.default_add_before, babel['new_frr_config'])
- frr_cfg.commit_configuration(frr.babel_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index d94ec6a0d..a0d7fdfb5 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -15,36 +15,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from vyos.config import Config
+from vyos.configdict import get_frrender_dict
from vyos.configverify import verify_vrf
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos.template import is_ipv6
-from vyos.template import render_to_string
from vyos.utils.network import is_ipv6_link_local
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'bfd']
- bfd = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
- # Bail out early if configuration tree does not exist
- if not conf.exists(base):
- return bfd
-
- bfd = conf.merge_defaults(bfd, recursive=True)
- return bfd
+ return get_frrender_dict(conf)
-def verify(bfd):
- if not bfd:
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'bfd'):
return None
+ bfd = config_dict['bfd']
if 'peer' in bfd:
for peer, peer_config in bfd['peer'].items():
# IPv6 link local peers require an explicit local address/interface
@@ -83,20 +78,11 @@ def verify(bfd):
return None
-def generate(bfd):
- if not bfd:
- return None
- bfd['new_frr_config'] = render_to_string('frr/bfdd.frr.j2', bfd)
-
-def apply(bfd):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.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(frr.bfd_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index e5c46aee6..989cf9b5c 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -19,92 +19,35 @@ from sys import argv
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_prefix_list
from vyos.configverify import verify_route_map
from vyos.configverify import verify_vrf
+from vyos.frrender import FRRender
from vyos.template import is_ip
from vyos.template import is_interface
-from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.network import get_interface_vrf
from vyos.utils.network import is_addr_assigned
from vyos.utils.process import process_named_running
-from vyos.utils.process import call
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
-
- base_path = ['protocols', 'bgp']
-
- # eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'bgp'] or base_path
- bgp = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'],
- key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # Remove per interface MPLS configuration - get a list if changed
- # nodes under the interface tagNode
- interfaces_removed = node_changed(conf, base + ['interface'])
- if interfaces_removed:
- bgp['interface_removed'] = list(interfaces_removed)
-
- # Assign the name of our VRF context. This MUST be done before the return
- # statement below, else on deletion we will delete the default instance
- # instead of the VRF instance.
- if vrf:
- bgp.update({'vrf' : vrf})
- # We can not delete the BGP VRF instance if there is a L3VNI configured
- # FRR L3VNI must be deleted first otherwise we will see error:
- # "FRR error: Please unconfigure l3vni 3000"
- tmp = ['vrf', 'name', vrf, 'vni']
- if conf.exists_effective(tmp):
- bgp.update({'vni' : conf.return_effective_value(tmp)})
- # We can safely delete ourself from the dependent vrf list
- if vrf in bgp['dependent_vrfs']:
- del bgp['dependent_vrfs'][vrf]
-
- bgp['dependent_vrfs'].update({'default': {'protocols': {
- 'bgp': conf.get_config_dict(base_path, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)}}})
-
- if not conf.exists(base):
- # If bgp instance is deleted then mark it
- bgp.update({'deleted' : ''})
- return bgp
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- bgp = conf.merge_defaults(bgp, recursive=True)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- bgp = dict_merge(tmp, bgp)
-
- return bgp
-
+ return get_frrender_dict(conf)
def verify_vrf_as_import(search_vrf_name: str, afi_name: str, vrfs_config: dict) -> bool:
"""
@@ -237,7 +180,18 @@ def verify_afi(peer_config, bgp_config):
if tmp: return True
return False
-def verify(bgp):
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'bgp', vrf):
+ return None
+
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ bgp = vrf and config_dict['vrf']['name'][vrf]['protocols']['bgp'] or config_dict['bgp']
+ bgp['policy'] = config_dict['policy']
+
+ if vrf:
+ bgp['vrf'] = vrf
+
if 'deleted' in bgp:
if 'vrf' in bgp:
# Cannot delete vrf if it exists in import vrf list in other vrfs
@@ -252,8 +206,9 @@ def verify(bgp):
for vrf, vrf_options in bgp['dependent_vrfs'].items():
if vrf != 'default':
if dict_search('protocols.bgp', vrf_options):
- raise ConfigError('Cannot delete default BGP instance, ' \
- 'dependent VRF instance(s) exist(s)!')
+ dependent_vrfs = ', '.join(bgp['dependent_vrfs'].keys())
+ raise ConfigError(f'Cannot delete default BGP instance, ' \
+ f'dependent VRF instance(s): {dependent_vrfs}')
if 'vni' in vrf_options:
raise ConfigError('Cannot delete default BGP instance, ' \
'dependent L3VNI exists!')
@@ -602,44 +557,11 @@ def verify(bgp):
return None
-def generate(bgp):
- if not bgp or 'deleted' in bgp:
- return None
-
- bgp['frr_bgpd_config'] = render_to_string('frr/bgpd.frr.j2', bgp)
- return None
-
-def apply(bgp):
- if 'deleted' in bgp:
- # We need to ensure that the L3VNI is deleted first.
- # This is not possible with old config backend
- # priority bug
- if {'vrf', 'vni'} <= set(bgp):
- call('vtysh -c "conf t" -c "vrf {vrf}" -c "no vni {vni}"'.format(**bgp))
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # Generate empty helper string which can be ammended to FRR commands, it
- # will be either empty (default VRF) or contain the "vrf <name" statement
- vrf = ''
- if 'vrf' in bgp:
- vrf = ' vrf ' + bgp['vrf']
-
- frr_cfg.load_configuration(frr.bgp_daemon)
-
- # Remove interface specific config
- for key in ['interface', 'interface_removed']:
- if key not in bgp:
- continue
- for interface in bgp[key]:
- frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
-
- 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(frr.bgp_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_eigrp.py b/src/conf_mode/protocols_eigrp.py
index a948b47da..5d60e6bfd 100755
--- a/src/conf_mode/protocols_eigrp.py
+++ b/src/conf_mode/protocols_eigrp.py
@@ -18,14 +18,18 @@ from sys import exit
from sys import argv
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_vrf
-from vyos.template import render_to_string
+from vyos.frrender import FRRender
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
def get_config(config=None):
if config:
@@ -33,48 +37,16 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf)
- base_path = ['protocols', 'eigrp']
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'eigrp', vrf):
+ return None
# eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'eigrp'] or base_path
- eigrp = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- # Assign the name of our VRF context. This MUST be done before the return
- # statement below, else on deletion we will delete the default instance
- # instead of the VRF instance.
- if vrf: eigrp.update({'vrf' : vrf})
-
- if not conf.exists(base):
- eigrp.update({'deleted' : ''})
- if not vrf:
- # We are running in the default VRF context, thus we can not delete
- # our main EIGRP instance if there are dependent EIGRP VRF instances.
- eigrp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'],
- key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- return eigrp
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- eigrp = dict_merge(tmp, eigrp)
-
- return eigrp
-
-def verify(eigrp):
- if not eigrp or 'deleted' in eigrp:
- return
+ eigrp = vrf and config_dict['vrf']['name'][vrf]['protocols']['eigrp'] or config_dict['eigrp']
+ eigrp['policy'] = config_dict['policy']
if 'system_as' not in eigrp:
raise ConfigError('EIGRP system-as must be defined!')
@@ -82,28 +54,11 @@ def verify(eigrp):
if 'vrf' in eigrp:
verify_vrf(eigrp)
-def generate(eigrp):
- if not eigrp or 'deleted' in eigrp:
- return None
-
- eigrp['frr_eigrpd_config'] = render_to_string('frr/eigrpd.frr.j2', eigrp)
-
-def apply(eigrp):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # Generate empty helper string which can be ammended to FRR commands, it
- # will be either empty (default VRF) or contain the "vrf <name" statement
- vrf = ''
- if 'vrf' in eigrp:
- vrf = ' vrf ' + eigrp['vrf']
-
- frr_cfg.load_configuration(frr.eigrp_daemon)
- frr_cfg.modify_section(f'^router eigrp \d+{vrf}', stop_pattern='^exit', remove_stop_mark=True)
- if 'frr_eigrpd_config' in eigrp:
- frr_cfg.add_before(frr.default_add_before, eigrp['frr_eigrpd_config'])
- frr_cfg.commit_configuration(frr.eigrp_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 9b70c7329..e812770bc 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -18,75 +18,89 @@ from sys import exit
from sys import argv
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_interface_exists
+from vyos.frrender import FRRender
from vyos.ifconfig import Interface
from vyos.utils.dict import dict_search
from vyos.utils.network import get_interface_config
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf)
- base_path = ['protocols', 'isis']
- # eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'isis'] or base_path
- isis = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # Assign the name of our VRF context. This MUST be done before the return
- # statement below, else on deletion we will delete the default instance
- # instead of the VRF instance.
- if vrf: isis['vrf'] = 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)
-
- # Bail out early if configuration tree does no longer exist. this must
- # be done after retrieving the list of interfaces to be removed.
- if not conf.exists(base):
- isis.update({'deleted' : ''})
- return isis
-
- # merge in default values
- isis = conf.merge_defaults(isis, recursive=True)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- isis = dict_merge(tmp, isis)
+ # base_path = ['protocols', 'isis']
+
+ # # eqivalent of the C foo ? 'a' : 'b' statement
+ # base = vrf and ['vrf', 'name', vrf, 'protocols', 'isis'] or base_path
+ # isis = conf.get_config_dict(base, key_mangling=('-', '_'),
+ # get_first_key=True,
+ # no_tag_node_value_mangle=True)
+
+ # # Assign the name of our VRF context. This MUST be done before the return
+ # # statement below, else on deletion we will delete the default instance
+ # # instead of the VRF instance.
+ # if vrf: isis['vrf'] = 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)
+
+ # # Bail out early if configuration tree does no longer exist. this must
+ # # be done after retrieving the list of interfaces to be removed.
+ # if not conf.exists(base):
+ # isis.update({'deleted' : ''})
+ # return isis
+
+ # # merge in default values
+ # isis = conf.merge_defaults(isis, recursive=True)
+
+ # # We also need some additional information from the config, prefix-lists
+ # # and route-maps for instance. They will be used in verify().
+ # #
+ # # XXX: one MUST always call this without the key_mangling() option! See
+ # # vyos.configverify.verify_common_route_maps() for more information.
+ # tmp = conf.get_config_dict(['policy'])
+ # # Merge policy dict into "regular" config dict
+ # isis = dict_merge(tmp, isis)
return isis
-def verify(isis):
- # bail out early - looks like removal from running config
- if not isis or 'deleted' in isis:
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'isis', vrf):
return None
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ isis = vrf and config_dict['vrf']['name'][vrf]['protocols']['isis'] or config_dict['isis']
+ isis['policy'] = config_dict['policy']
+
+ if 'deleted' in isis:
+ return None
+
+ if vrf:
+ isis['vrf'] = vrf
+
if 'net' not in isis:
raise ConfigError('Network entity is mandatory!')
@@ -266,37 +280,11 @@ def verify(isis):
return None
-def generate(isis):
- if not isis or 'deleted' in isis:
- return None
-
- isis['frr_isisd_config'] = render_to_string('frr/isisd.frr.j2', isis)
- return None
-
-def apply(isis):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # Generate empty helper string which can be ammended to FRR commands, it
- # will be either empty (default VRF) or contain the "vrf <name" statement
- vrf = ''
- if 'vrf' in isis:
- vrf = ' vrf ' + isis['vrf']
-
- frr_cfg.load_configuration(frr.isis_daemon)
- 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}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'frr_isisd_config' in isis:
- frr_cfg.add_before(frr.default_add_before, isis['frr_isisd_config'])
-
- frr_cfg.commit_configuration(frr.isis_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 4a05b8044..2a691f4a4 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
@@ -20,33 +20,33 @@ from sys import exit
from glob import glob
from vyos.config import Config
-from vyos.template import render_to_string
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
from vyos.utils.file import read_file
from vyos.utils.system import sysctl_write
from vyos.configverify import verify_interface_exists
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/ldpd.frr'
+frrender = FRRender()
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'mpls']
- mpls = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- return mpls
+ return get_frrender_dict(conf)
-def verify(mpls):
- # If no config, then just bail out early.
- if not mpls:
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'mpls'):
return None
+ mpls = config_dict['mpls']
+
if 'interface' in mpls:
for interface in mpls['interface']:
verify_interface_exists(mpls, interface)
@@ -68,24 +68,16 @@ def verify(mpls):
return None
-def generate(mpls):
- # If there's no MPLS config generated, create dictionary key with no value.
- if not mpls or 'deleted' in mpls:
- return None
+def generate(config_dict):
+ frrender.generate(config_dict)
- mpls['frr_ldpd_config'] = render_to_string('frr/ldpd.frr.j2', mpls)
- return None
+def apply(config_dict):
+ frrender.apply()
-def apply(mpls):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(frr.ldpd_daemon)
- frr_cfg.modify_section(f'^mpls ldp', stop_pattern='^exit', remove_stop_mark=True)
+ if not has_frr_protocol_in_dict(config_dict, 'mpls'):
+ return None
- if 'frr_ldpd_config' in mpls:
- frr_cfg.add_before(frr.default_add_before, mpls['frr_ldpd_config'])
- frr_cfg.commit_configuration(frr.ldpd_daemon)
+ mpls = config_dict['mpls']
# Set number of entries in the platform label tables
labels = '0'
diff --git a/src/conf_mode/protocols_openfabric.py b/src/conf_mode/protocols_openfabric.py
index b60117a02..3dc06ee68 100644
--- a/src/conf_mode/protocols_openfabric.py
+++ b/src/conf_mode/protocols_openfabric.py
@@ -18,14 +18,15 @@ from sys import exit
from vyos.base import Warning
from vyos.config import Config
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
from vyos.configverify import verify_interface_exists
-from vyos.template import render_to_string
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
def get_config(config=None):
if config:
@@ -33,32 +34,14 @@ def get_config(config=None):
else:
conf = Config()
- base_path = ['protocols', 'openfabric']
+ return get_frrender_dict(conf)
- openfabric = conf.get_config_dict(base_path, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # Remove per domain MPLS configuration - get a list of all changed Openfabric domains
- # (removed and added) so that they will be properly rendered for the FRR config.
- openfabric['domains_all'] = list(conf.list_nodes(' '.join(base_path) + f' domain') +
- node_changed(conf, base_path + ['domain']))
-
- # Get a list of all interfaces
- openfabric['interfaces_all'] = []
- for domain in openfabric['domains_all']:
- interfaces_modified = list(node_changed(conf, base_path + ['domain', domain, 'interface']) +
- conf.list_nodes(' '.join(base_path) + f' domain {domain} interface'))
- openfabric['interfaces_all'].extend(interfaces_modified)
-
- if not conf.exists(base_path):
- openfabric.update({'deleted': ''})
-
- return openfabric
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'openfabric'):
+ return None
-def verify(openfabric):
- # bail out early - looks like removal from running config
- if not openfabric or 'deleted' in openfabric:
+ openfabric = config_dict['openfabric']
+ if 'deleted' in openfabric:
return None
if 'net' not in openfabric:
@@ -107,29 +90,11 @@ def verify(openfabric):
return None
-def generate(openfabric):
- if not openfabric or 'deleted' in openfabric:
- return None
-
- openfabric['frr_fabricd_config'] = render_to_string('frr/fabricd.frr.j2', openfabric)
- return None
-
-def apply(openfabric):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(frr.openfabric_daemon)
- for domain in openfabric['domains_all']:
- frr_cfg.modify_section(f'^router openfabric {domain}', stop_pattern='^exit', remove_stop_mark=True)
-
- for interface in openfabric['interfaces_all']:
- frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'frr_fabricd_config' in openfabric:
- frr_cfg.add_before(frr.default_add_before, openfabric['frr_fabricd_config'])
-
- frr_cfg.commit_configuration(frr.openfabric_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 44817b9b7..32ad5e497 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -18,20 +18,23 @@ from sys import exit
from sys import argv
from vyos.config import Config
-from vyos.config import config_dict_merge
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_route_map
from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_access_list
-from vyos.template import render_to_string
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
from vyos.utils.network import get_interface_config
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
def get_config(config=None):
if config:
@@ -39,85 +42,16 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf)
- base_path = ['protocols', 'ospf']
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'ospf', vrf):
+ return None
# eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'ospf'] or base_path
- ospf = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True)
-
- # Assign the name of our VRF context. This MUST be done before the return
- # statement below, else on deletion we will delete the default instance
- # instead of the VRF instance.
- if vrf: ospf['vrf'] = 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)
-
- # Bail out early if configuration tree does no longer exist. this must
- # be done after retrieving the list of interfaces to be removed.
- if not conf.exists(base):
- ospf.update({'deleted' : ''})
- return ospf
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = conf.get_config_defaults(**ospf.kwargs, recursive=True)
-
- # We have to cleanup the default dict, as default values could enable features
- # which are not explicitly enabled on the CLI. Example: default-information
- # originate comes with a default metric-type of 2, which will enable the
- # entire default-information originate tree, even when not set via CLI so we
- # need to check this first and probably drop that key.
- if dict_search('default_information.originate', ospf) is None:
- del default_values['default_information']
- if 'mpls_te' not in ospf:
- del default_values['mpls_te']
- if 'graceful_restart' not in ospf:
- del default_values['graceful_restart']
- for area_num in default_values.get('area', []):
- if dict_search(f'area.{area_num}.area_type.nssa', ospf) is None:
- del default_values['area'][area_num]['area_type']['nssa']
-
- for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static']:
- if dict_search(f'redistribute.{protocol}', ospf) is None:
- del default_values['redistribute'][protocol]
- if not bool(default_values['redistribute']):
- del default_values['redistribute']
-
- for interface in ospf.get('interface', []):
- # We need to reload the defaults on every pass b/c of
- # hello-multiplier dependency on dead-interval
- # If hello-multiplier is set, we need to remove the default from
- # dead-interval.
- if 'hello_multiplier' in ospf['interface'][interface]:
- del default_values['interface'][interface]['dead_interval']
-
- ospf = config_dict_merge(default_values, ospf)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- ospf = dict_merge(tmp, ospf)
-
- return ospf
-
-def verify(ospf):
- if not ospf:
- return None
+ ospf = vrf and config_dict['vrf']['name'][vrf]['protocols']['ospf'] or config_dict['ospf']
+ ospf['policy'] = config_dict['policy']
verify_common_route_maps(ospf)
@@ -164,8 +98,7 @@ def verify(ospf):
# interface is bound to our requesting VRF. Due to the VyOS
# priorities the interface is bound to the VRF after creation of
# the VRF itself, and before any routing protocol is configured.
- if 'vrf' in ospf:
- vrf = ospf['vrf']
+ if vrf:
tmp = get_interface_config(interface)
if 'master' not in tmp or tmp['master'] != vrf:
raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!')
@@ -244,37 +177,11 @@ def verify(ospf):
return None
-def generate(ospf):
- if not ospf or 'deleted' in ospf:
- return None
-
- ospf['frr_ospfd_config'] = render_to_string('frr/ospfd.frr.j2', ospf)
- return None
-
-def apply(ospf):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # Generate empty helper string which can be ammended to FRR commands, it
- # will be either empty (default VRF) or contain the "vrf <name" statement
- vrf = ''
- if 'vrf' in ospf:
- vrf = ' vrf ' + ospf['vrf']
-
- frr_cfg.load_configuration(frr.ospf_daemon)
- 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}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'frr_ospfd_config' in ospf:
- frr_cfg.add_before(frr.default_add_before, ospf['frr_ospfd_config'])
-
- frr_cfg.commit_configuration(frr.ospf_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index 7bdab3017..038fcd2b4 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -18,96 +18,41 @@ from sys import exit
from sys import argv
from vyos.config import Config
-from vyos.config import config_dict_merge
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_route_map
from vyos.configverify import verify_interface_exists
-from vyos.template import render_to_string
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos.ifconfig import Interface
from vyos.utils.dict import dict_search
from vyos.utils.network import get_interface_config
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf)
- base_path = ['protocols', 'ospfv3']
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'ospfv3', vrf):
+ return None
# eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'ospfv3'] or base_path
- ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
-
- # Assign the name of our VRF context. This MUST be done before the return
- # statement below, else on deletion we will delete the default instance
- # instead of the VRF instance.
- if vrf: ospfv3['vrf'] = 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:
- ospfv3['interface_removed'] = list(interfaces_removed)
-
- # Bail out early if configuration tree does no longer exist. this must
- # be done after retrieving the list of interfaces to be removed.
- if not conf.exists(base):
- ospfv3.update({'deleted' : ''})
- return ospfv3
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = conf.get_config_defaults(**ospfv3.kwargs,
- recursive=True)
-
- # We have to cleanup the default dict, as default values could enable features
- # which are not explicitly enabled on the CLI. Example: default-information
- # originate comes with a default metric-type of 2, which will enable the
- # entire default-information originate tree, even when not set via CLI so we
- # need to check this first and probably drop that key.
- if dict_search('default_information.originate', ospfv3) is None:
- del default_values['default_information']
- if 'graceful_restart' not in ospfv3:
- del default_values['graceful_restart']
-
- for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'ripng', 'static']:
- if dict_search(f'redistribute.{protocol}', ospfv3) is None:
- del default_values['redistribute'][protocol]
- if not bool(default_values['redistribute']):
- del default_values['redistribute']
-
- default_values.pop('interface', {})
-
- # merge in remaining default values
- ospfv3 = config_dict_merge(default_values, ospfv3)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- ospfv3 = dict_merge(tmp, ospfv3)
-
- return ospfv3
-
-def verify(ospfv3):
- if not ospfv3:
- return None
+ ospfv3 = vrf and config_dict['vrf']['name'][vrf]['protocols']['ospfv3'] or config_dict['ospfv3']
+ ospfv3['policy'] = config_dict['policy']
verify_common_route_maps(ospfv3)
@@ -137,45 +82,18 @@ def verify(ospfv3):
# interface is bound to our requesting VRF. Due to the VyOS
# priorities the interface is bound to the VRF after creation of
# the VRF itself, and before any routing protocol is configured.
- if 'vrf' in ospfv3:
- vrf = ospfv3['vrf']
+ if vrf:
tmp = get_interface_config(interface)
if 'master' not in tmp or tmp['master'] != vrf:
raise ConfigError(f'Interface "{interface}" is not a member of VRF "{vrf}"!')
return None
-def generate(ospfv3):
- if not ospfv3 or 'deleted' in ospfv3:
- return None
-
- ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.j2', ospfv3)
- return None
-
-def apply(ospfv3):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # Generate empty helper string which can be ammended to FRR commands, it
- # will be either empty (default VRF) or contain the "vrf <name" statement
- vrf = ''
- if 'vrf' in ospfv3:
- vrf = ' vrf ' + ospfv3['vrf']
-
- frr_cfg.load_configuration(frr.ospf6_daemon)
- frr_cfg.modify_section(f'^router ospf6{vrf}', 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(frr.ospf6_daemon)
+def generate(config_dict):
+ frrender.generate(config_dict)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index 994007137..3862530a2 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -17,58 +17,34 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- 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
- # options which we need to update into the dictionary retrived.
- rip = conf.merge_defaults(rip, recursive=True)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- rip = dict_merge(tmp, rip)
-
- return rip
-
-def verify(rip):
- if not rip:
+
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'rip'):
return None
+ rip = config_dict['rip']
+ rip['policy'] = config_dict['policy']
+
verify_common_route_maps(rip)
acl_in = dict_search('distribute_list.access_list.in', rip)
@@ -93,33 +69,11 @@ def verify(rip):
raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
f'with "split-horizon disable" for "{interface}"!')
-def generate(rip):
- if not rip or 'deleted' in rip:
- return None
-
- rip['new_frr_config'] = render_to_string('frr/ripd.frr.j2', rip)
- return None
+def generate(frr_dict):
+ frrender.generate(frr_dict)
def apply(rip):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the mgmt daemon as of FRR 10.1
- frr_cfg.load_configuration(frr.mgmt_daemon)
- frr_cfg.modify_section('^ip protocol rip route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- 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)
-
- if 'new_frr_config' in rip:
- frr_cfg.add_before(frr.default_add_before, rip['new_frr_config'])
- frr_cfg.commit_configuration()
-
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
index 9d4447d1f..327a0d239 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -17,48 +17,34 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_access_list
from vyos.configverify import verify_prefix_list
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'ripng']
- ripng = 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):
- return ripng
-
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- ripng = conf.merge_defaults(ripng, recursive=True)
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- ripng = dict_merge(tmp, ripng)
-
- return ripng
-
-def verify(ripng):
- if not ripng:
+
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ripng'):
return None
+ ripng = config_dict['ripng']
+ ripng['policy'] = config_dict['policy']
+
verify_common_route_maps(ripng)
acl_in = dict_search('distribute_list.access_list.in', ripng)
@@ -83,25 +69,12 @@ def verify(ripng):
raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
f'with "split-horizon disable" for "{interface}"!')
-def generate(ripng):
- if not ripng:
- return None
- ripng['new_frr_config'] = render_to_string('frr/ripngd.frr.j2', ripng)
-
-def apply(ripng):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(frr.mgmt_daemon)
- frr_cfg.modify_section('^ipv6 protocol ripng route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- 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()
+def generate(config_dict):
+ frrender.generate(config_dict)
+ return None
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py
index bec0cda91..c75f95860 100755
--- a/src/conf_mode/protocols_rpki.py
+++ b/src/conf_mode/protocols_rpki.py
@@ -20,15 +20,17 @@ from glob import glob
from sys import exit
from vyos.config import Config
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
from vyos.pki import wrap_openssh_public_key
from vyos.pki import wrap_openssh_private_key
-from vyos.template import render_to_string
from vyos.utils.dict import dict_search_args
from vyos.utils.file import write_file
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
rpki_ssh_key_base = '/run/frr/id_rpki'
@@ -37,25 +39,14 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['protocols', 'rpki']
+ return get_frrender_dict(conf)
- rpki = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, with_pki=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
- # options which we need to update into the dictionary retrived.
- rpki = conf.merge_defaults(rpki, recursive=True)
-
- return rpki
-
-def verify(rpki):
- if not rpki:
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'rpki'):
return None
+ rpki = config_dict['rpki']
+
if 'cache' in rpki:
preferences = []
for peer, peer_config in rpki['cache'].items():
@@ -81,12 +72,14 @@ def verify(rpki):
return None
-def generate(rpki):
+def generate(config_dict):
for key in glob(f'{rpki_ssh_key_base}*'):
os.unlink(key)
- if not rpki:
- return
+ if not has_frr_protocol_in_dict(config_dict, 'rpki'):
+ return None
+
+ rpki = config_dict['rpki']
if 'cache' in rpki:
for cache, cache_config in rpki['cache'].items():
@@ -102,19 +95,11 @@ def generate(rpki):
write_file(cache_config['ssh']['public_key_file'], wrap_openssh_public_key(public_key_data, public_key_type))
write_file(cache_config['ssh']['private_key_file'], wrap_openssh_private_key(private_key_data))
- rpki['new_frr_config'] = render_to_string('frr/rpki.frr.j2', rpki)
-
+ frrender.generate(config_dict)
return None
-def apply(rpki):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.bgp_daemon)
- frr_cfg.modify_section('^rpki', stop_pattern='^exit', remove_stop_mark=True)
- if 'new_frr_config' in rpki:
- frr_cfg.add_before(frr.default_add_before, rpki['new_frr_config'])
-
- frr_cfg.commit_configuration(frr.bgp_daemon)
+def apply(config_dict):
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_segment-routing.py b/src/conf_mode/protocols_segment-routing.py
index 67f8005ef..0fad968e1 100755
--- a/src/conf_mode/protocols_segment-routing.py
+++ b/src/conf_mode/protocols_segment-routing.py
@@ -17,14 +17,17 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import node_changed
-from vyos.template import render_to_string
+from vyos.configdict import get_frrender_dict
+from vyos.configdict import list_diff
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
+from vyos.ifconfig import Section
from vyos.utils.dict import dict_search
from vyos.utils.system import sysctl_write
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
def get_config(config=None):
if config:
@@ -32,25 +35,14 @@ def get_config(config=None):
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,
- with_recursive_defaults=True)
+ return get_frrender_dict(conf)
- # 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:
- sr['interface_removed'] = list(interfaces_removed)
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
+ return None
- import pprint
- pprint.pprint(sr)
- return sr
+ sr = config_dict['segment_routing']
-def verify(sr):
if 'srv6' in sr:
srv6_enable = False
if 'interface' in sr:
@@ -62,45 +54,41 @@ def verify(sr):
raise ConfigError('SRv6 should be enabled on at least one interface!')
return None
-def generate(sr):
- if not sr:
+def generate(config_dict):
+ frrender.generate(config_dict)
+ return None
+
+def apply(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
return None
- sr['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', sr)
- return None
+ sr = config_dict['segment_routing']
-def apply(sr):
- if 'interface_removed' in sr:
- for interface in sr['interface_removed']:
- # Disable processing of IPv6-SR packets
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
+ current_interfaces = Section.interfaces()
+ sr_interfaces = list(sr.get('interface', {}).keys())
- if 'interface' in sr:
- for interface, interface_config in sr['interface'].items():
- # Accept or drop SR-enabled IPv6 packets on this interface
- if 'srv6' in interface_config:
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '1')
- # Define HMAC policy for ingress SR-enabled packets on this interface
- # It's a redundant check as HMAC has a default value - but better safe
- # then sorry
- tmp = dict_search('srv6.hmac', interface_config)
- if tmp == 'accept':
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '0')
- elif tmp == 'drop':
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '1')
- elif tmp == 'ignore':
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '-1')
- else:
- sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
+ for interface in list_diff(current_interfaces, sr_interfaces):
+ # Disable processing of IPv6-SR packets
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.zebra_daemon)
- frr_cfg.modify_section(r'^segment-routing')
- if 'new_frr_config' in sr:
- frr_cfg.add_before(frr.default_add_before, sr['new_frr_config'])
- frr_cfg.commit_configuration(frr.zebra_daemon)
+ for interface, interface_config in sr.get('interface', {}).items():
+ # Accept or drop SR-enabled IPv6 packets on this interface
+ if 'srv6' in interface_config:
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '1')
+ # Define HMAC policy for ingress SR-enabled packets on this interface
+ # It's a redundant check as HMAC has a default value - but better safe
+ # then sorry
+ tmp = dict_search('srv6.hmac', interface_config)
+ if tmp == 'accept':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '0')
+ elif tmp == 'drop':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '1')
+ elif tmp == 'ignore':
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '-1')
+ else:
+ sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0')
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index c5dc77fd2..1e498b256 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -14,21 +14,25 @@
# 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 ipaddress import IPv4Network
from sys import exit
from sys import argv
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.configdict import get_dhcp_interfaces
-from vyos.configdict import get_pppoe_interfaces
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_common_route_maps
from vyos.configverify import verify_vrf
+from vyos.frrender import FRRender
from vyos.template import render
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
+
+vrf = None
+if len(argv) > 1:
+ vrf = argv[1]
config_file = '/etc/iproute2/rt_tables.d/vyos-static.conf'
@@ -38,36 +42,17 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'static', vrf):
+ return None
- base_path = ['protocols', 'static']
# eqivalent of the C foo ? 'a' : 'b' statement
- base = vrf and ['vrf', 'name', vrf, 'protocols', 'static'] or base_path
- static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
-
- # Assign the name of our VRF context
- if vrf: static['vrf'] = vrf
-
- # We also need some additional information from the config, prefix-lists
- # and route-maps for instance. They will be used in verify().
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = conf.get_config_dict(['policy'])
- # Merge policy dict into "regular" config dict
- static = dict_merge(tmp, static)
-
- # T3680 - get a list of all interfaces currently configured to use DHCP
- tmp = get_dhcp_interfaces(conf, vrf)
- if tmp: static.update({'dhcp' : tmp})
- tmp = get_pppoe_interfaces(conf, vrf)
- if tmp: static.update({'pppoe' : tmp})
-
- return static
-
-def verify(static):
+ static = vrf and config_dict['vrf']['name'][vrf]['protocols']['static'] or config_dict['static']
+ static['policy'] = config_dict['policy']
+
verify_common_route_maps(static)
for route in ['route', 'route6']:
@@ -90,33 +75,28 @@ def verify(static):
raise ConfigError(f'Can not use both blackhole and reject for '\
f'prefix "{prefix}"!')
+ if 'multicast' in static and 'route' in static['multicast']:
+ for prefix, prefix_options in static['multicast']['route'].items():
+ if not IPv4Network(prefix).is_multicast:
+ raise ConfigError(f'{prefix} is not a multicast network!')
+
return None
-def generate(static):
- if not static:
+def generate(config_dict):
+ global vrf
+ if not has_frr_protocol_in_dict(config_dict, 'static', vrf):
return None
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ static = vrf and config_dict['vrf']['name'][vrf]['protocols']['static'] or config_dict['static']
+
# Put routing table names in /etc/iproute2/rt_tables
render(config_file, 'iproute2/static.conf.j2', static)
- static['new_frr_config'] = render_to_string('frr/staticd.frr.j2', static)
+ frrender.generate(config_dict)
return None
def apply(static):
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.mgmt_daemon)
-
- if 'vrf' in static:
- vrf = static['vrf']
- frr_cfg.modify_section(f'^vrf {vrf}', stop_pattern='^exit-vrf', remove_stop_mark=True)
- else:
- frr_cfg.modify_section(r'^ip route .*')
- frr_cfg.modify_section(r'^ipv6 route .*')
-
- if 'new_frr_config' in static:
- frr_cfg.add_before(frr.default_add_before, static['new_frr_config'])
- frr_cfg.commit_configuration()
-
+ frrender.apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_static_multicast.py b/src/conf_mode/protocols_static_multicast.py
deleted file mode 100755
index 4393f3ed3..000000000
--- a/src/conf_mode/protocols_static_multicast.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020-2024 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 ipaddress import IPv4Address
-from sys import exit
-
-from vyos import ConfigError
-from vyos import frr
-from vyos.config import Config
-from vyos.template import render_to_string
-
-from vyos import airbag
-airbag.enable()
-
-config_file = r'/tmp/static_mcast.frr'
-
-# Get configuration for static multicast route
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
- mroute = {
- 'old_mroute' : {},
- 'mroute' : {}
- }
-
- base_path = "protocols static multicast"
-
- if not (conf.exists(base_path) or conf.exists_effective(base_path)):
- return None
-
- conf.set_level(base_path)
-
- # Get multicast effective routes
- for route in conf.list_effective_nodes('route'):
- mroute['old_mroute'][route] = {}
- for next_hop in conf.list_effective_nodes('route {0} next-hop'.format(route)):
- mroute['old_mroute'][route].update({
- next_hop : conf.return_value('route {0} next-hop {1} distance'.format(route, next_hop))
- })
-
- # Get multicast effective interface-routes
- for route in conf.list_effective_nodes('interface-route'):
- if not route in mroute['old_mroute']:
- mroute['old_mroute'][route] = {}
- for next_hop in conf.list_effective_nodes('interface-route {0} next-hop-interface'.format(route)):
- mroute['old_mroute'][route].update({
- next_hop : conf.return_value('interface-route {0} next-hop-interface {1} distance'.format(route, next_hop))
- })
-
- # Get multicast routes
- for route in conf.list_nodes('route'):
- mroute['mroute'][route] = {}
- for next_hop in conf.list_nodes('route {0} next-hop'.format(route)):
- mroute['mroute'][route].update({
- next_hop : conf.return_value('route {0} next-hop {1} distance'.format(route, next_hop))
- })
-
- # Get multicast interface-routes
- for route in conf.list_nodes('interface-route'):
- if not route in mroute['mroute']:
- mroute['mroute'][route] = {}
- for next_hop in conf.list_nodes('interface-route {0} next-hop-interface'.format(route)):
- mroute['mroute'][route].update({
- next_hop : conf.return_value('interface-route {0} next-hop-interface {1} distance'.format(route, next_hop))
- })
-
- return mroute
-
-def verify(mroute):
- if mroute is None:
- return None
-
- for mcast_route in mroute['mroute']:
- route = mcast_route.split('/')
- if IPv4Address(route[0]) < IPv4Address('224.0.0.0'):
- raise ConfigError(f'{mcast_route} not a multicast network')
-
-
-def generate(mroute):
- if mroute is None:
- return None
-
- mroute['new_frr_config'] = render_to_string('frr/static_mcast.frr.j2', mroute)
- return None
-
-
-def apply(mroute):
- if mroute is None:
- return None
-
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.mgmt_daemon)
-
- if 'old_mroute' in mroute:
- for route_gr in mroute['old_mroute']:
- for nh in mroute['old_mroute'][route_gr]:
- if mroute['old_mroute'][route_gr][nh]:
- frr_cfg.modify_section(f'^ip mroute {route_gr} {nh} {mroute["old_mroute"][route_gr][nh]}')
- else:
- frr_cfg.modify_section(f'^ip mroute {route_gr} {nh}')
-
- if 'new_frr_config' in mroute:
- frr_cfg.add_before(frr.default_add_before, mroute['new_frr_config'])
-
- frr_cfg.commit_configuration()
- return None
-
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/service_snmp.py b/src/conf_mode/service_snmp.py
index 134662f85..1174b1238 100755
--- a/src/conf_mode/service_snmp.py
+++ b/src/conf_mode/service_snmp.py
@@ -34,7 +34,6 @@ from vyos.utils.process import call
from vyos.utils.permission import chmod_755
from vyos.version import get_version_data
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -261,16 +260,6 @@ def apply(snmp):
# start SNMP daemon
call(f'systemctl reload-or-restart {systemd_service}')
-
- # Enable AgentX in FRR
- # This should be done for each daemon individually because common command
- # works only if all the daemons started with SNMP support
- # Following daemons from FRR 9.0/stable have SNMP module compiled in VyOS
- frr_daemons_list = [frr.zebra_daemon, frr.bgp_daemon, frr.ospf_daemon, frr.ospf6_daemon,
- frr.rip_daemon, frr.isis_daemon, frr.ldpd_daemon]
- for frr_daemon in frr_daemons_list:
- call(f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null')
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 3d3845fdf..a13bb8b1e 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -19,13 +19,14 @@ from jmespath import search
from json import loads
from vyos.config import Config
+from vyos.configdict import get_frrender_dict
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_route_map
from vyos.firewall import conntrack_required
+from vyos.frrender import FRRender
from vyos.ifconfig import Interface
from vyos.template import render
-from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.network import get_vrf_tableid
from vyos.utils.network import get_vrf_members
@@ -35,9 +36,9 @@ from vyos.utils.process import cmd
from vyos.utils.process import popen
from vyos.utils.system import sysctl_write
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
+frrender = FRRender()
config_file = '/etc/iproute2/rt_tables.d/vyos-vrf.conf'
k_mod = ['vrf']
@@ -132,6 +133,10 @@ def get_config(config=None):
if 'name' in vrf:
vrf['conntrack'] = conntrack_required(conf)
+ # We need to merge the FRR rendering dict into the VRF dict
+ # this is required to get the route-map information to FRR
+ vrf.update({'frrender' : get_frrender_dict(conf)})
+
# We also need the route-map information from the config
#
# XXX: one MUST always call this without the key_mangling() option! See
@@ -204,8 +209,9 @@ def verify(vrf):
def generate(vrf):
# Render iproute2 VR helper names
render(config_file, 'iproute2/vrf.conf.j2', vrf)
- # Render VRF Kernel/Zebra route-map filters
- vrf['frr_zebra_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf)
+
+ if 'frrender' in vrf:
+ frrender.generate(vrf['frrender'])
return None
@@ -347,13 +353,8 @@ def apply(vrf):
if has_rule(afi, 2000, 'l3mdev'):
call(f'ip {afi} rule del pref 2000 l3mdev unreachable')
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(frr.mgmt_daemon)
- frr_cfg.modify_section(f'^vrf .+', stop_pattern='^exit-vrf', remove_stop_mark=True)
- if 'frr_zebra_config' in vrf:
- frr_cfg.add_before(frr.default_add_before, vrf['frr_zebra_config'])
- frr_cfg.commit_configuration()
+ if 'frrender' in vrf:
+ frrender.apply()
return None