From 53ca591dbbf93cf2c5b0d4f97b6e51587d79bc73 Mon Sep 17 00:00:00 2001 From: VitthalMagadum Date: Thu, 14 Nov 2024 08:12:12 -0500 Subject: [PATCH 1/6] Refactor: VerifyBFDPeersRegProtocols, VerifyBFDPeersIntervals, VerifyBFDSpecificPeers --- anta/input_models/bfd.py | 39 ++++++++++ anta/tests/bfd.py | 163 +++++++++++++++++---------------------- 2 files changed, 109 insertions(+), 93 deletions(-) create mode 100644 anta/input_models/bfd.py diff --git a/anta/input_models/bfd.py b/anta/input_models/bfd.py new file mode 100644 index 000000000..d9fea199f --- /dev/null +++ b/anta/input_models/bfd.py @@ -0,0 +1,39 @@ +# Copyright (c) 2023-2024 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +"""Module containing input models for bfd tests.""" + +from __future__ import annotations + +from ipaddress import IPv4Address +from typing import Any +from warnings import warn + +from pydantic import BaseModel, ConfigDict + +from anta.custom_types import BfdInterval, BfdMultiplier, BfdProtocol + + +class BFDPeer(BaseModel): + """BFD (Bidirectional Forwarding Detection) model representing the peer details. + + Only IPv4 peers are supported for now. + """ + + model_config = ConfigDict(extra="forbid") + peer_address: IPv4Address + """IPv4 address of a BFD peer.""" + vrf: str = "default" + """Optional VRF for the BFD peer. Defaults to `default`.""" + tx_interval: BfdInterval | None = None + """Tx interval of BFD peer in milliseconds. Required field in the `VerifyBFDPeersIntervals` test.""" + rx_interval: BfdInterval | None = None + """Rx interval of BFD peer in milliseconds. Required field in the `VerifyBFDPeersIntervals` test.""" + multiplier: BfdMultiplier | None = None + """Multiplier of BFD peer. Required field in the `VerifyBFDPeersIntervals` test.""" + protocols: list[BfdProtocol] | None = None + """List of protocols to be verified. Required field in the `VerifyBFDPeersRegProtocols` test.""" + + def __str__(self) -> str: + """Return a human-readable string representation of the BFDPeer for reporting.""" + return f"Peer: {self.peer_address} VRF: {self.vrf}" diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py index b48cf27c0..aa60ff998 100644 --- a/anta/tests/bfd.py +++ b/anta/tests/bfd.py @@ -8,12 +8,11 @@ from __future__ import annotations from datetime import datetime, timezone -from ipaddress import IPv4Address -from typing import TYPE_CHECKING, Any, ClassVar +from typing import TYPE_CHECKING, ClassVar -from pydantic import BaseModel, Field +from pydantic import Field -from anta.custom_types import BfdInterval, BfdMultiplier, BfdProtocol +from anta.input_models.bfd import BFDPeer from anta.models import AntaCommand, AntaTest from anta.tools import get_value @@ -22,12 +21,24 @@ class VerifyBFDSpecificPeers(AntaTest): - """Verifies if the IPv4 BFD peer's sessions are UP and remote disc is non-zero in the specified VRF. + """Verifies if the IPv4 BFD peer sessions are in the UP state and the remote discriminator (disc) is non-zero within the specified VRF. + + This test performs the following checks for each specified peer: + + 1. Confirms that the specified VRF is configured. + 2. Verifies that the peer exists in the BFD configuration. + 3. For each specified BFD peer: + - Validates that the state is up + - Confirms that the remote discriminator identifier is non-zero. Expected Results ---------------- - * Success: The test will pass if IPv4 BFD peers are up and remote disc is non-zero in the specified VRF. - * Failure: The test will fail if IPv4 BFD peers are not found, the status is not UP or remote disc is zero in the specified VRF. + * Success: If all of the following conditions are met: + - All specified peers are found in the BFD configuration within the specified VRF. + - All BFD peers are up and remote disc is non-zero. + * Failure: If any of the following occur: + - A specified peer is not found in the BFD configuration within the specified VRF. + - Any BFD peer session is not up or the remote discriminator identifier is zero. Examples -------- @@ -50,20 +61,13 @@ class Input(AntaTest.Input): """Input model for the VerifyBFDSpecificPeers test.""" bfd_peers: list[BFDPeer] - """List of IPv4 BFD peers.""" - - class BFDPeer(BaseModel): - """Model for an IPv4 BFD peer.""" - - peer_address: IPv4Address - """IPv4 address of a BFD peer.""" - vrf: str = "default" - """Optional VRF for BFD peer. If not provided, it defaults to `default`.""" + """List of IPv4 BFD""" + BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyBFDSpecificPeers.""" - failures: dict[Any, Any] = {} + self.result.is_success() # Iterating over BFD peers for bfd_peer in self.inputs.bfd_peers: @@ -77,31 +81,33 @@ def test(self) -> None: # Check if BFD peer configured if not bfd_output: - failures[peer] = {vrf: "Not Configured"} + self.result.is_failure(f"{bfd_peer} - Not found") continue # Check BFD peer status and remote disc - if not (bfd_output.get("status") == "up" and bfd_output.get("remoteDisc") != 0): - failures[peer] = { - vrf: { - "status": bfd_output.get("status"), - "remote_disc": bfd_output.get("remoteDisc"), - } - } - - if not failures: - self.result.is_success() - else: - self.result.is_failure(f"Following BFD peers are not configured, status is not up or remote disc is zero:\n{failures}") + state = bfd_output.get("status") + remote_disc = bfd_output.get("remoteDisc") + if not (state == "up" and remote_disc != 0): + self.result.is_failure(f"{bfd_peer} - Session not properly established; State: {state} Remote Discriminator: {remote_disc}") class VerifyBFDPeersIntervals(AntaTest): """Verifies the timers of the IPv4 BFD peers in the specified VRF. + This test performs the following checks for each specified peer: + + 1. Confirms that the specified VRF is configured. + 2. Verifies that the peer exists in the BFD configuration. + 3. Confirms that BFD peer is correctly configured with the `Transmit interval, Receive interval and Multiplier`. + Expected Results ---------------- - * Success: The test will pass if the timers of the IPv4 BFD peers are correct in the specified VRF. - * Failure: The test will fail if the IPv4 BFD peers are not found or their timers are incorrect in the specified VRF. + * Success: If all of the following conditions are met: + - All specified peers are found in the BFD configuration within the specified VRF. + - All BFD peers are correctly configured with the `Transmit interval, Receive interval and Multiplier`. + * Failure: If any of the following occur: + - A specified peer is not found in the BFD configuration within the specified VRF. + - Any BFD peer not correctly configured with the `Transmit interval, Receive interval and Multiplier`. Examples -------- @@ -129,34 +135,21 @@ class Input(AntaTest.Input): """Input model for the VerifyBFDPeersIntervals test.""" bfd_peers: list[BFDPeer] - """List of BFD peers.""" - - class BFDPeer(BaseModel): - """Model for an IPv4 BFD peer.""" - - peer_address: IPv4Address - """IPv4 address of a BFD peer.""" - vrf: str = "default" - """Optional VRF for BFD peer. If not provided, it defaults to `default`.""" - tx_interval: BfdInterval - """Tx interval of BFD peer in milliseconds.""" - rx_interval: BfdInterval - """Rx interval of BFD peer in milliseconds.""" - multiplier: BfdMultiplier - """Multiplier of BFD peer.""" + """List of IPv4 BFD""" + BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyBFDPeersIntervals.""" - failures: dict[Any, Any] = {} + self.result.is_success() # Iterating over BFD peers - for bfd_peers in self.inputs.bfd_peers: - peer = str(bfd_peers.peer_address) - vrf = bfd_peers.vrf - tx_interval = bfd_peers.tx_interval - rx_interval = bfd_peers.rx_interval - multiplier = bfd_peers.multiplier + for bfd_peer in self.inputs.bfd_peers: + peer = str(bfd_peer.peer_address) + vrf = bfd_peer.vrf + tx_interval = bfd_peer.tx_interval + rx_interval = bfd_peer.rx_interval + multiplier = bfd_peer.multiplier # Check if BFD peer configured bfd_output = get_value( @@ -165,7 +158,7 @@ def test(self) -> None: separator="..", ) if not bfd_output: - failures[peer] = {vrf: "Not Configured"} + self.result.is_failure(f"{bfd_peer} - Not found") continue # Convert interval timer(s) into milliseconds to be consistent with the inputs. @@ -173,23 +166,13 @@ def test(self) -> None: op_tx_interval = bfd_details.get("operTxInterval") // 1000 op_rx_interval = bfd_details.get("operRxInterval") // 1000 detect_multiplier = bfd_details.get("detectMult") - intervals_ok = op_tx_interval == tx_interval and op_rx_interval == rx_interval and detect_multiplier == multiplier # Check timers of BFD peer + intervals_ok = op_tx_interval == tx_interval and op_rx_interval == rx_interval and detect_multiplier == multiplier if not intervals_ok: - failures[peer] = { - vrf: { - "tx_interval": op_tx_interval, - "rx_interval": op_rx_interval, - "multiplier": detect_multiplier, - } - } - - # Check if any failures - if not failures: - self.result.is_success() - else: - self.result.is_failure(f"Following BFD peers are not configured or timers are not correct:\n{failures}") + self.result.is_failure( + f"{bfd_peer} - Incorrect timers; Transmit interval: {op_tx_interval}, Receive interval: {op_rx_interval}, Multiplier: {detect_multiplier}" + ) class VerifyBFDPeersHealth(AntaTest): @@ -283,12 +266,22 @@ def test(self) -> None: class VerifyBFDPeersRegProtocols(AntaTest): - """Verifies that IPv4 BFD peer(s) have the specified protocol(s) registered. + """Verifies the registered routing protocol of the IPv4 BFD peers in the specified VRF. + + This test performs the following checks for each specified peer: + + 1. Confirms that the specified VRF is configured. + 2. Verifies that the peer exists in the BFD configuration. + 3. Confirms that BFD peer is correctly configured with the routing protocol. Expected Results ---------------- - * Success: The test will pass if IPv4 BFD peers are registered with the specified protocol(s). - * Failure: The test will fail if IPv4 BFD peers are not found or the specified protocol(s) are not registered for the BFD peer(s). + * Success: If all of the following conditions are met: + - All specified peers are found in the BFD configuration within the specified VRF. + - All BFD peers are correctly configured with the `routing protocol`. + * Failure: If any of the following occur: + - A specified peer is not found in the BFD configuration within the specified VRF. + - Any BFD peer not correctly configured with the `routing protocol`. Examples -------- @@ -310,23 +303,13 @@ class Input(AntaTest.Input): """Input model for the VerifyBFDPeersRegProtocols test.""" bfd_peers: list[BFDPeer] - """List of IPv4 BFD peers.""" - - class BFDPeer(BaseModel): - """Model for an IPv4 BFD peer.""" - - peer_address: IPv4Address - """IPv4 address of a BFD peer.""" - vrf: str = "default" - """Optional VRF for BFD peer. If not provided, it defaults to `default`.""" - protocols: list[BfdProtocol] - """List of protocols to be verified.""" + """List of IPv4 BFD""" + BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyBFDPeersRegProtocols.""" - # Initialize failure messages - failures: dict[Any, Any] = {} + self.result.is_success() # Iterating over BFD peers, extract the parameters and command output for bfd_peer in self.inputs.bfd_peers: @@ -341,16 +324,10 @@ def test(self) -> None: # Check if BFD peer configured if not bfd_output: - failures[peer] = {vrf: "Not Configured"} + self.result.is_failure(f"{bfd_peer} - Not found") continue # Check registered protocols difference = set(protocols) - set(get_value(bfd_output, "peerStatsDetail.apps")) - if difference: - failures[peer] = {vrf: sorted(difference)} - - if not failures: - self.result.is_success() - else: - self.result.is_failure(f"The following BFD peers are not configured or have non-registered protocol(s):\n{failures}") + self.result.is_failure(f"{bfd_peer} - `{','.join(difference)}` routing protocols not configured") From 7f05881e55e6a866fb898b0a1b10d4be8f1e2ff4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:17:54 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- anta/input_models/bfd.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/anta/input_models/bfd.py b/anta/input_models/bfd.py index d9fea199f..eeb3acb8d 100644 --- a/anta/input_models/bfd.py +++ b/anta/input_models/bfd.py @@ -6,8 +6,6 @@ from __future__ import annotations from ipaddress import IPv4Address -from typing import Any -from warnings import warn from pydantic import BaseModel, ConfigDict @@ -16,7 +14,7 @@ class BFDPeer(BaseModel): """BFD (Bidirectional Forwarding Detection) model representing the peer details. - + Only IPv4 peers are supported for now. """ From b78abd4c2048efa4a443337ef635e87a74ff58e2 Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Sat, 16 Nov 2024 07:20:53 -0500 Subject: [PATCH 3/6] Updated unit tests and docs --- anta/tests/bfd.py | 47 +++++++++++------------------- docs/api/tests.bfd.md | 16 ++++++++++ tests/units/anta_tests/test_bfd.py | 40 +++++++++++++------------ 3 files changed, 54 insertions(+), 49 deletions(-) diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py index aa60ff998..7066a53d3 100644 --- a/anta/tests/bfd.py +++ b/anta/tests/bfd.py @@ -178,16 +178,20 @@ def test(self) -> None: class VerifyBFDPeersHealth(AntaTest): """Verifies the health of IPv4 BFD peers across all VRFs. - It checks that no BFD peer is in the down state and that the discriminator value of the remote system is not zero. + This test performs the following checks for BFD peers across all VRFs: - Optionally, it can also verify that BFD peers have not been down before a specified threshold of hours. + 1. Validates that the state is up + 2. Confirms that the remote discriminator identifier is non-zero. + 3. Optionally verifies that the peer have not been down before a specified threshold of hours Expected Results ---------------- - * Success: The test will pass if all IPv4 BFD peers are up, the discriminator value of each remote system is non-zero, - and the last downtime of each peer is above the defined threshold. - * Failure: The test will fail if any IPv4 BFD peer is down, the discriminator value of any remote system is zero, - or the last downtime of any peer is below the defined threshold. + * Success: If all of the following conditions are met: + - All BFD peers across the VRFs are up and remote disc is non-zero. + - Last downtime of each peer is above the defined threshold. + * Failure: If any of the following occur: + - Any BFD peer session is not up or the remote discriminator identifier is zero. + - Last downtime of any peer is below the defined threshold. Examples -------- @@ -214,18 +218,13 @@ class Input(AntaTest.Input): @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyBFDPeersHealth.""" - # Initialize failure strings - down_failures = [] - up_failures = [] + self.result.is_success() # Extract the current timestamp and command output clock_output = self.instance_commands[1].json_output current_timestamp = clock_output["utcTime"] bfd_output = self.instance_commands[0].json_output - # set the initial result - self.result.is_success() - # Check if any IPv4 BFD peer is configured ipv4_neighbors_exist = any(vrf_data["ipv4Neighbors"] for vrf_data in bfd_output["vrfs"].values()) if not ipv4_neighbors_exist: @@ -238,31 +237,19 @@ def test(self) -> None: for peer_data in neighbor_data["peerStats"].values(): peer_status = peer_data["status"] remote_disc = peer_data["remoteDisc"] - remote_disc_info = f" with remote disc {remote_disc}" if remote_disc == 0 else "" last_down = peer_data["lastDown"] hours_difference = ( datetime.fromtimestamp(current_timestamp, tz=timezone.utc) - datetime.fromtimestamp(last_down, tz=timezone.utc) ).total_seconds() / 3600 - # Check if peer status is not up - if peer_status != "up": - down_failures.append(f"{peer} is {peer_status} in {vrf} VRF{remote_disc_info}.") + if not (peer_status == "up" and remote_disc != 0): + self.result.is_failure( + f"Peer: {peer} VRF: {vrf} - Session not properly established; State: {peer_status} Remote Discriminator: {remote_disc}" + ) # Check if the last down is within the threshold - elif self.inputs.down_threshold and hours_difference < self.inputs.down_threshold: - up_failures.append(f"{peer} in {vrf} VRF was down {round(hours_difference)} hours ago{remote_disc_info}.") - - # Check if remote disc is 0 - elif remote_disc == 0: - up_failures.append(f"{peer} in {vrf} VRF has remote disc {remote_disc}.") - - # Check if there are any failures - if down_failures: - down_failures_str = "\n".join(down_failures) - self.result.is_failure(f"Following BFD peers are not up:\n{down_failures_str}") - if up_failures: - up_failures_str = "\n".join(up_failures) - self.result.is_failure(f"\nFollowing BFD peers were down:\n{up_failures_str}") + if self.inputs.down_threshold and hours_difference < self.inputs.down_threshold: + self.result.is_failure(f"Peer: {peer} VRF: {vrf} - Unstable session, Recent failure {round(hours_difference)} hours ago") class VerifyBFDPeersRegProtocols(AntaTest): diff --git a/docs/api/tests.bfd.md b/docs/api/tests.bfd.md index 719466e6d..ee950875d 100644 --- a/docs/api/tests.bfd.md +++ b/docs/api/tests.bfd.md @@ -7,7 +7,10 @@ anta_title: ANTA catalog for BFD tests ~ that can be found in the LICENSE file. --> +# Tests + ::: anta.tests.bfd + options: show_root_heading: false show_root_toc_entry: false @@ -18,3 +21,16 @@ anta_title: ANTA catalog for BFD tests filters: - "!test" - "!render" + +# Input models + +::: anta.input_models.bfd + + options: + show_root_heading: false + show_root_toc_entry: false + show_bases: false + merge_init_into_class: false + anta_hide_test_module_description: true + show_labels: true + filters: ["!^__str__"] diff --git a/tests/units/anta_tests/test_bfd.py b/tests/units/anta_tests/test_bfd.py index 9bd64656c..76c973d32 100644 --- a/tests/units/anta_tests/test_bfd.py +++ b/tests/units/anta_tests/test_bfd.py @@ -107,8 +107,8 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers are not configured or timers are not correct:\n" - "{'192.0.255.7': {'CS': 'Not Configured'}, '192.0.255.70': {'MGMT': 'Not Configured'}}" + "Peer: 192.0.255.7 VRF: CS - Not found", + "Peer: 192.0.255.70 VRF: MGMT - Not found", ], }, }, @@ -160,9 +160,8 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers are not configured or timers are not correct:\n" - "{'192.0.255.7': {'default': {'tx_interval': 1300, 'rx_interval': 1200, 'multiplier': 4}}, " - "'192.0.255.70': {'MGMT': {'tx_interval': 120, 'rx_interval': 120, 'multiplier': 5}}}" + "Peer: 192.0.255.7 VRF: default - Incorrect timers; Transmit interval: 1300, Receive interval: 1200, Multiplier: 4", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect timers; Transmit interval: 120, Receive interval: 120, Multiplier: 5", ], }, }, @@ -239,8 +238,8 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers are not configured, status is not up or remote disc is zero:\n" - "{'192.0.255.7': {'CS': 'Not Configured'}, '192.0.255.70': {'MGMT': 'Not Configured'}}" + "Peer: 192.0.255.7 VRF: CS - Not found", + "Peer: 192.0.255.70 VRF: MGMT - Not found", ], }, }, @@ -281,9 +280,8 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers are not configured, status is not up or remote disc is zero:\n" - "{'192.0.255.7': {'default': {'status': 'Down', 'remote_disc': 108328132}}, " - "'192.0.255.70': {'MGMT': {'status': 'Down', 'remote_disc': 0}}}" + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: Down Remote Discriminator: 108328132", + "Peer: 192.0.255.70 VRF: MGMT - Session not properly established; State: Down Remote Discriminator: 0", ], }, }, @@ -414,7 +412,8 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers are not up:\n192.0.255.7 is down in default VRF with remote disc 0.\n192.0.255.71 is down in MGMT VRF with remote disc 0." + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: MGMT - Session not properly established; State: down Remote Discriminator: 0", ], }, }, @@ -458,7 +457,10 @@ "inputs": {}, "expected": { "result": "failure", - "messages": ["Following BFD peers were down:\n192.0.255.7 in default VRF has remote disc 0.\n192.0.255.71 in default VRF has remote disc 0."], + "messages": [ + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: up Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: default - Session not properly established; State: up Remote Discriminator: 0", + ], }, }, { @@ -512,8 +514,9 @@ "expected": { "result": "failure", "messages": [ - "Following BFD peers were down:\n192.0.255.7 in default VRF was down 3 hours ago.\n" - "192.0.255.71 in default VRF was down 3 hours ago.\n192.0.255.17 in default VRF was down 3 hours ago." + "Peer: 192.0.255.7 VRF: default - Unstable session, Recent failure 3 hours ago", + "Peer: 192.0.255.71 VRF: default - Unstable session, Recent failure 3 hours ago", + "Peer: 192.0.255.17 VRF: default - Unstable session, Recent failure 3 hours ago", ], }, }, @@ -615,9 +618,8 @@ "expected": { "result": "failure", "messages": [ - "The following BFD peers are not configured or have non-registered protocol(s):\n" - "{'192.0.255.7': {'default': ['isis']}, " - "'192.0.255.70': {'MGMT': ['isis']}}" + "Peer: 192.0.255.7 VRF: default - `isis` routing protocols not configured", + "Peer: 192.0.255.70 VRF: MGMT - `isis` routing protocols not configured", ], }, }, @@ -641,8 +643,8 @@ "expected": { "result": "failure", "messages": [ - "The following BFD peers are not configured or have non-registered protocol(s):\n" - "{'192.0.255.7': {'default': 'Not Configured'}, '192.0.255.70': {'MGMT': 'Not Configured'}}" + "Peer: 192.0.255.7 VRF: default - Not found", + "Peer: 192.0.255.70 VRF: MGMT - Not found", ], }, }, From 7eac1ada8f2c5de178fc3f52e43a81235c3e2dfd Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Tue, 19 Nov 2024 02:22:08 -0500 Subject: [PATCH 4/6] Addressed review commentsL: updated failure msgs --- anta/input_models/bfd.py | 2 +- anta/tests/bfd.py | 50 ++++++++++++++++-------------- tests/units/anta_tests/test_bfd.py | 35 +++++++++++---------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/anta/input_models/bfd.py b/anta/input_models/bfd.py index eeb3acb8d..9ccc6254a 100644 --- a/anta/input_models/bfd.py +++ b/anta/input_models/bfd.py @@ -1,7 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -"""Module containing input models for bfd tests.""" +"""Module containing input models for BFD tests.""" from __future__ import annotations diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py index 7066a53d3..f5f428e39 100644 --- a/anta/tests/bfd.py +++ b/anta/tests/bfd.py @@ -21,15 +21,15 @@ class VerifyBFDSpecificPeers(AntaTest): - """Verifies if the IPv4 BFD peer sessions are in the UP state and the remote discriminator (disc) is non-zero within the specified VRF. + """Verifies the state of IPv4 BFD peer sessions. This test performs the following checks for each specified peer: 1. Confirms that the specified VRF is configured. 2. Verifies that the peer exists in the BFD configuration. 3. For each specified BFD peer: - - Validates that the state is up - - Confirms that the remote discriminator identifier is non-zero. + - Validates that the state is `up` + - Confirms that the remote discriminator identifier (disc) is non-zero. Expected Results ---------------- @@ -38,7 +38,7 @@ class VerifyBFDSpecificPeers(AntaTest): - All BFD peers are up and remote disc is non-zero. * Failure: If any of the following occur: - A specified peer is not found in the BFD configuration within the specified VRF. - - Any BFD peer session is not up or the remote discriminator identifier is zero. + - Any BFD peer session is not `up` or the remote discriminator identifier is zero. Examples -------- @@ -88,11 +88,11 @@ def test(self) -> None: state = bfd_output.get("status") remote_disc = bfd_output.get("remoteDisc") if not (state == "up" and remote_disc != 0): - self.result.is_failure(f"{bfd_peer} - Session not properly established; State: {state} Remote Discriminator: {remote_disc}") + self.result.is_failure(f"{bfd_peer} - Session not properly established; State: {state}, Remote Discriminator: {remote_disc}") class VerifyBFDPeersIntervals(AntaTest): - """Verifies the timers of the IPv4 BFD peers in the specified VRF. + """Verifies the timers of IPv4 BFD peer sessions. This test performs the following checks for each specified peer: @@ -167,12 +167,14 @@ def test(self) -> None: op_rx_interval = bfd_details.get("operRxInterval") // 1000 detect_multiplier = bfd_details.get("detectMult") - # Check timers of BFD peer - intervals_ok = op_tx_interval == tx_interval and op_rx_interval == rx_interval and detect_multiplier == multiplier - if not intervals_ok: - self.result.is_failure( - f"{bfd_peer} - Incorrect timers; Transmit interval: {op_tx_interval}, Receive interval: {op_rx_interval}, Multiplier: {detect_multiplier}" - ) + if op_tx_interval != tx_interval: + self.result.is_failure(f"{bfd_peer} - Incorrect Transmit interval; Expected: {tx_interval} Actual: {op_tx_interval}") + + if op_rx_interval != rx_interval: + self.result.is_failure(f"{bfd_peer} - Incorrect Receive interval; Expected: {rx_interval} Actual: {op_rx_interval}") + + if detect_multiplier != multiplier: + self.result.is_failure(f"{bfd_peer} - Incorrect Multiplier; Expected: {multiplier} Actual: {detect_multiplier}") class VerifyBFDPeersHealth(AntaTest): @@ -180,18 +182,18 @@ class VerifyBFDPeersHealth(AntaTest): This test performs the following checks for BFD peers across all VRFs: - 1. Validates that the state is up - 2. Confirms that the remote discriminator identifier is non-zero. - 3. Optionally verifies that the peer have not been down before a specified threshold of hours + 1. Validates that the state is `up`. + 2. Confirms that the remote discriminator identifier (disc) is non-zero. + 3. Optionally verifies that the peer have not been down before a specified threshold of hours. Expected Results ---------------- * Success: If all of the following conditions are met: - All BFD peers across the VRFs are up and remote disc is non-zero. - - Last downtime of each peer is above the defined threshold. + - Last downtime of each peer is above the defined threshold, if specified. * Failure: If any of the following occur: - Any BFD peer session is not up or the remote discriminator identifier is zero. - - Last downtime of any peer is below the defined threshold. + - Last downtime of any peer is below the defined threshold, if specified. Examples -------- @@ -244,22 +246,24 @@ def test(self) -> None: if not (peer_status == "up" and remote_disc != 0): self.result.is_failure( - f"Peer: {peer} VRF: {vrf} - Session not properly established; State: {peer_status} Remote Discriminator: {remote_disc}" + f"Peer: {peer} VRF: {vrf} - Session not properly established; State: {peer_status}, Remote Discriminator: {remote_disc}" ) # Check if the last down is within the threshold if self.inputs.down_threshold and hours_difference < self.inputs.down_threshold: - self.result.is_failure(f"Peer: {peer} VRF: {vrf} - Unstable session, Recent failure {round(hours_difference)} hours ago") + self.result.is_failure( + f"Peer: {peer} VRF: {vrf} - Session failure detected within the expected uptime threshold ({round(hours_difference)} hours ago)" + ) class VerifyBFDPeersRegProtocols(AntaTest): - """Verifies the registered routing protocol of the IPv4 BFD peers in the specified VRF. + """Verifies the registered routing protocol of IPv4 BFD peer sessions. This test performs the following checks for each specified peer: 1. Confirms that the specified VRF is configured. 2. Verifies that the peer exists in the BFD configuration. - 3. Confirms that BFD peer is correctly configured with the routing protocol. + 3. Confirms that BFD peer is correctly configured with the `routing protocol`. Expected Results ---------------- @@ -315,6 +319,6 @@ def test(self) -> None: continue # Check registered protocols - difference = set(protocols) - set(get_value(bfd_output, "peerStatsDetail.apps")) + difference = sorted(set(protocols) - set(get_value(bfd_output, "peerStatsDetail.apps"))) if difference: - self.result.is_failure(f"{bfd_peer} - `{','.join(difference)}` routing protocols not configured") + self.result.is_failure(f"{bfd_peer} - `{', '.join(difference)}` routing protocol(s) not configured") diff --git a/tests/units/anta_tests/test_bfd.py b/tests/units/anta_tests/test_bfd.py index 76c973d32..bbcb94a0e 100644 --- a/tests/units/anta_tests/test_bfd.py +++ b/tests/units/anta_tests/test_bfd.py @@ -160,8 +160,11 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Incorrect timers; Transmit interval: 1300, Receive interval: 1200, Multiplier: 4", - "Peer: 192.0.255.70 VRF: MGMT - Incorrect timers; Transmit interval: 120, Receive interval: 120, Multiplier: 5", + "Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval; Expected: 1200 Actual: 1300", + "Peer: 192.0.255.7 VRF: default - Incorrect Multiplier; Expected: 3 Actual: 4", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Transmit interval; Expected: 1200 Actual: 120", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Receive interval; Expected: 1200 Actual: 120", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Multiplier; Expected: 3 Actual: 5", ], }, }, @@ -254,7 +257,7 @@ "192.0.255.7": { "peerStats": { "": { - "status": "Down", + "status": "down", "remoteDisc": 108328132, } } @@ -266,7 +269,7 @@ "192.0.255.70": { "peerStats": { "": { - "status": "Down", + "status": "down", "remoteDisc": 0, } } @@ -280,8 +283,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: Down Remote Discriminator: 108328132", - "Peer: 192.0.255.70 VRF: MGMT - Session not properly established; State: Down Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down, Remote Discriminator: 108328132", + "Peer: 192.0.255.70 VRF: MGMT - Session not properly established; State: down, Remote Discriminator: 0", ], }, }, @@ -412,8 +415,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down Remote Discriminator: 0", - "Peer: 192.0.255.71 VRF: MGMT - Session not properly established; State: down Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down, Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: MGMT - Session not properly established; State: down, Remote Discriminator: 0", ], }, }, @@ -458,8 +461,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: up Remote Discriminator: 0", - "Peer: 192.0.255.71 VRF: default - Session not properly established; State: up Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established; State: up, Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: default - Session not properly established; State: up, Remote Discriminator: 0", ], }, }, @@ -514,9 +517,9 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Unstable session, Recent failure 3 hours ago", - "Peer: 192.0.255.71 VRF: default - Unstable session, Recent failure 3 hours ago", - "Peer: 192.0.255.17 VRF: default - Unstable session, Recent failure 3 hours ago", + "Peer: 192.0.255.7 VRF: default - Session failure detected within the expected uptime threshold (3 hours ago)", + "Peer: 192.0.255.71 VRF: default - Session failure detected within the expected uptime threshold (3 hours ago)", + "Peer: 192.0.255.17 VRF: default - Session failure detected within the expected uptime threshold (3 hours ago)", ], }, }, @@ -612,14 +615,14 @@ "inputs": { "bfd_peers": [ {"peer_address": "192.0.255.7", "vrf": "default", "protocols": ["isis"]}, - {"peer_address": "192.0.255.70", "vrf": "MGMT", "protocols": ["isis"]}, + {"peer_address": "192.0.255.70", "vrf": "MGMT", "protocols": ["isis", "ospf"]}, ] }, "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - `isis` routing protocols not configured", - "Peer: 192.0.255.70 VRF: MGMT - `isis` routing protocols not configured", + "Peer: 192.0.255.7 VRF: default - `isis` routing protocol(s) not configured", + "Peer: 192.0.255.70 VRF: MGMT - `isis, ospf` routing protocol(s) not configured", ], }, }, From a5afad92f32e693de6a094b4cd422de1277766c0 Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Tue, 26 Nov 2024 08:31:47 -0500 Subject: [PATCH 5/6] updated failure msgs: removed , and ; --- anta/tests/bfd.py | 17 ++++++++++------- tests/units/anta_tests/test_bfd.py | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py index f5f428e39..119d22a61 100644 --- a/anta/tests/bfd.py +++ b/anta/tests/bfd.py @@ -53,7 +53,6 @@ class VerifyBFDSpecificPeers(AntaTest): ``` """ - description = "Verifies the IPv4 BFD peer's sessions and remote disc in the specified VRF." categories: ClassVar[list[str]] = ["bfd"] commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show bfd peers", revision=1)] @@ -63,6 +62,7 @@ class Input(AntaTest.Input): bfd_peers: list[BFDPeer] """List of IPv4 BFD""" BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer + """To maintain backward compatibility.""" @AntaTest.anta_test def test(self) -> None: @@ -88,7 +88,7 @@ def test(self) -> None: state = bfd_output.get("status") remote_disc = bfd_output.get("remoteDisc") if not (state == "up" and remote_disc != 0): - self.result.is_failure(f"{bfd_peer} - Session not properly established; State: {state}, Remote Discriminator: {remote_disc}") + self.result.is_failure(f"{bfd_peer} - Session not properly established - State: {state} Remote Discriminator: {remote_disc}") class VerifyBFDPeersIntervals(AntaTest): @@ -137,6 +137,7 @@ class Input(AntaTest.Input): bfd_peers: list[BFDPeer] """List of IPv4 BFD""" BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer + """To maintain backward compatibility""" @AntaTest.anta_test def test(self) -> None: @@ -168,13 +169,13 @@ def test(self) -> None: detect_multiplier = bfd_details.get("detectMult") if op_tx_interval != tx_interval: - self.result.is_failure(f"{bfd_peer} - Incorrect Transmit interval; Expected: {tx_interval} Actual: {op_tx_interval}") + self.result.is_failure(f"{bfd_peer} - Incorrect Transmit interval - Expected: {tx_interval} Actual: {op_tx_interval}") if op_rx_interval != rx_interval: - self.result.is_failure(f"{bfd_peer} - Incorrect Receive interval; Expected: {rx_interval} Actual: {op_rx_interval}") + self.result.is_failure(f"{bfd_peer} - Incorrect Receive interval - Expected: {rx_interval} Actual: {op_rx_interval}") if detect_multiplier != multiplier: - self.result.is_failure(f"{bfd_peer} - Incorrect Multiplier; Expected: {multiplier} Actual: {detect_multiplier}") + self.result.is_failure(f"{bfd_peer} - Incorrect Multiplier - Expected: {multiplier} Actual: {detect_multiplier}") class VerifyBFDPeersHealth(AntaTest): @@ -246,7 +247,7 @@ def test(self) -> None: if not (peer_status == "up" and remote_disc != 0): self.result.is_failure( - f"Peer: {peer} VRF: {vrf} - Session not properly established; State: {peer_status}, Remote Discriminator: {remote_disc}" + f"Peer: {peer} VRF: {vrf} - Session not properly established - State: {peer_status} Remote Discriminator: {remote_disc}" ) # Check if the last down is within the threshold @@ -296,6 +297,7 @@ class Input(AntaTest.Input): bfd_peers: list[BFDPeer] """List of IPv4 BFD""" BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer + """To maintain backward compatibility""" @AntaTest.anta_test def test(self) -> None: @@ -321,4 +323,5 @@ def test(self) -> None: # Check registered protocols difference = sorted(set(protocols) - set(get_value(bfd_output, "peerStatsDetail.apps"))) if difference: - self.result.is_failure(f"{bfd_peer} - `{', '.join(difference)}` routing protocol(s) not configured") + failures = " ".join(f"`{item}`" for item in difference) + self.result.is_failure(f"{bfd_peer} - {failures} routing protocol(s) not configured") diff --git a/tests/units/anta_tests/test_bfd.py b/tests/units/anta_tests/test_bfd.py index bbcb94a0e..952e8388d 100644 --- a/tests/units/anta_tests/test_bfd.py +++ b/tests/units/anta_tests/test_bfd.py @@ -160,11 +160,11 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval; Expected: 1200 Actual: 1300", - "Peer: 192.0.255.7 VRF: default - Incorrect Multiplier; Expected: 3 Actual: 4", - "Peer: 192.0.255.70 VRF: MGMT - Incorrect Transmit interval; Expected: 1200 Actual: 120", - "Peer: 192.0.255.70 VRF: MGMT - Incorrect Receive interval; Expected: 1200 Actual: 120", - "Peer: 192.0.255.70 VRF: MGMT - Incorrect Multiplier; Expected: 3 Actual: 5", + "Peer: 192.0.255.7 VRF: default - Incorrect Transmit interval - Expected: 1200 Actual: 1300", + "Peer: 192.0.255.7 VRF: default - Incorrect Multiplier - Expected: 3 Actual: 4", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Transmit interval - Expected: 1200 Actual: 120", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Receive interval - Expected: 1200 Actual: 120", + "Peer: 192.0.255.70 VRF: MGMT - Incorrect Multiplier - Expected: 3 Actual: 5", ], }, }, @@ -283,8 +283,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down, Remote Discriminator: 108328132", - "Peer: 192.0.255.70 VRF: MGMT - Session not properly established; State: down, Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established - State: down Remote Discriminator: 108328132", + "Peer: 192.0.255.70 VRF: MGMT - Session not properly established - State: down Remote Discriminator: 0", ], }, }, @@ -415,8 +415,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: down, Remote Discriminator: 0", - "Peer: 192.0.255.71 VRF: MGMT - Session not properly established; State: down, Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established - State: down Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: MGMT - Session not properly established - State: down Remote Discriminator: 0", ], }, }, @@ -461,8 +461,8 @@ "expected": { "result": "failure", "messages": [ - "Peer: 192.0.255.7 VRF: default - Session not properly established; State: up, Remote Discriminator: 0", - "Peer: 192.0.255.71 VRF: default - Session not properly established; State: up, Remote Discriminator: 0", + "Peer: 192.0.255.7 VRF: default - Session not properly established - State: up Remote Discriminator: 0", + "Peer: 192.0.255.71 VRF: default - Session not properly established - State: up Remote Discriminator: 0", ], }, }, @@ -622,7 +622,7 @@ "result": "failure", "messages": [ "Peer: 192.0.255.7 VRF: default - `isis` routing protocol(s) not configured", - "Peer: 192.0.255.70 VRF: MGMT - `isis, ospf` routing protocol(s) not configured", + "Peer: 192.0.255.70 VRF: MGMT - `isis` `ospf` routing protocol(s) not configured", ], }, }, From fb5b3836463cd9bbc02ceb320aab405244e2492c Mon Sep 17 00:00:00 2001 From: vitthalmagadum Date: Tue, 26 Nov 2024 08:33:40 -0500 Subject: [PATCH 6/6] updated docstring --- anta/tests/bfd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anta/tests/bfd.py b/anta/tests/bfd.py index 119d22a61..ba27f942b 100644 --- a/anta/tests/bfd.py +++ b/anta/tests/bfd.py @@ -35,7 +35,7 @@ class VerifyBFDSpecificPeers(AntaTest): ---------------- * Success: If all of the following conditions are met: - All specified peers are found in the BFD configuration within the specified VRF. - - All BFD peers are up and remote disc is non-zero. + - All BFD peers are `up` and remote disc is non-zero. * Failure: If any of the following occur: - A specified peer is not found in the BFD configuration within the specified VRF. - Any BFD peer session is not `up` or the remote discriminator identifier is zero.