Skip to content

ANTA Tests for BFD

Tests

Module related to BFD tests.

VerifyBFDPeersHealth

Verifies the health of BFD peers across all VRFs.

Warning

Seamless BFD (S-BFD) is not supported.

Expected Results
  • Success: The test will pass if all BFD peers are up, remote discriminators (disc) are non-zero and last downtime is above down_threshold (if provided).
  • Failure: The test will fail if any BFD peer is not up, remote disc is zero or last downtime is below down_threshold (if provided).
Examples
anta.tests.bfd:
  - VerifyBFDPeersHealth:
      down_threshold: 2

Inputs

Name Type Description Default
down_threshold int | None
Optional down threshold in hours to check if a BFD peer was down before those hours or not.
Field(default=None, gt=0)
Source code in anta/tests/bfd.py
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
class VerifyBFDPeersHealth(AntaTest):
    """Verifies the health of BFD peers across all VRFs.

    !!! warning
        Seamless BFD (S-BFD) is **not** supported.

    Expected Results
    ----------------
    * Success: The test will pass if all BFD peers are `up`, remote discriminators (disc) are non-zero
                and last downtime is above `down_threshold` (if provided).
    * Failure: The test will fail if any BFD peer is not `up`, remote disc is zero or last downtime is below `down_threshold` (if provided).

    Examples
    --------
    ```yaml
    anta.tests.bfd:
      - VerifyBFDPeersHealth:
          down_threshold: 2
    ```
    """

    categories: ClassVar[list[str]] = ["bfd"]
    # Using revision 1 as latest revision introduces additional nesting for type
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [
        AntaCommand(command="show bfd peers", revision=1),
        AntaCommand(command="show clock", revision=1),
    ]
    inputs: VerifyBFDPeersHealth.Input

    class Input(AntaTest.Input):
        """Input model for the VerifyBFDPeersHealth test."""

        down_threshold: int | None = Field(default=None, gt=0)
        """Optional down threshold in hours to check if a BFD peer was down before those hours or not."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBFDPeersHealth."""
        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

        # Check if any IPv4 or IPv6 BFD peer is configured
        if not any(vrf_data["ipv4Neighbors"] | vrf_data["ipv6Neighbors"] for vrf_data in bfd_output["vrfs"].values()):
            self.result.is_failure("No IPv4 or IPv6 BFD peers configured for any VRF")
            return

        for vrf, vrf_data in bfd_output["vrfs"].items():
            # Merging the IPv4 and IPv6 peers into a single dict
            all_peers = vrf_data["ipv4Neighbors"] | vrf_data["ipv6Neighbors"]
            for peer_ip, peer_data in all_peers.items():
                for interface, peer_stats in peer_data["peerStats"].items():
                    identifier = f"Peer: {peer_ip} VRF: {vrf} Interface: {interface}" if interface else f"Peer: {peer_ip} VRF: {vrf}"
                    peer_status = peer_stats["status"]
                    remote_disc = peer_stats["remoteDisc"]

                    if not (peer_status == "up" and remote_disc != 0):
                        self.result.is_failure(f"{identifier} - 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 is not None:
                        last_down = peer_stats["lastDown"]
                        hours_difference = (
                            datetime.fromtimestamp(current_timestamp, tz=timezone.utc) - datetime.fromtimestamp(last_down, tz=timezone.utc)
                        ).total_seconds() / 3600
                        if hours_difference < self.inputs.down_threshold:
                            self.result.is_failure(
                                f"{identifier} - Session failure detected within the expected uptime threshold ({round(hours_difference)} hours ago)"
                            )

VerifyBFDPeersIntervals

Verifies the operational timers of BFD peer sessions.

Warning

Seamless BFD (S-BFD) is not supported.

Expected Results
  • Success: The test will pass if all specified BFD peer sessions are operating with the proper timers.
  • Failure: The test will fail if any specified BFD peer session is not found or not operating with the proper timers.
Examples
anta.tests.bfd:
  - VerifyBFDPeersIntervals:
      bfd_peers:
        # Multi-hop session in VRF default
        - peer_address: 192.0.255.8
          tx_interval: 3600
          rx_interval: 3600
          multiplier: 3
        # Multi-hop session in VRF DEV
        - peer_address: 192.0.255.7
          vrf: DEV
          tx_interval: 3600
          rx_interval: 3600
          multiplier: 3
        # Single-hop session on local transport interface Ethernet3 in VRF PROD
        - peer_address: 192.168.10.2
          vrf: PROD
          interface: Ethernet3
          tx_interval: 1200
          rx_interval: 1200
          multiplier: 3
          detection_time: 3600  # Optional
        # IPv6 peers also supported
        - peer_address: fd00:dc:1::1
          tx_interval: 1200
          rx_interval: 1200
          multiplier: 3
          detection_time: 3600  # Optional

Inputs

Name Type Description Default
bfd_peers list[BFDPeer]
List of BFD peers.
-
BFDPeer type[BFDPeer]
To maintain backward compatibility.
BFDPeer
Source code in anta/tests/bfd.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
class VerifyBFDPeersIntervals(AntaTest):
    """Verifies the operational timers of BFD peer sessions.

    !!! warning
        Seamless BFD (S-BFD) is **not** supported.

    Expected Results
    ----------------
    * Success: The test will pass if all specified BFD peer sessions are operating with the proper timers.
    * Failure: The test will fail if any specified BFD peer session is not found or not operating with the proper timers.


    Examples
    --------
    ```yaml
    anta.tests.bfd:
      - VerifyBFDPeersIntervals:
          bfd_peers:
            # Multi-hop session in VRF default
            - peer_address: 192.0.255.8
              tx_interval: 3600
              rx_interval: 3600
              multiplier: 3
            # Multi-hop session in VRF DEV
            - peer_address: 192.0.255.7
              vrf: DEV
              tx_interval: 3600
              rx_interval: 3600
              multiplier: 3
            # Single-hop session on local transport interface Ethernet3 in VRF PROD
            - peer_address: 192.168.10.2
              vrf: PROD
              interface: Ethernet3
              tx_interval: 1200
              rx_interval: 1200
              multiplier: 3
              detection_time: 3600  # Optional
            # IPv6 peers also supported
            - peer_address: fd00:dc:1::1
              tx_interval: 1200
              rx_interval: 1200
              multiplier: 3
              detection_time: 3600  # Optional
    ```
    """

    categories: ClassVar[list[str]] = ["bfd"]
    # Using revision 1 as latest revision introduces additional nesting for type
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show bfd peers detail", revision=1)]
    inputs: VerifyBFDPeersIntervals.Input

    class Input(AntaTest.Input):
        """Input model for the VerifyBFDPeersIntervals test."""

        bfd_peers: list[BFDPeer]
        """List of BFD peers."""
        BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer
        """To maintain backward compatibility."""

        @field_validator("bfd_peers")
        @classmethod
        def validate_bfd_peers(cls, bfd_peers: list[T]) -> list[T]:
            """Validate that 'tx_interval', 'rx_interval' and 'multiplier' fields are provided in each BFD peer."""
            for peer in bfd_peers:
                missing_fileds = []
                if peer.tx_interval is None:
                    missing_fileds.append("tx_interval")
                if peer.rx_interval is None:
                    missing_fileds.append("rx_interval")
                if peer.multiplier is None:
                    missing_fileds.append("multiplier")
                if missing_fileds:
                    msg = f"{peer} {', '.join(missing_fileds)} field(s) are missing in the input"
                    raise ValueError(msg)
            return bfd_peers

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBFDPeersIntervals."""
        self.result.is_success()

        output = self.instance_commands[0].json_output

        for bfd_peer in self.inputs.bfd_peers:
            # Check if BFD peer is found
            if (peer_stats := _get_bfd_peer_stats(bfd_peer, output)) is None:
                self.result.is_failure(f"{bfd_peer} - Not found")
                continue

            # Convert interval timers into milliseconds to be consistent with the inputs
            act_tx_interval = get_value(peer_stats, "peerStatsDetail.operTxInterval") // 1000
            act_rx_interval = get_value(peer_stats, "peerStatsDetail.operRxInterval") // 1000
            act_detect_time = get_value(peer_stats, "peerStatsDetail.detectTime") // 1000
            act_detect_mult = get_value(peer_stats, "peerStatsDetail.detectMult")

            if act_tx_interval != bfd_peer.tx_interval:
                self.result.is_failure(f"{bfd_peer} - Incorrect Transmit interval - Expected: {bfd_peer.tx_interval} Actual: {act_tx_interval}")

            if act_rx_interval != bfd_peer.rx_interval:
                self.result.is_failure(f"{bfd_peer} - Incorrect Receive interval - Expected: {bfd_peer.rx_interval} Actual: {act_rx_interval}")

            if act_detect_mult != bfd_peer.multiplier:
                self.result.is_failure(f"{bfd_peer} - Incorrect Multiplier - Expected: {bfd_peer.multiplier} Actual: {act_detect_mult}")

            if bfd_peer.detection_time and act_detect_time != bfd_peer.detection_time:
                self.result.is_failure(f"{bfd_peer} - Incorrect Detection Time - Expected: {bfd_peer.detection_time} Actual: {act_detect_time}")

VerifyBFDPeersRegProtocols

Verifies the registered protocols of BFD peer sessions.

Warning

Seamless BFD (S-BFD) is not supported.

Expected Results
  • Success: The test will pass if all specified BFD peers have the proper registered protocols.
  • Failure: The test will fail if any specified BFD peer is not found or doesn’t have the proper registered protocols.
Examples
anta.tests.bfd:
  - VerifyBFDPeersRegProtocols:
      bfd_peers:
        # Multi-hop session in VRF default
        - peer_address: 192.0.255.8
          protocols: [ bgp ]
        # Multi-hop session in VRF DEV
        - peer_address: 192.0.255.7
          vrf: DEV
          protocols: [ bgp, vxlan ]
        # Single-hop session on local transport interface Ethernet3 in VRF PROD
        - peer_address: 192.168.10.2
          vrf: PROD
          interface: Ethernet3
          protocols: [ ospf ]
          detection_time: 3600  # Optional
        # IPv6 peers also supported
        - peer_address: fd00:dc:1::1
          protocols: [ isis ]

Inputs

Name Type Description Default
bfd_peers list[BFDPeer]
List of BFD peers.
-
BFDPeer type[BFDPeer]
To maintain backward compatibility.
BFDPeer
Source code in anta/tests/bfd.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
class VerifyBFDPeersRegProtocols(AntaTest):
    """Verifies the registered protocols of BFD peer sessions.

    !!! warning
        Seamless BFD (S-BFD) is **not** supported.

    Expected Results
    ----------------
    * Success: The test will pass if all specified BFD peers have the proper registered protocols.
    * Failure: The test will fail if any specified BFD peer is not found or doesn't have the proper registered protocols.

    Examples
    --------
    ```yaml
    anta.tests.bfd:
      - VerifyBFDPeersRegProtocols:
          bfd_peers:
            # Multi-hop session in VRF default
            - peer_address: 192.0.255.8
              protocols: [ bgp ]
            # Multi-hop session in VRF DEV
            - peer_address: 192.0.255.7
              vrf: DEV
              protocols: [ bgp, vxlan ]
            # Single-hop session on local transport interface Ethernet3 in VRF PROD
            - peer_address: 192.168.10.2
              vrf: PROD
              interface: Ethernet3
              protocols: [ ospf ]
              detection_time: 3600  # Optional
            # IPv6 peers also supported
            - peer_address: fd00:dc:1::1
              protocols: [ isis ]
    ```
    """

    categories: ClassVar[list[str]] = ["bfd"]
    # Using revision 1 as latest revision introduces additional nesting for type
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show bfd peers detail", revision=1)]
    inputs: VerifyBFDPeersRegProtocols.Input

    class Input(AntaTest.Input):
        """Input model for the VerifyBFDPeersRegProtocols test."""

        bfd_peers: list[BFDPeer]
        """List of BFD peers."""
        BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer
        """To maintain backward compatibility."""

        @field_validator("bfd_peers")
        @classmethod
        def validate_bfd_peers(cls, bfd_peers: list[T]) -> list[T]:
            """Validate that 'protocols' field is provided in each BFD peer."""
            for peer in bfd_peers:
                if peer.protocols is None:
                    msg = f"{peer} 'protocols' field missing in the input"
                    raise ValueError(msg)
            return bfd_peers

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBFDPeersRegProtocols."""
        self.result.is_success()

        output = self.instance_commands[0].json_output

        for bfd_peer in self.inputs.bfd_peers:
            # Check if BFD peer is found
            if (peer_stats := _get_bfd_peer_stats(bfd_peer, output)) is None:
                self.result.is_failure(f"{bfd_peer} - Not found")
                continue

            # Check registered protocols
            difference = sorted(set(bfd_peer.protocols) - set(get_value(peer_stats, "peerStatsDetail.apps", default=[])))
            if difference:
                self.result.is_failure(f"{bfd_peer} - {', '.join(difference)} protocol{'s' if len(difference) > 1 else ''} not registered")

VerifyBFDSpecificPeers

Verifies the state of BFD peer sessions.

Warning

Seamless BFD (S-BFD) is not supported.

Expected Results
  • Success: The test will pass if all specified BFD peers are up and remote discriminators (disc) are non-zero.
  • Failure: The test will fail if any specified BFD peer is not found, not up or remote disc is zero.
Examples
anta.tests.bfd:
  - VerifyBFDSpecificPeers:
      bfd_peers:
        # Multi-hop session in VRF default
        - peer_address: 192.0.255.8
        # Multi-hop session in VRF DEV
        - peer_address: 192.0.255.7
          vrf: DEV
        # Single-hop session on local transport interface Ethernet3 in VRF PROD
        - peer_address: 192.168.10.2
          vrf: PROD
          interface: Ethernet3
        # IPv6 peers also supported
        - peer_address: fd00:dc:1::1

Inputs

Name Type Description Default
bfd_peers list[BFDPeer]
List of BFD peers.
-
BFDPeer type[BFDPeer]
To maintain backward compatibility.
BFDPeer
Source code in anta/tests/bfd.py
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
class VerifyBFDSpecificPeers(AntaTest):
    """Verifies the state of BFD peer sessions.

    !!! warning
        Seamless BFD (S-BFD) is **not** supported.

    Expected Results
    ----------------
    * Success: The test will pass if all specified BFD peers are `up` and remote discriminators (disc) are non-zero.
    * Failure: The test will fail if any specified BFD peer is not found, not `up` or remote disc is zero.

    Examples
    --------
    ```yaml
    anta.tests.bfd:
      - VerifyBFDSpecificPeers:
          bfd_peers:
            # Multi-hop session in VRF default
            - peer_address: 192.0.255.8
            # Multi-hop session in VRF DEV
            - peer_address: 192.0.255.7
              vrf: DEV
            # Single-hop session on local transport interface Ethernet3 in VRF PROD
            - peer_address: 192.168.10.2
              vrf: PROD
              interface: Ethernet3
            # IPv6 peers also supported
            - peer_address: fd00:dc:1::1
    ```
    """

    categories: ClassVar[list[str]] = ["bfd"]
    # Using revision 1 as latest revision introduces additional nesting for type
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show bfd peers", revision=1)]
    inputs: VerifyBFDSpecificPeers.Input

    class Input(AntaTest.Input):
        """Input model for the VerifyBFDSpecificPeers test."""

        bfd_peers: list[BFDPeer]
        """List of BFD peers."""
        BFDPeer: ClassVar[type[BFDPeer]] = BFDPeer
        """To maintain backward compatibility."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBFDSpecificPeers."""
        self.result.is_success()

        output = self.instance_commands[0].json_output

        for bfd_peer in self.inputs.bfd_peers:
            # Check if BFD peer is found
            if (peer_stats := _get_bfd_peer_stats(bfd_peer, output)) is None:
                self.result.is_failure(f"{bfd_peer} - Not found")
                continue

            # Check BFD peer status and remote disc
            state = peer_stats["status"]
            remote_disc = peer_stats["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}")

Input models

Module containing input models for BFD tests.

BFDPeer

Model for a BFD peer.

Name Type Description Default
peer_address IPv4Address | IPv6Address
IPv4 or IPv6 address of the BFD peer.
-
vrf str
VRF of the BFD peer.
'default'
interface Interface | None
Single-hop transport interface. Use `None` for multi-hop sessions.
None
protocols list[BfdProtocol] | None
List of protocols using BFD with this peer. Required field in the `VerifyBFDPeersRegProtocols` test.
None
tx_interval BfdInterval | None
Operational transmit interval of the BFD session in milliseconds. Required field in the `VerifyBFDPeersIntervals` test.
None
rx_interval BfdInterval | None
Operational minimum receive interval of the BFD session in milliseconds. Required field in the `VerifyBFDPeersIntervals` test.
None
multiplier BfdMultiplier | None
Multiplier of the BFD session. Required field in the `VerifyBFDPeersIntervals` test.
None
detection_time int | None
Detection time of the BFD session in milliseconds. Defines how long it takes for BFD to detect connection failure. Optional field in the `VerifyBFDPeersIntervals` test.
None
Source code in anta/input_models/bfd.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class BFDPeer(BaseModel):
    """Model for a BFD peer."""

    model_config = ConfigDict(extra="forbid")
    peer_address: IPv4Address | IPv6Address
    """IPv4 or IPv6 address of the BFD peer."""
    vrf: str = "default"
    """VRF of the BFD peer."""
    interface: Interface | None = None
    """Single-hop transport interface. Use `None` for multi-hop sessions."""
    protocols: list[BfdProtocol] | None = None
    """List of protocols using BFD with this peer. Required field in the `VerifyBFDPeersRegProtocols` test."""
    tx_interval: BfdInterval | None = None
    """Operational transmit interval of the BFD session in milliseconds. Required field in the `VerifyBFDPeersIntervals` test."""
    rx_interval: BfdInterval | None = None
    """Operational minimum receive interval of the BFD session in milliseconds. Required field in the `VerifyBFDPeersIntervals` test."""
    multiplier: BfdMultiplier | None = None
    """Multiplier of the BFD session. Required field in the `VerifyBFDPeersIntervals` test."""
    detection_time: int | None = None
    """Detection time of the BFD session in milliseconds. Defines how long it takes for BFD to detect connection failure.

    Optional field in the `VerifyBFDPeersIntervals` test."""

    def __str__(self) -> str:
        """Return a human-readable string representation of the BFDPeer for reporting."""
        base = f"Peer: {self.peer_address} VRF: {self.vrf}"
        if self.interface is not None:
            base += f" Interface: {self.interface}"
        return base