summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/interfaces_bonding.py38
-rwxr-xr-xsrc/conf_mode/interfaces_ethernet.py37
-rwxr-xr-xsrc/conf_mode/policy.py134
-rwxr-xr-xsrc/conf_mode/protocols_babel.py81
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py43
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py156
-rwxr-xr-xsrc/conf_mode/protocols_eigrp.py93
-rwxr-xr-xsrc/conf_mode/protocols_isis.py105
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py46
-rw-r--r--src/conf_mode/protocols_openfabric.py67
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py135
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py125
-rwxr-xr-xsrc/conf_mode/protocols_pim.py107
-rwxr-xr-xsrc/conf_mode/protocols_pim6.py71
-rwxr-xr-xsrc/conf_mode/protocols_rip.py82
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py67
-rwxr-xr-xsrc/conf_mode/protocols_rpki.py53
-rwxr-xr-xsrc/conf_mode/protocols_segment-routing.py96
-rwxr-xr-xsrc/conf_mode/protocols_static.py89
-rwxr-xr-xsrc/conf_mode/protocols_static_multicast.py135
-rwxr-xr-xsrc/conf_mode/service_snmp.py9
-rwxr-xr-xsrc/conf_mode/system_ip.py71
-rwxr-xr-xsrc/conf_mode/system_ipv6.py71
-rwxr-xr-xsrc/conf_mode/vrf.py48
-rwxr-xr-xsrc/init/vyos-router3
-rw-r--r--src/migration-scripts/quagga/11-to-1275
-rwxr-xr-xsrc/services/vyos-configd8
-rw-r--r--src/tests/test_initial_setup.py4
28 files changed, 600 insertions, 1449 deletions
diff --git a/src/conf_mode/interfaces_bonding.py b/src/conf_mode/interfaces_bonding.py
index bbbfb0385..0844d2913 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,19 +31,20 @@ 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
from vyos.utils.network import interface_exists
+from vyos.utils.process import is_systemd_service_running
from vyos.configdict import has_address_configured
from vyos.configdict import has_vrf_configured
-from vyos.configdep import set_dependents, call_dependents
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -87,10 +89,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({'frr_dict' : get_frrender_dict(conf)})
# determine which members have been removed
interfaces_removed = leaf_node_changed(conf, base + [ifname, 'member', 'interface'])
@@ -260,16 +265,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 'frr_dict' in bond and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(bond['frr_dict'])
return None
def apply(bond):
- ifname = bond['ifname']
- b = BondIf(ifname)
+ if 'frr_dict' in bond and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
+
+ b = BondIf(bond['ifname'])
if 'deleted' in bond:
- # delete interface
b.remove()
else:
b.update(bond)
@@ -281,17 +286,6 @@ def apply(bond):
raise ConfigError('Error in updating ethernet interface '
'after deleting it from bond')
- zebra_daemon = 'zebra'
- # 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(zebra_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(zebra_daemon)
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/interfaces_ethernet.py b/src/conf_mode/interfaces_ethernet.py
index 34ce7bc47..5024e6982 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,15 +34,15 @@ 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -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({'frr_dict' : get_frrender_dict(conf)})
+
return ethernet
def verify_speed_duplex(ethernet: dict, ethtool: Ethtool):
@@ -318,42 +322,25 @@ 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 'frr_dict' in ethernet and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(ethernet['frr_dict'])
return None
def apply(ethernet):
- ifname = ethernet['ifname']
-
- e = EthernetIf(ifname)
+ if 'frr_dict' in ethernet and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
+ e = EthernetIf(ethernet['ifname'])
if 'deleted' in ethernet:
- # delete interface
e.remove()
else:
e.update(ethernet)
-
- zebra_daemon = 'zebra'
- # 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(zebra_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(zebra_daemon)
+ return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
-
apply(c)
except ConfigError as e:
print(e)
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index a5963e72c..5e71a612d 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,16 +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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
-
airbag.enable()
-
def community_action_compatibility(actions: dict) -> bool:
"""
Check compatibility of values in community and large community sections
@@ -87,31 +87,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,72 +242,36 @@ 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!')
+ # 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!')
return None
-def generate(policy):
- if not policy:
- return None
- policy['new_frr_config'] = render_to_string('frr/policy.frr.j2', policy)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-
-def apply(policy):
- bgp_daemon = 'bgpd'
- zebra_daemon = 'zebra'
-
- # 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(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(bgp_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(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(zebra_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ 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 90b6e4a31..48b7ae734 100755
--- a/src/conf_mode/protocols_babel.py
+++ b/src/conf_mode/protocols_babel.py
@@ -17,15 +17,14 @@
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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -34,46 +33,16 @@ def get_config(config=None):
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)
+ return get_frrender_dict(conf)
- # 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)
-
- # 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,32 +89,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(babel):
- babel_daemon = 'babeld'
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(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(babel_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 1361bb1a9..2e7d40676 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -15,12 +15,14 @@
# 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -29,22 +31,14 @@ def get_config(config=None):
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 get_frrender_dict(conf)
- return bfd
-
-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,22 +77,13 @@ 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):
- bfd_daemon = 'bfdd'
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 22f020099..ae32dd839 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -19,21 +19,20 @@ 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 is_systemd_service_running
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()
@@ -43,68 +42,7 @@ def get_config(config=None):
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, argv)
def verify_vrf_as_import(search_vrf_name: str, afi_name: str, vrfs_config: dict) -> bool:
"""
@@ -237,13 +175,29 @@ def verify_afi(peer_config, bgp_config):
if tmp: return True
return False
-def verify(bgp):
+def verify(config_dict):
+
+ print('====== verify() ======')
+ import pprint
+ pprint.pprint(config_dict)
+
+ if not has_frr_protocol_in_dict(config_dict, 'bgp'):
+ return None
+
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
+
+ # 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 'deleted' in bgp:
- if 'vrf' in bgp:
+ if vrf:
# Cannot delete vrf if it exists in import vrf list in other vrfs
for tmp_afi in ['ipv4_unicast', 'ipv6_unicast']:
- if verify_vrf_as_import(bgp['vrf'], tmp_afi, bgp['dependent_vrfs']):
- raise ConfigError(f'Cannot delete VRF instance "{bgp["vrf"]}", ' \
+ if verify_vrf_as_import(vrf, tmp_afi, bgp['dependent_vrfs']):
+ raise ConfigError(f'Cannot delete VRF instance "{vrf}", ' \
'unconfigure "import vrf" commands!')
else:
# We are running in the default VRF context, thus we can not delete
@@ -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!')
@@ -281,9 +236,8 @@ def verify(bgp):
for interface in bgp['interface']:
error_msg = f'Interface "{interface}" belongs to different VRF instance'
tmp = get_interface_vrf(interface)
- if 'vrf' in bgp:
- if bgp['vrf'] != tmp:
- vrf = bgp['vrf']
+ if vrf:
+ if vrf != tmp:
raise ConfigError(f'{error_msg} "{vrf}"!')
elif tmp != 'default':
raise ConfigError(f'{error_msg} "{tmp}"!')
@@ -384,10 +338,8 @@ def verify(bgp):
# Only checks for ipv4 and ipv6 neighbors
# Check if neighbor address is assigned as system interface address
- vrf = None
vrf_error_msg = f' in default VRF!'
- if 'vrf' in bgp:
- vrf = bgp['vrf']
+ if vrf:
vrf_error_msg = f' in VRF "{vrf}"!'
if is_ip(peer) and is_addr_assigned(peer, vrf):
@@ -529,7 +481,7 @@ def verify(bgp):
f'{afi} administrative distance {key}!')
if afi in ['ipv4_unicast', 'ipv6_unicast']:
- vrf_name = bgp['vrf'] if dict_search('vrf', bgp) else 'default'
+ vrf_name = vrf if vrf else 'default'
# Verify if currant VRF contains rd and route-target options
# and does not exist in import list in other VRFs
if dict_search(f'rd.vpn.export', afi_config):
@@ -602,46 +554,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
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))
-
- bgp_daemon = 'bgpd'
-
- # 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(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(bgp_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_eigrp.py b/src/conf_mode/protocols_eigrp.py
index c13e52a3d..8f49bb151 100755
--- a/src/conf_mode/protocols_eigrp.py
+++ b/src/conf_mode/protocols_eigrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -18,94 +18,49 @@ 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.utils.process import is_systemd_service_running
+from vyos.frrender import FRRender
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
-
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
-
- base_path = ['protocols', 'eigrp']
-
- # 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)
+ return get_frrender_dict(conf, argv)
- # 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)
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'eigrp'):
+ return None
- return eigrp
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
-def verify(eigrp):
- if not eigrp or 'deleted' in eigrp:
- return
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ 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!')
- 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)
+ if vrf:
+ verify_vrf({'vrf': vrf})
-def apply(eigrp):
- eigrp_daemon = 'eigrpd'
-
- # 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(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(eigrp_daemon)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
+ return None
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index ba2f3cf0d..1e5f0d6e8 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -18,16 +18,16 @@ 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -37,54 +37,21 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf, argv)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'isis'):
+ return None
- base_path = ['protocols', 'isis']
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
# 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:
+ 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 'net' not in isis:
@@ -114,12 +81,11 @@ def verify(isis):
f'Recommended area lsp-mtu {recom_area_mtu} or less ' \
'(calculated on MTU size).')
- if 'vrf' in isis:
+ if vrf:
# If interface specific options are set, we must ensure that the
# 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.
- vrf = isis['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}"!')
@@ -266,39 +232,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(isis):
- isis_daemon = 'isisd'
-
- # 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(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(isis_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index ad164db9f..e8097b7ff 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,32 @@ 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.process import is_systemd_service_running
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'
-
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,26 +67,19 @@ 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
-
- mpls['frr_ldpd_config'] = render_to_string('frr/ldpd.frr.j2', mpls)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(mpls):
- ldpd_damon = 'ldpd'
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
- frr_cfg.load_configuration(ldpd_damon)
- 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(ldpd_damon)
+ 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 8e8c50c06..41c5d9544 100644
--- a/src/conf_mode/protocols_openfabric.py
+++ b/src/conf_mode/protocols_openfabric.py
@@ -18,13 +18,13 @@ 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.utils.process import is_systemd_service_running
+from vyos.frrender import FRRender
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
-
airbag.enable()
def get_config(config=None):
@@ -33,32 +33,14 @@ def get_config(config=None):
else:
conf = Config()
- base_path = ['protocols', 'openfabric']
-
- 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)
+ return get_frrender_dict(conf)
- 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,31 +89,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(openfabric):
- openfabric_daemon = 'fabricd'
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(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(openfabric_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 7347c4faa..f2c95a63c 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -18,18 +18,17 @@ 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -39,85 +38,19 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
-
- base_path = ['protocols', 'ospf']
-
- # 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
+ return get_frrender_dict(conf, argv)
- # 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)
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ospf'):
+ return None
- return ospf
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
-def verify(ospf):
- if not ospf:
- return None
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ 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 +97,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,39 +176,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(ospf):
- ospf_daemon = 'ospfd'
-
- # 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(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(ospf_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index 60c2a9b16..ac189c378 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -18,18 +18,17 @@ 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -39,75 +38,19 @@ def get_config(config=None):
else:
conf = Config()
- vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ return get_frrender_dict(conf, argv)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ospfv3'):
+ return None
- base_path = ['protocols', 'ospfv3']
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
# 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,47 +80,21 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(ospfv3):
- ospf6_daemon = 'ospf6d'
-
- # 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(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(ospf6_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py
index 79294a1f0..477895b0b 100755
--- a/src/conf_mode/protocols_pim.py
+++ b/src/conf_mode/protocols_pim.py
@@ -22,72 +22,33 @@ from signal import SIGTERM
from sys import exit
from vyos.config import Config
-from vyos.config import config_dict_merge
-from vyos.configdict import node_changed
+from vyos.configdict import get_frrender_dict
from vyos.configverify import verify_interface_exists
+from vyos.configverify import has_frr_protocol_in_dict
+from vyos.frrender import FRRender
+from vyos.frrender import pim_daemon
+from vyos.utils.process import is_systemd_service_running
from vyos.utils.process import process_named_running
from vyos.utils.process import call
-from vyos.template import render_to_string
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
-RESERVED_MC_NET = '224.0.0.0/24'
-
-
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base = ['protocols', 'pim']
-
- pim = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- # We can not run both IGMP proxy and PIM at the same time - get IGMP
- # proxy status
- if conf.exists(['protocols', 'igmp-proxy']):
- pim.update({'igmp_proxy_enabled' : {}})
-
- # 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:
- pim['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):
- pim.update({'deleted' : ''})
- return pim
-
- # 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(**pim.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.
- for interface in pim.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 'igmp' not in pim['interface'][interface]:
- del default_values['interface'][interface]['igmp']
-
- pim = config_dict_merge(default_values, pim)
- return pim
-
-def verify(pim):
- if not pim or 'deleted' in pim:
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'pim'):
+ return None
+
+ pim = config_dict['pim']
+
+ if 'deleted' in pim:
return None
if 'igmp_proxy_enabled' in pim:
@@ -96,6 +57,7 @@ def verify(pim):
if 'interface' not in pim:
raise ConfigError('PIM require defined interfaces!')
+ RESERVED_MC_NET = '224.0.0.0/24'
for interface, interface_config in pim['interface'].items():
verify_interface_exists(pim, interface)
@@ -124,41 +86,26 @@ def verify(pim):
raise ConfigError(f'{pim_base_error} must be unique!')
unique.append(gr_addr)
-def generate(pim):
- if not pim or 'deleted' in pim:
- return None
- pim['frr_pimd_config'] = render_to_string('frr/pimd.frr.j2', pim)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(pim):
- pim_daemon = 'pimd'
- pim_pid = process_named_running(pim_daemon)
-
- if not pim or 'deleted' in pim:
- if 'deleted' in pim:
- os.kill(int(pim_pid), SIGTERM)
+def apply(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'pim'):
+ return None
+ pim_pid = process_named_running(pim_daemon)
+ pim = config_dict['pim']
+ if 'deleted' in pim:
+ os.kill(int(pim_pid), SIGTERM)
return None
if not pim_pid:
call('/usr/lib/frr/pimd -d -F traditional --daemon -A 127.0.0.1')
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(pim_daemon)
- frr_cfg.modify_section(f'^ip pim')
- frr_cfg.modify_section(f'^ip igmp')
-
- for key in ['interface', 'interface_removed']:
- if key not in pim:
- continue
- for interface in pim[key]:
- frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'frr_pimd_config' in pim:
- frr_cfg.add_before(frr.default_add_before, pim['frr_pimd_config'])
- frr_cfg.commit_configuration(pim_daemon)
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_pim6.py b/src/conf_mode/protocols_pim6.py
index 581ffe238..3a9b876cc 100755
--- a/src/conf_mode/protocols_pim6.py
+++ b/src/conf_mode/protocols_pim6.py
@@ -19,12 +19,12 @@ from ipaddress import IPv6Network
from sys import exit
from vyos.config import Config
-from vyos.config import config_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_interface_exists
-from vyos.template import render_to_string
+from vyos.utils.process import is_systemd_service_running
+from vyos.frrender import FRRender
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -33,34 +33,15 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['protocols', 'pim6']
- pim6 = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=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:
- pim6['interface_removed'] = list(interfaces_removed)
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'pim6'):
+ return None
- # 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):
- pim6.update({'deleted' : ''})
- return pim6
-
- # 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(**pim6.kwargs, recursive=True)
-
- pim6 = config_dict_merge(default_values, pim6)
- return pim6
-
-def verify(pim6):
- if not pim6 or 'deleted' in pim6:
- return
+ pim6 = config_dict['pim6']
+ if 'deleted' in pim6:
+ return None
for interface, interface_config in pim6.get('interface', {}).items():
verify_interface_exists(pim6, interface)
@@ -94,32 +75,14 @@ def verify(pim6):
raise ConfigError(f'{pim_base_error} must be unique!')
unique.append(gr_addr)
-def generate(pim6):
- if not pim6 or 'deleted' in pim6:
- return
- pim6['new_frr_config'] = render_to_string('frr/pim6d.frr.j2', pim6)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(pim6):
- if pim6 is None:
- return
-
- pim6_daemon = 'pim6d'
-
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- frr_cfg.load_configuration(pim6_daemon)
-
- for key in ['interface', 'interface_removed']:
- if key not in pim6:
- continue
- for interface in pim6[key]:
- frr_cfg.modify_section(f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
-
- if 'new_frr_config' in pim6:
- frr_cfg.add_before(frr.default_add_before, pim6['new_frr_config'])
- frr_cfg.commit_configuration(pim6_daemon)
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index 9afac544d..39743f965 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -17,15 +17,15 @@
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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -34,41 +34,16 @@ def get_config(config=None):
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)
+ return get_frrender_dict(conf)
- # 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:
+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,39 +68,14 @@ 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)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(rip):
- rip_daemon = 'ripd'
- zebra_daemon = 'zebra'
-
- # 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(zebra_daemon)
- 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('^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(rip_daemon)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
index 23416ff96..14f038444 100755
--- a/src/conf_mode/protocols_ripng.py
+++ b/src/conf_mode/protocols_ripng.py
@@ -17,14 +17,15 @@
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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -33,32 +34,16 @@ def get_config(config=None):
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
+ return get_frrender_dict(conf)
- # 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:
+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,34 +68,14 @@ 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:
- ripng['new_frr_config'] = ''
- return None
-
- ripng['new_frr_config'] = render_to_string('frr/ripngd.frr.j2', ripng)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(ripng):
- ripng_daemon = 'ripngd'
- zebra_daemon = 'zebra'
-
- # 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(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)
-
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_rpki.py b/src/conf_mode/protocols_rpki.py
index a59ecf3e4..5ad656586 100755
--- a/src/conf_mode/protocols_rpki.py
+++ b/src/conf_mode/protocols_rpki.py
@@ -20,13 +20,15 @@ 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.utils.process import is_systemd_service_running
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -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,21 +95,13 @@ 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)
-
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
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(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(bgp_daemon)
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ 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 b36c2ca11..99cf87556 100755
--- a/src/conf_mode/protocols_segment-routing.py
+++ b/src/conf_mode/protocols_segment-routing.py
@@ -17,12 +17,15 @@
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.process import is_systemd_service_running
from vyos.utils.system import sysctl_write
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -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,47 +54,43 @@ def verify(sr):
raise ConfigError('SRv6 should be enabled on at least one interface!')
return None
-def generate(sr):
- if not sr:
- return None
-
- sr['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', sr)
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
return None
-def apply(sr):
- zebra_daemon = 'zebra'
+def apply(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'segment_routing'):
+ return None
- 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')
+ sr = config_dict['segment_routing']
+
+ 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(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(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')
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 430cc69d4..9d02db6dd 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -14,19 +14,19 @@
# 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.utils.process import is_systemd_service_running
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()
@@ -38,36 +38,20 @@ def get_config(config=None):
else:
conf = Config()
+ return get_frrender_dict(conf, argv)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'static'):
+ return None
+
vrf = None
- if len(argv) > 1:
- vrf = argv[1]
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
- 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,35 +74,34 @@ 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):
+ if not has_frr_protocol_in_dict(config_dict, 'static'):
return None
- # 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)
- return None
-
-def apply(static):
- static_daemon = 'staticd'
+ vrf = None
+ if 'vrf_context' in config_dict:
+ vrf = config_dict['vrf_context']
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(static_daemon)
+ # eqivalent of the C foo ? 'a' : 'b' statement
+ static = vrf and config_dict['vrf']['name'][vrf]['protocols']['static'] or config_dict['static']
- 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 .*')
+ # Put routing table names in /etc/iproute2/rt_tables
+ render(config_file, 'iproute2/static.conf.j2', static)
- if 'new_frr_config' in static:
- frr_cfg.add_before(frr.default_add_before, static['new_frr_config'])
- frr_cfg.commit_configuration(static_daemon)
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
+ return None
+def apply(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ 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 c8894fd41..000000000
--- a/src/conf_mode/protocols_static_multicast.py
+++ /dev/null
@@ -1,135 +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
- static_daemon = 'staticd'
-
- frr_cfg = frr.FRRConfig()
- frr_cfg.load_configuration(static_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(static_daemon)
-
- 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 c9c0ed9a0..1174b1238 100755
--- a/src/conf_mode/service_snmp.py
+++ b/src/conf_mode/service_snmp.py
@@ -260,15 +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 = ['zebra', 'bgpd', 'ospf6d', 'ospfd', 'ripd', 'isisd', 'ldpd']
- 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/system_ip.py b/src/conf_mode/system_ip.py
index c8a91fd2f..86843eb78 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -17,17 +17,17 @@
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_route_map
-from vyos.template import render_to_string
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
-from vyos.utils.file import write_file
from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import is_systemd_service_running
from vyos.utils.system import sysctl_write
-from vyos.configdep import set_dependents
-from vyos.configdep import call_dependents
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -36,42 +36,36 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['system', 'ip']
-
- opt = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- with_recursive_defaults=True)
-
- # When working with FRR we need to know the corresponding address-family
- opt['afi'] = 'ip'
-
- # We also need the route-map information from the config
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
- get_first_key=True)}}
- # Merge policy dict into "regular" config dict
- opt = dict_merge(tmp, opt)
# If IPv4 ARP table size is set here and also manually in sysctl, the more
# fine grained value from sysctl must win
set_dependents('sysctl', conf)
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ip'):
+ return None
- return opt
+ opt = config_dict['ip']
+ opt['policy'] = config_dict['policy']
-def verify(opt):
if 'protocol' in opt:
for protocol, protocol_options in opt['protocol'].items():
if 'route_map' in protocol_options:
verify_route_map(protocol_options['route_map'], opt)
return
-def generate(opt):
- opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
- return
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
+ return None
+
+def apply(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ip'):
+
+ return None
+ opt = config_dict['ip']
-def apply(opt):
# Apply ARP threshold values
# table_size has a default value - thus the key always exists
size = int(dict_search('arp.table_size', opt))
@@ -82,11 +76,6 @@ def apply(opt):
# Minimum number of stored records is indicated which is not cleared
sysctl_write('net.ipv4.neigh.default.gc_thresh1', size // 8)
- # enable/disable IPv4 forwarding
- tmp = dict_search('disable_forwarding', opt)
- value = '0' if (tmp != None) else '1'
- write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
-
# configure multipath
tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
value = '1' if (tmp != None) else '0'
@@ -121,19 +110,11 @@ def apply(opt):
# running when this script is called first. Skip this part and wait for initial
# commit of the configuration to trigger this statement
if is_systemd_service_active('frr.service'):
- zebra_daemon = 'zebra'
- # 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(zebra_daemon)
- frr_cfg.modify_section(r'no ip nht resolve-via-default')
- frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
call_dependents()
+ return None
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system_ipv6.py b/src/conf_mode/system_ipv6.py
index a2442d009..593b8f7f3 100755
--- a/src/conf_mode/system_ipv6.py
+++ b/src/conf_mode/system_ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -18,17 +18,18 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
+from vyos.configdict import get_frrender_dict
+from vyos.configverify import has_frr_protocol_in_dict
from vyos.configverify import verify_route_map
-from vyos.template import render_to_string
+from vyos.frrender import FRRender
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import is_systemd_service_running
from vyos.utils.system import sysctl_write
-from vyos.configdep import set_dependents
-from vyos.configdep import call_dependents
from vyos import ConfigError
-from vyos import frr
from vyos import airbag
airbag.enable()
@@ -37,42 +38,35 @@ def get_config(config=None):
conf = config
else:
conf = Config()
- base = ['system', 'ipv6']
-
- opt = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- with_recursive_defaults=True)
-
- # When working with FRR we need to know the corresponding address-family
- opt['afi'] = 'ipv6'
-
- # We also need the route-map information from the config
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
- get_first_key=True)}}
- # Merge policy dict into "regular" config dict
- opt = dict_merge(tmp, opt)
# If IPv6 neighbor table size is set here and also manually in sysctl, the more
# fine grained value from sysctl must win
set_dependents('sysctl', conf)
+ return get_frrender_dict(conf)
+
+def verify(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ipv6'):
+ return None
- return opt
+ opt = config_dict['ipv6']
+ opt['policy'] = config_dict['policy']
-def verify(opt):
if 'protocol' in opt:
for protocol, protocol_options in opt['protocol'].items():
if 'route_map' in protocol_options:
verify_route_map(protocol_options['route_map'], opt)
return
-def generate(opt):
- opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
- return
+def generate(config_dict):
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(config_dict)
+ return None
+
+def apply(config_dict):
+ if not has_frr_protocol_in_dict(config_dict, 'ipv6'):
+ return None
+ opt = config_dict['ipv6']
-def apply(opt):
# configure multipath
tmp = dict_search('multipath.layer4_hashing', opt)
value = '1' if (tmp != None) else '0'
@@ -88,11 +82,6 @@ def apply(opt):
# Minimum number of stored records is indicated which is not cleared
sysctl_write('net.ipv6.neigh.default.gc_thresh1', size // 8)
- # enable/disable IPv6 forwarding
- tmp = dict_search('disable_forwarding', opt)
- value = '0' if (tmp != None) else '1'
- write_file('/proc/sys/net/ipv6/conf/all/forwarding', value)
-
# configure IPv6 strict-dad
tmp = dict_search('strict_dad', opt)
value = '2' if (tmp != None) else '1'
@@ -105,19 +94,11 @@ def apply(opt):
# running when this script is called first. Skip this part and wait for initial
# commit of the configuration to trigger this statement
if is_systemd_service_active('frr.service'):
- zebra_daemon = 'zebra'
- # 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(zebra_daemon)
- frr_cfg.modify_section(r'no ipv6 nht resolve-via-default')
- frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ if config_dict and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
call_dependents()
+ return None
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 72b178c89..6533f493f 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -19,23 +19,23 @@ from jmespath import search
from json import loads
from vyos.config import Config
-from vyos.configdict import dict_merge
+from vyos.configdict import get_frrender_dict
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
from vyos.utils.network import interface_exists
from vyos.utils.process import call
from vyos.utils.process import cmd
+from vyos.utils.process import is_systemd_service_running
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()
@@ -132,15 +132,9 @@ def get_config(config=None):
if 'name' in vrf:
vrf['conntrack'] = conntrack_required(conf)
- # We also need the route-map information from the config
- #
- # XXX: one MUST always call this without the key_mangling() option! See
- # vyos.configverify.verify_common_route_maps() for more information.
- tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
- get_first_key=True)}}
-
- # Merge policy dict into "regular" config dict
- vrf = dict_merge(tmp, vrf)
+ # 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({'frr_dict' : get_frrender_dict(conf)})
return vrf
def verify(vrf):
@@ -158,6 +152,7 @@ def verify(vrf):
reserved_names = ["add", "all", "broadcast", "default", "delete", "dev",
"get", "inet", "mtu", "link", "type", "vrf"]
table_ids = []
+ vnis = []
for name, vrf_config in vrf['name'].items():
# Reserved VRF names
if name in reserved_names:
@@ -178,17 +173,24 @@ def verify(vrf):
raise ConfigError(f'VRF "{name}" table id is not unique!')
table_ids.append(vrf_config['table'])
+ # VRF VNIs must be unique on the system
+ if 'vni' in vrf_config:
+ vni = vrf_config['vni']
+ if vni in vnis:
+ raise ConfigError(f'VRF "{name}" VNI "{vni}" is not unique!')
+ vnis.append(vni)
+
tmp = dict_search('ip.protocol', vrf_config)
if tmp != None:
for protocol, protocol_options in tmp.items():
if 'route_map' in protocol_options:
- verify_route_map(protocol_options['route_map'], vrf)
+ verify_route_map(protocol_options['route_map'], vrf['frr_dict'])
tmp = dict_search('ipv6.protocol', vrf_config)
if tmp != None:
for protocol, protocol_options in tmp.items():
if 'route_map' in protocol_options:
- verify_route_map(protocol_options['route_map'], vrf)
+ verify_route_map(protocol_options['route_map'], vrf['frr_dict'])
return None
@@ -196,8 +198,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 'frr_dict' in vrf and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().generate(vrf['frr_dict'])
return None
@@ -339,17 +342,8 @@ def apply(vrf):
if has_rule(afi, 2000, 'l3mdev'):
call(f'ip {afi} rule del pref 2000 l3mdev unreachable')
- # Apply FRR filters
- zebra_daemon = 'zebra'
- # 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(zebra_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(zebra_daemon)
+ if 'frr_dict' in vrf and not is_systemd_service_running('vyos-configd.service'):
+ FRRender().apply()
return None
diff --git a/src/init/vyos-router b/src/init/vyos-router
index e2e964656..00136309b 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -474,9 +474,10 @@ start ()
# enable some debugging before loading the configuration
if grep -q vyos-debug /proc/cmdline; then
log_action_begin_msg "Enable runtime debugging options"
+ FRR_DEBUG=$(python3 -c "from vyos.defaults import frr_debug_enable; print(frr_debug_enable)")
+ touch $FRR_DEBUG
touch /tmp/vyos.container.debug
touch /tmp/vyos.ifconfig.debug
- touch /tmp/vyos.frr.debug
touch /tmp/vyos.container.debug
touch /tmp/vyos.smoketest.debug
fi
diff --git a/src/migration-scripts/quagga/11-to-12 b/src/migration-scripts/quagga/11-to-12
new file mode 100644
index 000000000..8ae2023a1
--- /dev/null
+++ b/src/migration-scripts/quagga/11-to-12
@@ -0,0 +1,75 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# T6747:
+# - Migrate static BFD configuration to match FRR possibillities
+# - Consolidate static multicast routing configuration under a new node
+
+from vyos.configtree import ConfigTree
+
+static_base = ['protocols', 'static']
+
+def migrate(config: ConfigTree) -> None:
+ # Check for static route/route6 configuration
+ # Migrate static BFD configuration to match FRR possibillities
+ for route_route6 in ['route', 'route6']:
+ route_route6_base = static_base + [route_route6]
+ if not config.exists(route_route6_base):
+ continue
+
+ for prefix in config.list_nodes(route_route6_base):
+ next_hop_base = route_route6_base + [prefix, 'next-hop']
+ if not config.exists(next_hop_base):
+ continue
+
+ for next_hop in config.list_nodes(next_hop_base):
+ multi_hop_base = next_hop_base + [next_hop, 'bfd', 'multi-hop']
+
+ if not config.exists(multi_hop_base):
+ continue
+
+ mh_source_base = multi_hop_base + ['source']
+ source = None
+ profile = None
+ for src_ip in config.list_nodes(mh_source_base):
+ source = src_ip
+ if config.exists(mh_source_base + [source, 'profile']):
+ profile = config.return_value(mh_source_base + [source, 'profile'])
+ # FRR only supports one source, we will use the first one
+ break
+
+ config.delete(multi_hop_base)
+ config.set(multi_hop_base + ['source-address'], value=source)
+ config.set(next_hop_base + [next_hop, 'bfd', 'profile'], value=profile)
+
+ # Consolidate static multicast routing configuration under a new node
+ if config.exists(static_base + ['multicast']):
+ for mroute in ['interface-route', 'route']:
+ mroute_base = static_base + ['multicast', mroute]
+ if not config.exists(mroute_base):
+ continue
+ config.set(static_base + ['mroute'])
+ config.set_tag(static_base + ['mroute'])
+ for route in config.list_nodes(mroute_base):
+ config.copy(mroute_base + [route], static_base + ['mroute', route])
+
+ mroute_base = static_base + ['mroute']
+ if config.exists(mroute_base):
+ for mroute in config.list_nodes(mroute_base):
+ interface_path = mroute_base + [mroute, 'next-hop-interface']
+ if config.exists(interface_path):
+ config.rename(interface_path, 'interface')
+
+ config.delete(static_base + ['multicast'])
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index d977ba2cb..ecad85801 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -37,6 +37,7 @@ from vyos.configsource import ConfigSourceString
from vyos.configsource import ConfigSourceError
from vyos.configdiff import get_commit_scripts
from vyos.config import Config
+from vyos.frrender import FRRender
from vyos import ConfigError
CFG_GROUP = 'vyattacfg'
@@ -209,6 +210,9 @@ def initialization(socket):
scripts_called = []
setattr(config, 'scripts_called', scripts_called)
+ if not hasattr(config, 'frrender_cls'):
+ setattr(config, 'frrender_cls', FRRender())
+
return config
@@ -326,5 +330,9 @@ if __name__ == '__main__':
if message['last'] and config:
scripts_called = getattr(config, 'scripts_called', [])
logger.debug(f'scripts_called: {scripts_called}')
+
+ if hasattr(config, 'frrender_cls') and res == R_SUCCESS:
+ frrender_cls = getattr(config, 'frrender_cls')
+ frrender_cls.apply()
else:
logger.critical(f'Unexpected message: {message}')
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
index 4cd5fb169..7737f9df5 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -92,8 +92,8 @@ class TestInitialSetup(TestCase):
vis.set_default_gateway(self.config, '192.0.2.1')
self.assertTrue(self.config.exists(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', '192.0.2.1']))
- self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route', '0.0.0.0/0', 'next-hop']))
- self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route']))
+ self.assertTrue(self.xml.is_tag(['protocols', 'static', 'mroute', '0.0.0.0/0', 'next-hop']))
+ self.assertTrue(self.xml.is_tag(['protocols', 'static', 'mroute']))
if __name__ == "__main__":
unittest.main()