summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2021-02-15 13:44:27 -0600
committerGitHub <noreply@github.com>2021-02-15 13:44:27 -0600
commit96656b1a7bb7166c923a45d1333d7e7c7add5514 (patch)
tree10d0a0f4eef5b68a10e24a438264be06c4f72024
parente9f581efe834dc27e78acff4a331fb9242440de5 (diff)
parent3d3d09d6e5d7350b09709447ed4d7a7790e09b81 (diff)
downloadvyos-1x-96656b1a7bb7166c923a45d1333d7e7c7add5514.tar.gz
vyos-1x-96656b1a7bb7166c923a45d1333d7e7c7add5514.zip
Merge pull request #732 from c-po/t3310-bfd
bfd: T3310: migrate to get_config_dict() and FRR reload
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/frr/bfd.frr.tmpl56
-rw-r--r--interface-definitions/include/bfd-common.xml.i72
-rw-r--r--interface-definitions/protocols-bfd.xml.in100
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py69
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py230
6 files changed, 273 insertions, 255 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index e50dbf1b2..aabd7232e 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -32,6 +32,7 @@
"nat66.py",
"ntp.py",
"policy-local-route.py",
+"protocols_bfd.py",
"protocols_bgp.py",
"protocols_igmp.py",
"protocols_isis.py",
diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl
index 9e5ad3379..3b3d13f9d 100644
--- a/data/templates/frr/bfd.frr.tmpl
+++ b/data/templates/frr/bfd.frr.tmpl
@@ -1,22 +1,44 @@
!
bfd
-{% for peer in old_peers %}
- no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
-
-{% endfor %}
-!
-{% for peer in new_peers %}
- peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
-
- detect-multiplier {{ peer.multiplier }}
- receive-interval {{ peer.rx_interval }}
- transmit-interval {{ peer.tx_interval }}
-{% if peer.echo_mode %}
- echo-mode
+{% if profile is defined and profile is not none %}
+{% for profile_name, profile_config in profile.items() %}
+ profile {{ profile_name }}
+ detect-multiplier {{ profile_config.interval.multiplier }}
+ receive-interval {{ profile_config.interval.receive }}
+ transmit-interval {{ profile_config.interval.transmit }}
+{% if profile_config.interval['echo-interval'] is defined and profile_config.interval['echo-interval'] is not none %}
+ echo-interval {{ profile_config.interval['echo-interval'] }}
+{% endif %}
+{% if profile_config['echo-mode'] is defined %}
+ echo-mode
+{% endif %}
+{% if profile_config.shutdown is defined %}
+ shutdown
+{% else %}
+ no shutdown
+{% endif %}
+ exit
+{% endfor %}
{% endif %}
-{% if peer.echo_interval != '' %}
- echo-interval {{ peer.echo_interval }}
+{% if peer is defined and peer is not none %}
+{% for peer_name, peer_config in peer.items() %}
+ peer {{ peer_name }}{{ ' multihop' if peer_config.multihop is defined }}{{ ' local-address ' + peer_config.source.address if peer_config.source is defined and peer_config.source.address is defined }}{{ ' interface ' + peer_config.source.interface if peer_config.source is defined and peer_config.source.interface is defined }}
+ detect-multiplier {{ peer_config.interval.multiplier }}
+ receive-interval {{ peer_config.interval.receive }}
+ transmit-interval {{ peer_config.interval.transmit }}
+{% if peer_config.interval['echo-interval'] is defined and peer_config.interval['echo-interval'] is not none %}
+ echo-interval {{ peer_config.interval['echo-interval'] }}
+{% endif %}
+{% if peer_config['echo-mode'] is defined %}
+ echo-mode
+{% endif %}
+{% if peer_config.shutdown is defined %}
+ shutdown
+{% else %}
+ no shutdown
+{% endif %}
+ exit
+{% endfor %}
{% endif %}
- {% if not peer.shutdown %}no {% endif %}shutdown
-{% endfor %}
+ exit
!
diff --git a/interface-definitions/include/bfd-common.xml.i b/interface-definitions/include/bfd-common.xml.i
new file mode 100644
index 000000000..ff73e4b20
--- /dev/null
+++ b/interface-definitions/include/bfd-common.xml.i
@@ -0,0 +1,72 @@
+<!-- included start from bfd-common.xml.i -->
+<leafNode name="echo-mode">
+ <properties>
+ <help>Enables the echo transmission mode</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<node name="interval">
+ <properties>
+ <help>Configure timer intervals</help>
+ </properties>
+ <children>
+ <leafNode name="receive">
+ <properties>
+ <help>Minimum interval of receiving control packets</help>
+ <valueHelp>
+ <format>10-60000</format>
+ <description>Interval in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-60000"/>
+ </constraint>
+ </properties>
+ <defaultValue>300</defaultValue>
+ </leafNode>
+ <leafNode name="transmit">
+ <properties>
+ <help>Minimum interval of transmitting control packets</help>
+ <valueHelp>
+ <format>10-60000</format>
+ <description>Interval in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-60000"/>
+ </constraint>
+ </properties>
+ <defaultValue>300</defaultValue>
+ </leafNode>
+ <leafNode name="multiplier">
+ <properties>
+ <help>Multiplier to determine packet loss</help>
+ <valueHelp>
+ <format>2-255</format>
+ <description>Remote transmission interval will be multiplied by this value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 2-255"/>
+ </constraint>
+ </properties>
+ <defaultValue>3</defaultValue>
+ </leafNode>
+ <leafNode name="echo-interval">
+ <properties>
+ <help>Echo receive transmission interval</help>
+ <valueHelp>
+ <format>10-60000</format>
+ <description>The minimal echo receive transmission interval that this system is capable of handling</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 10-60000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<leafNode name="shutdown">
+ <properties>
+ <help>Disable this peer</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in
index 8900e7955..cc3c3bf12 100644
--- a/interface-definitions/protocols-bfd.xml.in
+++ b/interface-definitions/protocols-bfd.xml.in
@@ -11,7 +11,7 @@
<children>
<tagNode name="peer">
<properties>
- <help>Configures a new BFD peer to listen and talk to</help>
+ <help>Configures BFD peer to listen and talk to</help>
<valueHelp>
<format>ipv4</format>
<description>BFD peer IPv4 address</description>
@@ -26,6 +26,18 @@
</constraint>
</properties>
<children>
+ <leafNode name="profile">
+ <properties>
+ <help>Use settings from BFD profile</help>
+ <completionHelp>
+ <path>protocols bfd profile</path>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>BFD profile name</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
<node name="source">
<properties>
<help>Bind listener to specified interface/address, mandatory for IPv6</help>
@@ -42,6 +54,9 @@
<leafNode name="address">
<properties>
<help>Local address to bind our peer listener to</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ </completionHelp>
<valueHelp>
<format>ipv4</format>
<description>Local IPv4 address used to connect to the peer</description>
@@ -58,79 +73,28 @@
</leafNode>
</children>
</node>
- <node name="interval">
- <properties>
- <help>Configure timer intervals</help>
- </properties>
- <children>
- <leafNode name="receive">
- <properties>
- <help>Minimum interval of receiving control packets</help>
- <valueHelp>
- <format>10-60000</format>
- <description>Interval in milliseconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 10-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="transmit">
- <properties>
- <help>Minimum interval of transmitting control packets</help>
- <valueHelp>
- <format>10-60000</format>
- <description>Interval in milliseconds</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 10-60000"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="multiplier">
- <properties>
- <help>Multiplier to determine packet loss</help>
- <valueHelp>
- <format>2-255</format>
- <description>Remote transmission interval will be multiplied by this value</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 2-255"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="echo-interval">
- <properties>
- <help>Echo receive transmission interval</help>
- <valueHelp>
- <format>10-60000</format>
- <description>The minimal echo receive transmission interval that this system is capable of handling</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 10-60000"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="shutdown">
- <properties>
- <help>Disable this peer</help>
- <valueless/>
- </properties>
- </leafNode>
+ #include <include/bfd-common.xml.i>
<leafNode name="multihop">
<properties>
<help>Allow this BFD peer to not be directly connected</help>
<valueless/>
</properties>
</leafNode>
- <leafNode name="echo-mode">
- <properties>
- <help>Enables the echo transmission mode</help>
- <valueless/>
- </properties>
- </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="profile">
+ <properties>
+ <help>Configure BFD profile used by individual peer</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of BFD profile</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9]{1,32}$</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/bfd-common.xml.i>
</children>
</tagNode>
</children>
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 996a54a9d..80e5daa7c 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -26,7 +26,7 @@ PROCESS_NAME = 'bfdd'
base_path = ['protocols', 'bfd']
dum_if = 'dum1001'
-neighbor_config = {
+peers = {
'192.0.2.10' : {
'intv_rx' : '500',
'intv_tx' : '600',
@@ -36,7 +36,7 @@ neighbor_config = {
'192.0.2.20' : {
'echo_mode' : '',
'intv_echo' : '100',
- 'intv_mult' : '111',
+ 'intv_mult' : '100',
'intv_rx' : '222',
'intv_tx' : '333',
'shutdown' : '',
@@ -52,20 +52,35 @@ neighbor_config = {
},
}
+profiles = {
+ 'foo' : {
+ 'echo_mode' : '',
+ 'intv_echo' : '100',
+ 'intv_mult' : '101',
+ 'intv_rx' : '222',
+ 'intv_tx' : '333',
+ 'shutdown' : '',
+ },
+ 'bar' : {
+ 'intv_mult' : '102',
+ 'intv_rx' : '444',
+ },
+}
+
def getFRRconfig():
return cmd('vtysh -c "show run" | sed -n "/^bfd/,/^!/p"')
def getBFDPeerconfig(peer):
return cmd(f'vtysh -c "show run" | sed -n "/^ {peer}/,/^!/p"')
+def getBFDProfileconfig(profile):
+ return cmd(f'vtysh -c "show run" | sed -n "/^ {profile}/,/^!/p"')
+
class TestProtocolsBFD(unittest.TestCase):
def setUp(self):
self.session = ConfigSession(os.getpid())
- self.session.set(['interfaces', 'dummy', dum_if, 'address', '192.0.2.1/24'])
- self.session.set(['interfaces', 'dummy', dum_if, 'address', '2001:db8::1/64'])
def tearDown(self):
- self.session.delete(['interfaces', 'dummy', dum_if])
self.session.delete(base_path)
self.session.commit()
del self.session
@@ -73,8 +88,8 @@ class TestProtocolsBFD(unittest.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
- def test_bfd_simple(self):
- for peer, peer_config in neighbor_config.items():
+ def test_bfd_peer(self):
+ for peer, peer_config in peers.items():
if 'echo_mode' in peer_config:
self.session.set(base_path + ['peer', peer, 'echo-mode'])
if 'intv_echo' in peer_config:
@@ -99,7 +114,7 @@ class TestProtocolsBFD(unittest.TestCase):
# Verify FRR bgpd configuration
frrconfig = getFRRconfig()
- for peer, peer_config in neighbor_config.items():
+ for peer, peer_config in peers.items():
tmp = f'peer {peer}'
if 'multihop' in peer_config:
tmp += f' multihop'
@@ -124,5 +139,43 @@ class TestProtocolsBFD(unittest.TestCase):
if 'shutdown' not in peer_config:
self.assertIn(f' no shutdown', peerconfig)
+ def test_bfd_profile(self):
+ peer = '192.0.2.10'
+
+ for profile, profile_config in profiles.items():
+ if 'echo_mode' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'echo-mode'])
+ if 'intv_echo' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'interval', 'echo-interval', profile_config["intv_echo"]])
+ if 'intv_mult' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'interval', 'multiplier', profile_config["intv_mult"]])
+ if 'intv_rx' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]])
+ if 'intv_tx' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]])
+ if 'shutdown' in profile_config:
+ self.session.set(base_path + ['profile', profile, 'shutdown'])
+
+ self.session.set(base_path + ['peer', peer, 'profile', list(profiles)[0]])
+
+ # commit changes
+ self.session.commit()
+
+ # Verify FRR bgpd configuration
+ for profile, profile_config in profiles.items():
+ config = getBFDProfileconfig(f'profile {profile}')
+ if 'echo_mode' in profile_config:
+ self.assertIn(f' echo-mode', config)
+ if 'intv_echo' in profile_config:
+ self.assertIn(f' echo-interval {profile_config["intv_echo"]}', config)
+ if 'intv_mult' in profile_config:
+ self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config)
+ if 'intv_rx' in profile_config:
+ self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config)
+ if 'intv_tx' in profile_config:
+ self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
+ if 'shutdown' not in profile_config:
+ self.assertIn(f' no shutdown', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
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