summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py230
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py75
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py32
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py40
-rwxr-xr-xsrc/conf_mode/protocols_rip.py27
-rwxr-xr-xsrc/conf_mode/protocols_ripng.py133
-rwxr-xr-xsrc/conf_mode/protocols_rpki.py28
-rwxr-xr-xsrc/conf_mode/protocols_static.py28
-rwxr-xr-xsrc/conf_mode/protocols_vrf.py28
-rwxr-xr-xsrc/migration-scripts/conntrack/1-to-232
-rwxr-xr-xsrc/migration-scripts/interfaces/18-to-1971
-rwxr-xr-xsrc/migration-scripts/quagga/6-to-772
12 files changed, 396 insertions, 400 deletions
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index d1e551cad..a43eed504 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -17,191 +17,97 @@
import os
from sys import exit
-from copy import deepcopy
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.template import is_ipv6
-from vyos.template import render
+from vyos.template import render_to_string
from vyos.util import call
from vyos.validate import is_ipv6_link_local
+from vyos.xml import defaults
from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/bfd.frr'
-
-default_config_data = {
- 'new_peers': [],
- 'old_peers' : []
-}
-
-# get configuration for BFD peer from proposed or effective configuration
-def get_bfd_peer_config(peer, conf_mode="proposed"):
- conf = Config()
- conf.set_level('protocols bfd peer {0}'.format(peer))
-
- bfd_peer = {
- 'remote': peer,
- 'shutdown': False,
- 'src_if': '',
- 'src_addr': '',
- 'multiplier': '3',
- 'rx_interval': '300',
- 'tx_interval': '300',
- 'multihop': False,
- 'echo_interval': '',
- 'echo_mode': False,
- }
-
- # Check if individual peer is disabled
- if conf_mode == "effective" and conf.exists_effective('shutdown'):
- bfd_peer['shutdown'] = True
- if conf_mode == "proposed" and conf.exists('shutdown'):
- bfd_peer['shutdown'] = True
-
- # Check if peer has a local source interface configured
- if conf_mode == "effective" and conf.exists_effective('source interface'):
- bfd_peer['src_if'] = conf.return_effective_value('source interface')
- if conf_mode == "proposed" and conf.exists('source interface'):
- bfd_peer['src_if'] = conf.return_value('source interface')
-
- # Check if peer has a local source address configured - this is mandatory for IPv6
- if conf_mode == "effective" and conf.exists_effective('source address'):
- bfd_peer['src_addr'] = conf.return_effective_value('source address')
- if conf_mode == "proposed" and conf.exists('source address'):
- bfd_peer['src_addr'] = conf.return_value('source address')
-
- # Tell BFD daemon that we should expect packets with TTL less than 254
- # (because it will take more than one hop) and to listen on the multihop
- # port (4784)
- if conf_mode == "effective" and conf.exists_effective('multihop'):
- bfd_peer['multihop'] = True
- if conf_mode == "proposed" and conf.exists('multihop'):
- bfd_peer['multihop'] = True
-
- # Configures the minimum interval that this system is capable of receiving
- # control packets. The default value is 300 milliseconds.
- if conf_mode == "effective" and conf.exists_effective('interval receive'):
- bfd_peer['rx_interval'] = conf.return_effective_value('interval receive')
- if conf_mode == "proposed" and conf.exists('interval receive'):
- bfd_peer['rx_interval'] = conf.return_value('interval receive')
-
- # The minimum transmission interval (less jitter) that this system wants
- # to use to send BFD control packets.
- if conf_mode == "effective" and conf.exists_effective('interval transmit'):
- bfd_peer['tx_interval'] = conf.return_effective_value('interval transmit')
- if conf_mode == "proposed" and conf.exists('interval transmit'):
- bfd_peer['tx_interval'] = conf.return_value('interval transmit')
-
- # Configures the detection multiplier to determine packet loss. The remote
- # transmission interval will be multiplied by this value to determine the
- # connection loss detection timer. The default value is 3.
- if conf_mode == "effective" and conf.exists_effective('interval multiplier'):
- bfd_peer['multiplier'] = conf.return_effective_value('interval multiplier')
- if conf_mode == "proposed" and conf.exists('interval multiplier'):
- bfd_peer['multiplier'] = conf.return_value('interval multiplier')
-
- # Configures the minimal echo receive transmission interval that this system is capable of handling
- if conf_mode == "effective" and conf.exists_effective('interval echo-interval'):
- bfd_peer['echo_interval'] = conf.return_effective_value('interval echo-interval')
- if conf_mode == "proposed" and conf.exists('interval echo-interval'):
- bfd_peer['echo_interval'] = conf.return_value('interval echo-interval')
-
- # Enables or disables the echo transmission mode
- if conf_mode == "effective" and conf.exists_effective('echo-mode'):
- bfd_peer['echo_mode'] = True
- if conf_mode == "proposed" and conf.exists('echo-mode'):
- bfd_peer['echo_mode'] = True
-
- return bfd_peer
-
-def get_config():
- bfd = deepcopy(default_config_data)
- conf = Config()
- if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')):
- return None
+def get_config(config=None):
+ if config:
+ conf = config
else:
- conf.set_level('protocols bfd')
-
- # as we have to use vtysh to talk to FRR we also need to know
- # which peers are gone due to a config removal - thus we read in
- # all peers (active or to delete)
- for peer in conf.list_effective_nodes('peer'):
- bfd['old_peers'].append(get_bfd_peer_config(peer, "effective"))
-
- for peer in conf.list_nodes('peer'):
- bfd['new_peers'].append(get_bfd_peer_config(peer))
-
- # find deleted peers
- set_new_peers = set(conf.list_nodes('peer'))
- set_old_peers = set(conf.list_effective_nodes('peer'))
- bfd['deleted_peers'] = set_old_peers - set_new_peers
+ conf = Config()
+ base = ['protocols', 'bfd']
+ bfd = conf.get_config_dict(base, get_first_key=True)
+
+ # Bail out early if configuration tree does not exist
+ if not conf.exists(base):
+ return bfd
+
+ # We have gathered the dict representation of the CLI, but there are
+ # default options which we need to update into the dictionary retrived.
+ # XXX: T2665: we currently have no nice way for defaults under tag
+ # nodes, thus we load the defaults "by hand"
+ default_values = defaults(base + ['peer'])
+ if 'peer' in bfd:
+ for peer in bfd['peer']:
+ bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer])
+
+ if 'profile' in bfd:
+ for profile in bfd['profile']:
+ bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile])
return bfd
def verify(bfd):
- if bfd is None:
+ if not bfd:
return None
- # some variables to use later
- conf = Config()
-
- for peer in bfd['new_peers']:
- # IPv6 link local peers require an explicit local address/interface
- if is_ipv6_link_local(peer['remote']):
- if not (peer['src_if'] and peer['src_addr']):
- raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
-
- # IPv6 peers require an explicit local address
- if is_ipv6(peer['remote']):
- if not peer['src_addr']:
- raise ConfigError('BFD IPv6 peers require explicit local address setting')
-
- # multihop require source address
- if peer['multihop'] and not peer['src_addr']:
- raise ConfigError('Multihop require source address')
-
- # multihop and echo-mode cannot be used together
- if peer['multihop'] and peer['echo_mode']:
- raise ConfigError('Multihop and echo-mode cannot be used together')
-
- # multihop doesn't accept interface names
- if peer['multihop'] and peer['src_if']:
- raise ConfigError('Multihop and source interface cannot be used together')
-
- # echo interval can be configured only with enabled echo-mode
- if peer['echo_interval'] != '' and not peer['echo_mode']:
- raise ConfigError('echo-interval can be configured only with enabled echo-mode')
-
- # check if we deleted peers are not used in configuration
- if conf.exists('protocols bgp'):
- bgp_as = conf.list_nodes('protocols bgp')[0]
-
- # check BGP neighbors
- for peer in bfd['deleted_peers']:
- if conf.exists('protocols bgp {0} neighbor {1} bfd'.format(bgp_as, peer)):
- raise ConfigError('Cannot delete BFD peer {0}: it is used in BGP configuration'.format(peer))
- if conf.exists('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)):
- peer_group = conf.return_value('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer))
- if conf.exists('protocols bgp {0} peer-group {1} bfd'.format(bgp_as, peer_group)):
- raise ConfigError('Cannot delete BFD peer {0}: it belongs to BGP peer-group {1} with enabled BFD'.format(peer, peer_group))
+ if 'peer' in bfd:
+ for peer, peer_config in bfd['peer'].items():
+ # IPv6 link local peers require an explicit local address/interface
+ if is_ipv6_link_local(peer):
+ if 'source' not in peer_config or len(peer_config['source'] < 2):
+ raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
+
+ # IPv6 peers require an explicit local address
+ if is_ipv6(peer):
+ if 'source' not in peer_config or 'address' not in peer_config['source']:
+ raise ConfigError('BFD IPv6 peers require explicit local address setting')
+
+ if 'multihop' in peer_config:
+ # multihop require source address
+ if 'source' not in peer_config or 'address' not in peer_config['source']:
+ raise ConfigError('BFD multihop require source address')
+
+ # multihop and echo-mode cannot be used together
+ if 'echo_mode' in peer_config:
+ raise ConfigError('Multihop and echo-mode cannot be used together')
+
+ # multihop doesn't accept interface names
+ if 'source' in peer_config and 'interface' in peer_config['source']:
+ raise ConfigError('Multihop and source interface cannot be used together')
return None
def generate(bfd):
- if bfd is None:
+ if not bfd:
+ bfd['new_frr_config'] = ''
return None
- render(config_file, 'frr/bfd.frr.tmpl', bfd)
- return None
+ bfd['new_frr_config'] = render_to_string('frr/bfd.frr.tmpl', bfd)
def apply(bfd):
- if bfd is None:
- return None
-
- call("vtysh -d bfdd -f " + config_file)
- if os.path.exists(config_file):
- os.remove(config_file)
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration()
+ frr_cfg.modify_section('^bfd', '')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config'])
+ frr_cfg.commit_configuration()
+
+ # If FRR config is blank, rerun the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if bfd['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration()
return None
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 41d89e03b..b5bb018ae 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -20,7 +20,6 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
@@ -29,17 +28,8 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/bgp.frr'
frr_daemon = 'bgpd'
-DEBUG = os.path.exists('/tmp/bgp.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -64,6 +54,26 @@ def get_config(config=None):
return bgp
+def verify_remote_as(peer_config, asn_config):
+ if 'remote_as' in peer_config:
+ return peer_config['remote_as']
+
+ if 'peer_group' in peer_config:
+ peer_group_name = peer_config['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ if 'interface' in peer_config:
+ if 'remote_as' in peer_config['interface']:
+ return peer_config['interface']['remote_as']
+
+ if 'peer_group' in peer_config['interface']:
+ peer_group_name = peer_config['interface']['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ return None
+
def verify(bgp):
if not bgp:
return None
@@ -89,20 +99,15 @@ def verify(bgp):
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
- # Some checks can/must only be done on a neighbor and nor a peer-group
+
+ # Some checks can/must only be done on a neighbor and not a peer-group
if neighbor == 'neighbor':
# remote-as must be either set explicitly for the neighbor
# or for the entire peer-group
- if 'interface' in peer_config:
- if 'remote_as' not in peer_config['interface']:
- if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+ if not verify_remote_as(peer_config, asn_config):
+ raise ConfigError(f'Neighbor "{peer}" remote-as must be set!')
- elif 'remote_as' not in peer_config:
- if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
-
- for afi in ['ipv4_unicast', 'ipv6_unicast']:
+ for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']:
# Bail out early if address family is not configured
if 'address_family' not in peer_config or afi not in peer_config['address_family']:
continue
@@ -133,6 +138,15 @@ def verify(bgp):
if dict_search(f'policy.route_map.{route_map}', asn_config) == None:
raise ConfigError(f'route-map "{route_map}" used for "{tmp}" does not exist!')
+ if 'route_reflector_client' in afi_config:
+ if 'remote_as' in peer_config and asn != peer_config['remote_as']:
+ raise ConfigError('route-reflector-client only supported for iBGP peers')
+ else:
+ peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config)
+ if 'peer_group' in peer_config and peer_group_as != None and peer_group_as != asn:
+ raise ConfigError('route-reflector-client only supported for iBGP peers')
+
+
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', asn_config) or []:
# we can not use dict_search() here as prefix contains dots ...
@@ -156,33 +170,15 @@ def generate(bgp):
asn = list(bgp.keys())[0]
bgp[asn]['asn'] = asn
- # render(config) not needed, its only for debug
- render(config_file, 'frr/bgp.frr.tmpl', bgp[asn])
bgp['new_frr_config'] = render_to_string('frr/bgp.frr.tmpl', bgp[asn])
-
return None
def apply(bgp):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section(f'router bgp \S+', '')
+ frr_cfg.modify_section(f'^router bgp \d+$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{bgp["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, rerun the blank commit x times due to frr-reload
@@ -191,7 +187,6 @@ def apply(bgp):
for a in range(5):
frr_cfg.commit_configuration(frr_daemon)
-
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 3310fac5a..6d9eb828b 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -22,7 +22,6 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_maps
from vyos.configverify import verify_interface_exists
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
@@ -32,17 +31,8 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/ospf.frr'
frr_daemon = 'ospfd'
-DEBUG = os.path.exists('/tmp/ospf.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -140,34 +130,16 @@ def generate(ospf):
ospf['new_frr_config'] = ''
return None
- # render(config) not needed, its only for debug
- render(config_file, 'frr/ospf.frr.tmpl', ospf)
ospf['new_frr_config'] = render_to_string('frr/ospf.frr.tmpl', ospf)
-
return None
def apply(ospf):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section(r'interface \S+', '')
- frr_cfg.modify_section('router ospf', '')
+ frr_cfg.modify_section(r'^interface \S+', '')
+ frr_cfg.modify_section('^router ospf$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{ospf["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, rerun the blank commit x times due to frr-reload
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index e008a350b..6f068b196 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -21,26 +21,17 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_maps
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
+from vyos.ifconfig import Interface
from vyos.xml import defaults
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/ospfv3.frr'
frr_daemon = 'ospf6d'
-DEBUG = os.path.exists('/tmp/ospfv3.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -67,6 +58,14 @@ def verify(ospfv3):
return None
verify_route_maps(ospfv3)
+
+ if 'interface' in ospfv3:
+ for ifname, if_config in ospfv3['interface'].items():
+ if 'ifmtu' in if_config:
+ mtu = Interface(ifname).get_mtu()
+ if int(if_config['ifmtu']) > int(mtu):
+ raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"')
+
return None
def generate(ospfv3):
@@ -74,33 +73,16 @@ def generate(ospfv3):
ospfv3['new_frr_config'] = ''
return None
- # render(config) not needed, its only for debug
- render(config_file, 'frr/ospfv3.frr.tmpl', ospfv3)
ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3)
-
return None
def apply(ospfv3):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section('router ospf6', '')
+ frr_cfg.modify_section(r'^interface \S+', '')
+ frr_cfg.modify_section('^router ospf6$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{ospfv3["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, re-run the blank commit x times due to frr-reload
diff --git a/src/conf_mode/protocols_rip.py b/src/conf_mode/protocols_rip.py
index 06d7c6d49..6db5143c5 100755
--- a/src/conf_mode/protocols_rip.py
+++ b/src/conf_mode/protocols_rip.py
@@ -24,24 +24,14 @@ from vyos.configverify import verify_route_maps
from vyos.util import call
from vyos.util import dict_search
from vyos.xml import defaults
-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()
-config_file = r'/tmp/rip.frr'
frr_daemon = 'ripd'
-DEBUG = os.path.exists('/tmp/rip.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -106,8 +96,6 @@ def generate(rip):
rip['new_frr_config'] = ''
return None
- # render(config) not needed, its only for debug
- render(config_file, 'frr/rip.frr.tmpl', rip)
rip['new_frr_config'] = render_to_string('frr/rip.frr.tmpl', rip)
return None
@@ -120,21 +108,6 @@ def apply(rip):
frr_cfg.modify_section(r'interface \S+', '')
frr_cfg.modify_section('router rip', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rip['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{rip["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, rerun the blank commit x times due to frr-reload
diff --git a/src/conf_mode/protocols_ripng.py b/src/conf_mode/protocols_ripng.py
new file mode 100755
index 000000000..8cc5de64a
--- /dev/null
+++ b/src/conf_mode/protocols_ripng.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.configverify import verify_route_maps
+from vyos.util import call
+from vyos.util import dict_search
+from vyos.xml import defaults
+from vyos.template import render_to_string
+from vyos import ConfigError
+from vyos import frr
+from vyos import airbag
+airbag.enable()
+
+frr_daemon = 'ripngd'
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['protocols', 'ripng']
+ ripng = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ # Bail out early if configuration tree does not exist
+ if not conf.exists(base):
+ return ripng
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ # merge in remaining default values
+ ripng = dict_merge(default_values, ripng)
+
+ # We also need some additional information from the config, prefix-lists
+ # and route-maps for instance. They will be used in verify()
+ base = ['policy']
+ tmp = conf.get_config_dict(base, key_mangling=('-', '_'))
+ # Merge policy dict into OSPF dict
+ ripng = dict_merge(tmp, ripng)
+
+ import pprint
+ pprint.pprint(ripng)
+ return ripng
+
+def verify(ripng):
+ if not ripng:
+ return None
+
+ acl_in = dict_search('distribute_list.access_list.in', ripng)
+ if acl_in and acl_in not in (dict_search('policy.access_list6', ripng) or []):
+ raise ConfigError(f'Inbound access-list6 "{acl_in}" does not exist!')
+
+ acl_out = dict_search('distribute_list.access_list.out', ripng)
+ if acl_out and acl_out not in (dict_search('policy.access_list6', ripng) or []):
+ raise ConfigError(f'Outbound access-list6 "{acl_out}" does not exist!')
+
+ prefix_list_in = dict_search('distribute_list.prefix_list.in', ripng)
+ if prefix_list_in and prefix_list_in.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []):
+ raise ConfigError(f'Inbound prefix-list6 "{prefix_list_in}" does not exist!')
+
+ prefix_list_out = dict_search('distribute_list.prefix_list.out', ripng)
+ if prefix_list_out and prefix_list_out.replace('-','_') not in (dict_search('policy.prefix_list6', ripng) or []):
+ raise ConfigError(f'Outbound prefix-list6 "{prefix_list_out}" does not exist!')
+
+ if 'interface' in ripng:
+ for interface, interface_options in ripng['interface'].items():
+ if 'authentication' in interface_options:
+ if {'md5', 'plaintext_password'} <= set(interface_options['authentication']):
+ raise ConfigError('Can not use both md5 and plaintext-password at the same time!')
+ if 'split_horizon' in interface_options:
+ if {'disable', 'poison_reverse'} <= set(interface_options['split_horizon']):
+ raise ConfigError(f'You can not have "split-horizon poison-reverse" enabled ' \
+ f'with "split-horizon disable" for "{interface}"!')
+
+ verify_route_maps(ripng)
+
+def generate(ripng):
+ if not ripng:
+ ripng['new_frr_config'] = ''
+ return None
+
+ ripng['new_frr_config'] = render_to_string('frr/ripng.frr.tmpl', ripng)
+ import pprint
+ pprint.pprint(ripng['new_frr_config'])
+
+ return None
+
+def apply(ripng):
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration(frr_daemon)
+ frr_cfg.modify_section(r'key chain \S+', '')
+ frr_cfg.modify_section(r'interface \S+', '')
+ frr_cfg.modify_section('router ripng', '')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ripng['new_frr_config'])
+ frr_cfg.commit_configuration(frr_daemon)
+
+ # If FRR config is blank, rerun the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if ripng['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration(frr_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/protocols_rpki.py b/src/conf_mode/protocols_rpki.py
index 0b9525caf..75b870b05 100755
--- a/src/conf_mode/protocols_rpki.py
+++ b/src/conf_mode/protocols_rpki.py
@@ -20,7 +20,6 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
@@ -30,17 +29,8 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/rpki.frr'
frr_daemon = 'bgpd'
-DEBUG = os.path.exists('/tmp/rpki.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -90,10 +80,7 @@ def verify(rpki):
return None
def generate(rpki):
- # render(config) not needed, its only for debug
- render(config_file, 'frr/rpki.frr.tmpl', rpki)
rpki['new_frr_config'] = render_to_string('frr/rpki.frr.tmpl', rpki)
-
return None
def apply(rpki):
@@ -102,21 +89,6 @@ def apply(rpki):
frr_cfg.load_configuration(frr_daemon)
frr_cfg.modify_section('rpki', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', rpki['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{rpki["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, re-run the blank commit x times due to frr-reload
diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py
index 62a3fecd7..5d101b33e 100755
--- a/src/conf_mode/protocols_static.py
+++ b/src/conf_mode/protocols_static.py
@@ -19,7 +19,6 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
from vyos.configverify import verify_route_maps
@@ -28,17 +27,8 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/static.frr'
frr_daemon = 'staticd'
-DEBUG = os.path.exists('/tmp/static.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -53,10 +43,7 @@ def verify(static):
return None
def generate(static):
- # render(config) not needed, its only for debug
- render(config_file, 'frr/static.frr.tmpl', static)
static['new_frr_config'] = render_to_string('frr/static.frr.tmpl', static)
-
return None
def apply(static):
@@ -66,21 +53,6 @@ def apply(static):
frr_cfg.modify_section(r'^ip route .*', '')
frr_cfg.modify_section(r'^ipv6 route .*', '')
frr_cfg.add_before(r'(interface .*|line vty)', static['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{static["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, rerun the blank commit x times due to frr-reload
diff --git a/src/conf_mode/protocols_vrf.py b/src/conf_mode/protocols_vrf.py
index 7c32c7013..227e7d5e1 100755
--- a/src/conf_mode/protocols_vrf.py
+++ b/src/conf_mode/protocols_vrf.py
@@ -19,7 +19,6 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.template import render
from vyos.template import render_to_string
from vyos.util import call
from vyos import ConfigError
@@ -27,17 +26,8 @@ from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/vrf.frr'
frr_daemon = 'staticd'
-DEBUG = os.path.exists('/tmp/vrf.debug')
-if DEBUG:
- import logging
- lg = logging.getLogger("vyos.frr")
- lg.setLevel(logging.DEBUG)
- ch = logging.StreamHandler()
- lg.addHandler(ch)
-
def get_config(config=None):
if config:
conf = config
@@ -52,10 +42,7 @@ def verify(vrf):
return None
def generate(vrf):
- # render(config) not needed, its only for debug
- render(config_file, 'frr/vrf.frr.tmpl', vrf)
vrf['new_frr_config'] = render_to_string('frr/vrf.frr.tmpl', vrf)
-
return None
def apply(vrf):
@@ -64,21 +51,6 @@ def apply(vrf):
frr_cfg.load_configuration(frr_daemon)
frr_cfg.modify_section(r'vrf \S+', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', vrf['new_frr_config'])
-
- # Debugging
- if DEBUG:
- from pprint import pprint
- print('')
- print('--------- DEBUGGING ----------')
- pprint(dir(frr_cfg))
- print('Existing config:\n')
- for line in frr_cfg.original_config:
- print(line)
- print(f'Replacement config:\n')
- print(f'{vrf["new_frr_config"]}')
- print(f'Modified config:\n')
- print(f'{frr_cfg}')
-
frr_cfg.commit_configuration(frr_daemon)
# If FRR config is blank, rerun the blank commit x times due to frr-reload
diff --git a/src/migration-scripts/conntrack/1-to-2 b/src/migration-scripts/conntrack/1-to-2
new file mode 100755
index 000000000..4fc88a1ed
--- /dev/null
+++ b/src/migration-scripts/conntrack/1-to-2
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+# Delete "set system conntrack modules gre" option
+
+import sys
+
+from vyos.configtree import ConfigTree
+
+if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+if not config.exists(['system', 'conntrack', 'modules', 'gre']):
+ # Nothing to do
+ sys.exit(0)
+else:
+ # Delete abandoned node
+ config.delete(['system', 'conntrack', 'modules', 'gre'])
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ sys.exit(1)
diff --git a/src/migration-scripts/interfaces/18-to-19 b/src/migration-scripts/interfaces/18-to-19
index 31e253098..06e07572f 100755
--- a/src/migration-scripts/interfaces/18-to-19
+++ b/src/migration-scripts/interfaces/18-to-19
@@ -32,6 +32,20 @@ def migrate_ospf(config, path, interface):
if len(config.list_nodes(path[:-1])) == 0:
config.delete(path[:-1])
+def migrate_ospfv3(config, path, interface):
+ path = path + ['ospfv3']
+ if config.exists(path):
+ new_base = ['protocols', 'ospfv3', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ipv6 ospfv3" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
def migrate_rip(config, path, interface):
path = path + ['rip']
if config.exists(path):
@@ -46,6 +60,20 @@ def migrate_rip(config, path, interface):
if len(config.list_nodes(path[:-1])) == 0:
config.delete(path[:-1])
+def migrate_ripng(config, path, interface):
+ path = path + ['ripng']
+ if config.exists(path):
+ new_base = ['protocols', 'ripng', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ipv6 ripng" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
if __name__ == '__main__':
if (len(argv) < 1):
print("Must specify file name!")
@@ -62,33 +90,52 @@ if __name__ == '__main__':
#
for type in config.list_nodes(['interfaces']):
for interface in config.list_nodes(['interfaces', type]):
- if_base = ['interfaces', type, interface, 'ip']
- migrate_rip(config, if_base, interface)
- migrate_ospf(config, if_base, interface)
+ ip_base = ['interfaces', type, interface, 'ip']
+ ipv6_base = ['interfaces', type, interface, 'ipv6']
+ migrate_rip(config, ip_base, interface)
+ migrate_ripng(config, ipv6_base, interface)
+ migrate_ospf(config, ip_base, interface)
+ migrate_ospfv3(config, ipv6_base, interface)
vif_path = ['interfaces', type, interface, 'vif']
if config.exists(vif_path):
for vif in config.list_nodes(vif_path):
- vif_if_base = vif_path + [vif, 'ip']
- migrate_rip(config, vif_if_base, f'{interface}.{vif}')
- migrate_ospf(config, vif_if_base, f'{interface}.{vif}')
+ vif_ip_base = vif_path + [vif, 'ip']
+ vif_ipv6_base = vif_path + [vif, 'ipv6']
+ ifname = f'{interface}.{vif}'
+
+ migrate_rip(config, vif_ip_base, ifname)
+ migrate_ripng(config, vif_ipv6_base, ifname)
+ migrate_ospf(config, vif_ip_base, ifname)
+ migrate_ospfv3(config, vif_ipv6_base, ifname)
+
vif_s_path = ['interfaces', type, interface, 'vif-s']
if config.exists(vif_s_path):
for vif_s in config.list_nodes(vif_s_path):
- vif_s_if_base = vif_s_path + [vif_s, 'ip']
+ vif_s_ip_base = vif_s_path + [vif_s, 'ip']
+ vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6']
# vif-c interfaces MUST be migrated before their parent vif-s
# interface as the migrate_*() functions delete the path!
vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
if config.exists(vif_c_path):
for vif_c in config.list_nodes(vif_c_path):
- vif_c_if_base = vif_c_path + [vif_c, 'ip']
- migrate_rip(config, vif_c_if_base, f'{interface}.{vif_s}.{vif_c}')
- migrate_ospf(config, vif_c_if_base, f'{interface}.{vif_s}.{vif_c}')
+ vif_c_ip_base = vif_c_path + [vif_c, 'ip']
+ vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6']
+ ifname = f'{interface}.{vif_s}.{vif_c}'
+
+ migrate_rip(config, vif_c_ip_base, ifname)
+ migrate_ripng(config, vif_c_ipv6_base, ifname)
+ migrate_ospf(config, vif_c_ip_base, ifname)
+ migrate_ospfv3(config, vif_c_ipv6_base, ifname)
+
- migrate_rip(config, vif_s_if_base, f'{interface}.{vif_s}')
- migrate_ospf(config, vif_s_if_base, f'{interface}.{vif_s}')
+ ifname = f'{interface}.{vif_s}'
+ migrate_rip(config, vif_s_ip_base, ifname)
+ migrate_ripng(config, vif_s_ipv6_base, ifname)
+ migrate_ospf(config, vif_s_ip_base, ifname)
+ migrate_ospfv3(config, vif_s_ipv6_base, ifname)
try:
with open(file_name, 'w') as f:
diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7
index 3a229b5df..25cf5eebd 100755
--- a/src/migration-scripts/quagga/6-to-7
+++ b/src/migration-scripts/quagga/6-to-7
@@ -17,14 +17,17 @@
# - T3037, BGP address-family ipv6-unicast capability dynamic does not exist in
# FRR, there is only a base, per neighbor dynamic capability, migrate config
-import sys
+from sys import argv
+from sys import exit
from vyos.configtree import ConfigTree
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
-if (len(sys.argv) < 2):
+if (len(argv) < 2):
print("Must specify file name!")
- sys.exit(1)
+ exit(1)
-file_name = sys.argv[1]
+file_name = argv[1]
with open(file_name, 'r') as f:
config_file = f.read()
@@ -34,7 +37,7 @@ config = ConfigTree(config_file)
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ exit(0)
# Check if BGP is actually configured and obtain the ASN
asn_list = config.list_nodes(base)
@@ -46,31 +49,68 @@ if asn_list:
if not config.exists(bgp_base + [neighbor_type]):
continue
for neighbor in config.list_nodes(bgp_base + [neighbor_type]):
+ # T2844 - add IPv4 AFI disable-send-community support
+ send_comm_path = bgp_base + [neighbor_type, neighbor, 'disable-send-community']
+ if config.exists(send_comm_path):
+ new_base = bgp_base + [neighbor_type, neighbor, 'address-family', 'ipv4-unicast']
+ config.set(new_base)
+ config.copy(send_comm_path, new_base + ['disable-send-community'])
+ config.delete(send_comm_path)
+
cap_dynamic = False
+ peer_group = None
for afi in ['ipv4-unicast', 'ipv6-unicast']:
- afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi, 'capability', 'dynamic']
- if config.exists(afi_path):
+ afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi]
+ # Exit loop early if AFI does not exist
+ if not config.exists(afi_path):
+ continue
+
+ cap_path = afi_path + ['capability', 'dynamic']
+ if config.exists(cap_path):
cap_dynamic = True
- config.delete(afi_path)
+ config.delete(cap_path)
+
+ # We have now successfully migrated the address-family
+ # specific dynamic capability to the neighbor/peer-group
+ # level. If this has been the only option under the
+ # address-family nodes, we can clean them up by checking if
+ # no other nodes are left under that tree and if so, delete
+ # the parent.
+ #
+ # We walk from the most inner node to the most outer one.
+ cleanup = -1
+ while len(config.list_nodes(cap_path[:cleanup])) == 0:
+ config.delete(cap_path[:cleanup])
+ cleanup -= 1
+
+ peer_group_path = afi_path + ['peer-group']
+ if config.exists(peer_group_path):
+ if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or
+ (is_ipv6(neighbor) and afi == 'ipv6-unicast')):
+ peer_group = config.return_value(peer_group_path)
+
+ config.delete(peer_group_path)
- # We have now successfully migrated the address-family specific
- # dynamic capability to the neighbor/peer-group level. If this
- # has been the only option under the address-family nodes, we
- # can clean them up by checking if no other nodes are left under
- # that tree and if so, delete the parent.
+ # We have now successfully migrated the address-family
+ # specific peer-group to the neighbor level. If this has
+ # been the only option under the address-family nodes, we
+ # can clean them up by checking if no other nodes are left
+ # under that tree and if so, delete the parent.
#
# We walk from the most inner node to the most outer one.
cleanup = -1
- while len(config.list_nodes(afi_path[:cleanup])) == 0:
- config.delete(afi_path[:cleanup])
+ while len(config.list_nodes(peer_group_path[:cleanup])) == 0:
+ config.delete(peer_group_path[:cleanup])
cleanup -= 1
if cap_dynamic:
config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic'])
+ if peer_group:
+ config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group)
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- sys.exit(1)
+ exit(1)