diff --git a/netsim/validate/bgp/eos.py b/netsim/validate/bgp/eos.py index 2c6beffb53..7629e5623a 100644 --- a/netsim/validate/bgp/eos.py +++ b/netsim/validate/bgp/eos.py @@ -78,6 +78,57 @@ def valid_bgp_neighbor( return f'Neighbor {n_addr} ({n_id}) is in state {data[n_addr].peerState}' +def show_bgp_neighbor_details(ngb: list, n_id: str, af: str='ipv4', *, activate: str = '', **kwargs: typing.Any) -> str: + n_addr = _common.get_bgp_neighbor_id(ngb,n_id,af) + global af_lookup + if not activate: + return f'bgp neighbors {n_addr} | json' + + if activate not in af_lookup: + raise Exception(f'Unsupported address family {activate}') + + return f'bgp {af_lookup[activate]} neighbors {n_addr} | json' + +def valid_bgp_neighbor_details( + ngb: list, + n_id: str, + af: str = 'ipv4', + state: str = 'Established', + vrf: str = 'default', + activate: str = '', + intf: str = '', + bfd: bool = False) -> str: + _result = global_vars.get_result_dict('_result') + n_addr = _common.get_bgp_neighbor_id(ngb,n_id,af) + + data = check_vrf_data(_result,vrf,'peerList','BGP peers') + + act_err = f' in address family {activate}' if activate else '' + found = next(item for item in data if item.peerAddress == n_addr) + + if not found: + result = f'The router has no BGP neighbor with {af} address {n_addr} ({n_id}){act_err}' + if state == 'missing': + return result + else: + raise Exception(result) + + if not state == found.state: + result = f'The neighbor {n_addr} ({n_id}){act_err} is in state {found.state}' + if state == 'missing' and data[0].state != 'Established': + return result + else: + raise Exception(f'{result} (expected {state})') + + if not bfd: + return result + + result = f'The neighbor {n_addr} ({n_id}){act_err} is in BFD state {found.bfdState}' + if not found.bfdState == 3: + raise Exception(f'{result} (expected 3 - Up)') + + return result + """ BGP prefix checks, starting with 'get a BGP prefix from JSON results' """ diff --git a/netsim/validate/bgp/frr.py b/netsim/validate/bgp/frr.py index 3bc728d2b4..b8d1bf1a9a 100644 --- a/netsim/validate/bgp/frr.py +++ b/netsim/validate/bgp/frr.py @@ -89,7 +89,9 @@ def show_bgp_neighbor_details(ngb: list, n_id: str, af: str = 'ipv4', **kwargs: def valid_bgp_neighbor_details( ngb: list, n_id: str, - af: str = 'ipv4',**kwargs: typing.Any) -> str: + af: str = 'ipv4', + bfd: bool = False, + **kwargs: typing.Any) -> str: _result = global_vars.get_result_dict('_result') n_addr = _common.get_bgp_neighbor_id(ngb,n_id,af) @@ -101,6 +103,13 @@ def valid_bgp_neighbor_details( if data[k] != v: raise Exception(f'{k} expected value {v} actual {data[k]}') + if bfd: + if data.peerBfdInfo: + if data.peerBfdInfo.status != 'Up': + raise Exception(f'BGP {k} expected value UP actual {data.peerBfdInfo.status}') + else: + raise Exception(f'No BFD information for BGP peer {n_id}') + return f'All specified BGP neighbor parameters have the expected values' """ diff --git a/tests/integration/bgp.session/13-bfd.yml b/tests/integration/bgp.session/13-bfd.yml index a3e5c6e5e8..949907f0de 100644 --- a/tests/integration/bgp.session/13-bfd.yml +++ b/tests/integration/bgp.session/13-bfd.yml @@ -2,6 +2,14 @@ message: | This lab tests the BGP BFD functionality. The EBGP session between the probe and the lab device should trigger a BFD session between them. + The final test disables BFD on the probe to verify that the BGP session is + promptly torn down when BFD fails. It catches the FRR 10.5.1 regression that + kept BGP sessions with default timers Established for 30 seconds after BFD + failure. To run the test against FRR 10.5.1: + + netlab up tests/integration/bgp.session/13-bfd.yml --validate \ + --set defaults.devices.frr.clab.image=quay.io/frrouting/frr:10.5.1 + plugin: [ bgp.session ] module: [ bgp, bfd ] @@ -16,13 +24,13 @@ groups: nodes: dut: bgp.as: 65000 + config: [ static_bfd ] x1: bgp.as: 65100 - config: [ static_bfd ] + bgp.bfd: True links: - dut: - bgp.bfd: True x1: pool: p2p # Force addressing to come from the p2p pool, else with host devices like bird it becomes a lan @@ -34,6 +42,13 @@ validate: nodes: [ x1 ] plugin: bgp_neighbor(node.bgp.neighbors,'dut') + bgp_bfd_v4: + description: Check IPv4 EBGP sessions with DUT + wait_msg: Waiting for BFD EBGP session data + wait: ebgp_session + nodes: [ x1 ] + plugin: bgp_neighbor_details(node.bgp.neighbors,'dut',bfd='True') + bfd_v4: description: Check BFD peer on X1 wait_msg: Waiting for BFD to start @@ -41,6 +56,32 @@ validate: nodes: [ x1 ] show: eos: "bfd peers | json" + frr: "bfd peers json" valid: eos: >- vrfs.default.ipv4Neighbors["10.1.0.1"].peers.Ethernet1.types.normal.peerStats["10.1.0.2"].status == "up" + frr: >- + result[0].status == "up" + + drop_bfd_x1: + description: Disable BFD on X1 BGP neighbor + nodes: [ x1 ] + devices: [ frr ] + exec: >- + vtysh -c "configure terminal" -c "router bgp 65100" -c "no neighbor 10.1.0.1 bfd" + pass: BFD disabled on X1 BGP neighbor + + bfd_bgp_down: + description: BGP session should go down promptly after BFD failure + wait_msg: Waiting for BFD failure to tear down BGP + wait: bfd_init + nodes: [ x1 ] + plugin: bgp_neighbor(node.bgp.neighbors,'dut',state=['Idle','Active','Connect']) + + restore_bfd_x1: + description: Restore BFD on X1 BGP neighbor + nodes: [ x1 ] + devices: [ frr ] + exec: >- + vtysh -c "configure terminal" -c "router bgp 65100" -c "neighbor 10.1.0.1 bfd" + pass: BFD restored on X1 BGP neighbor