From 3d3d09d6e5d7350b09709447ed4d7a7790e09b81 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 15 Feb 2021 20:16:02 +0100 Subject: bfd: T3310: implement peer profile support --- data/templates/frr/bfd.frr.tmpl | 29 ++++++- interface-definitions/include/bfd-common.xml.i | 72 ++++++++++++++++++ interface-definitions/protocols-bfd.xml.in | 100 +++++++------------------ smoketest/scripts/cli/test_protocols_bfd.py | 69 +++++++++++++++-- src/conf_mode/protocols_bfd.py | 71 +++++++++--------- 5 files changed, 223 insertions(+), 118 deletions(-) create mode 100644 interface-definitions/include/bfd-common.xml.i diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl index 921d9b0bc..3b3d13f9d 100644 --- a/data/templates/frr/bfd.frr.tmpl +++ b/data/templates/frr/bfd.frr.tmpl @@ -1,15 +1,35 @@ ! bfd +{% 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 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 }} +{% 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 %} +{% if peer_config['echo-mode'] is defined %} echo-mode {% endif %} {% if peer_config.shutdown is defined %} @@ -17,7 +37,8 @@ bfd {% else %} no shutdown {% endif %} - ! + exit {% endfor %} {% endif %} + 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 @@ + + + + Enables the echo transmission mode + + + + + + Configure timer intervals + + + + + Minimum interval of receiving control packets + + 10-60000 + Interval in milliseconds + + + + + + 300 + + + + Minimum interval of transmitting control packets + + 10-60000 + Interval in milliseconds + + + + + + 300 + + + + Multiplier to determine packet loss + + 2-255 + Remote transmission interval will be multiplied by this value + + + + + + 3 + + + + Echo receive transmission interval + + 10-60000 + The minimal echo receive transmission interval that this system is capable of handling + + + + + + + + + + + Disable this peer + + + + diff --git a/interface-definitions/protocols-bfd.xml.in b/interface-definitions/protocols-bfd.xml.in index 6f82a5c2b..cc3c3bf12 100644 --- a/interface-definitions/protocols-bfd.xml.in +++ b/interface-definitions/protocols-bfd.xml.in @@ -11,7 +11,7 @@ - Configures a new BFD peer to listen and talk to + Configures BFD peer to listen and talk to ipv4 BFD peer IPv4 address @@ -26,6 +26,18 @@ + + + Use settings from BFD profile + + protocols bfd profile + + + txt + BFD profile name + + + Bind listener to specified interface/address, mandatory for IPv6 @@ -61,82 +73,28 @@ - - - Configure timer intervals - - - - - Minimum interval of receiving control packets - - 10-60000 - Interval in milliseconds - - - - - - 300 - - - - Minimum interval of transmitting control packets - - 10-60000 - Interval in milliseconds - - - - - - 300 - - - - Multiplier to determine packet loss - - 2-255 - Remote transmission interval will be multiplied by this value - - - - - - 3 - - - - Echo receive transmission interval - - 10-60000 - The minimal echo receive transmission interval that this system is capable of handling - - - - - - - - - - - Disable this peer - - - + #include Allow this BFD peer to not be directly connected - - - Enables the echo transmission mode - - - + + + + + Configure BFD profile used by individual peer + + txt + Name of BFD profile + + + ^[-_a-zA-Z0-9]{1,32}$ + + + + #include 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 7737c6aa1..a43eed504 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -36,54 +36,55 @@ def get_config(config=None): else: conf = Config() base = ['protocols', 'bfd'] - bfd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + 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: - # 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']) 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 not bfd or 'peer' not in bfd: + if not bfd: return None - 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') - - # echo interval can be configured only with enabled echo-mode - if 'interval' in peer_config and 'echo_interval' in peer_config['interval'] and 'echo_mode' not in peer_config: - raise ConfigError('echo-interval can be configured only with enabled echo-mode') + 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 @@ -98,7 +99,7 @@ def apply(bfd): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration() - frr_cfg.modify_section('bfd', '') + 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() -- cgit v1.2.3