162 lines
6.2 KiB
Python
162 lines
6.2 KiB
Python
# Copyright (c) 2023-2025 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 related to OSPF tests."""
|
|
|
|
# Mypy does not understand AntaTest.Input typing
|
|
# mypy: disable-error-code=attr-defined
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, ClassVar
|
|
|
|
from anta.models import AntaCommand, AntaTest
|
|
from anta.tools import get_value
|
|
|
|
if TYPE_CHECKING:
|
|
from anta.models import AntaTemplate
|
|
|
|
|
|
class VerifyOSPFNeighborState(AntaTest):
|
|
"""Verifies all OSPF neighbors are in FULL state.
|
|
|
|
Expected Results
|
|
----------------
|
|
* Success: The test will pass if all OSPF neighbors are in FULL state.
|
|
* Failure: The test will fail if some OSPF neighbors are not in FULL state.
|
|
* Skipped: The test will be skipped if no OSPF neighbor is found.
|
|
|
|
Examples
|
|
--------
|
|
```yaml
|
|
anta.tests.routing:
|
|
ospf:
|
|
- VerifyOSPFNeighborState:
|
|
```
|
|
"""
|
|
|
|
categories: ClassVar[list[str]] = ["ospf"]
|
|
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip ospf neighbor", revision=1)]
|
|
|
|
@AntaTest.anta_test
|
|
def test(self) -> None:
|
|
"""Main test function for VerifyOSPFNeighborState."""
|
|
self.result.is_success()
|
|
|
|
# If OSPF is not configured on device, test skipped.
|
|
if not (command_output := get_value(self.instance_commands[0].json_output, "vrfs")):
|
|
self.result.is_skipped("OSPF not configured")
|
|
return
|
|
|
|
no_neighbor = True
|
|
for vrf, vrf_data in command_output.items():
|
|
for instance, instance_data in vrf_data["instList"].items():
|
|
neighbors = instance_data["ospfNeighborEntries"]
|
|
if not neighbors:
|
|
continue
|
|
no_neighbor = False
|
|
interfaces = [(neighbor["routerId"], state) for neighbor in neighbors if (state := neighbor["adjacencyState"]) != "full"]
|
|
for interface in interfaces:
|
|
self.result.is_failure(
|
|
f"Instance: {instance} VRF: {vrf} Interface: {interface[0]} - Incorrect adjacency state - Expected: Full Actual: {interface[1]}"
|
|
)
|
|
|
|
# If OSPF neighbors are not configured on device, test skipped.
|
|
if no_neighbor:
|
|
self.result.is_skipped("No OSPF neighbor detected")
|
|
|
|
|
|
class VerifyOSPFNeighborCount(AntaTest):
|
|
"""Verifies the number of OSPF neighbors in FULL state is the one we expect.
|
|
|
|
Expected Results
|
|
----------------
|
|
* Success: The test will pass if the number of OSPF neighbors in FULL state is the one we expect.
|
|
* Failure: The test will fail if the number of OSPF neighbors in FULL state is not the one we expect.
|
|
* Skipped: The test will be skipped if no OSPF neighbor is found.
|
|
|
|
Examples
|
|
--------
|
|
```yaml
|
|
anta.tests.routing:
|
|
ospf:
|
|
- VerifyOSPFNeighborCount:
|
|
number: 3
|
|
```
|
|
"""
|
|
|
|
categories: ClassVar[list[str]] = ["ospf"]
|
|
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip ospf neighbor", revision=1)]
|
|
|
|
class Input(AntaTest.Input):
|
|
"""Input model for the VerifyOSPFNeighborCount test."""
|
|
|
|
number: int
|
|
"""The expected number of OSPF neighbors in FULL state."""
|
|
|
|
@AntaTest.anta_test
|
|
def test(self) -> None:
|
|
"""Main test function for VerifyOSPFNeighborCount."""
|
|
self.result.is_success()
|
|
# If OSPF is not configured on device, test skipped.
|
|
if not (command_output := get_value(self.instance_commands[0].json_output, "vrfs")):
|
|
self.result.is_skipped("OSPF not configured")
|
|
return
|
|
|
|
no_neighbor = True
|
|
interfaces = []
|
|
for vrf_data in command_output.values():
|
|
for instance_data in vrf_data["instList"].values():
|
|
neighbors = instance_data["ospfNeighborEntries"]
|
|
if not neighbors:
|
|
continue
|
|
no_neighbor = False
|
|
interfaces.extend([neighbor["routerId"] for neighbor in neighbors if neighbor["adjacencyState"] == "full"])
|
|
|
|
# If OSPF neighbors are not configured on device, test skipped.
|
|
if no_neighbor:
|
|
self.result.is_skipped("No OSPF neighbor detected")
|
|
return
|
|
|
|
# If the number of OSPF neighbors expected to be in the FULL state does not match with actual one, test fails.
|
|
if len(interfaces) != self.inputs.number:
|
|
self.result.is_failure(f"Neighbor count mismatch - Expected: {self.inputs.number} Actual: {len(interfaces)}")
|
|
|
|
|
|
class VerifyOSPFMaxLSA(AntaTest):
|
|
"""Verifies all OSPF instances did not cross the maximum LSA threshold.
|
|
|
|
Expected Results
|
|
----------------
|
|
* Success: The test will pass if all OSPF instances did not cross the maximum LSA Threshold.
|
|
* Failure: The test will fail if some OSPF instances crossed the maximum LSA Threshold.
|
|
* Skipped: The test will be skipped if no OSPF instance is found.
|
|
|
|
Examples
|
|
--------
|
|
```yaml
|
|
anta.tests.routing:
|
|
ospf:
|
|
- VerifyOSPFMaxLSA:
|
|
```
|
|
"""
|
|
|
|
categories: ClassVar[list[str]] = ["ospf"]
|
|
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip ospf", revision=1)]
|
|
|
|
@AntaTest.anta_test
|
|
def test(self) -> None:
|
|
"""Main test function for VerifyOSPFMaxLSA."""
|
|
self.result.is_success()
|
|
|
|
# If OSPF is not configured on device, test skipped.
|
|
if not (command_output := get_value(self.instance_commands[0].json_output, "vrfs")):
|
|
self.result.is_skipped("OSPF not configured")
|
|
return
|
|
|
|
for vrf_data in command_output.values():
|
|
for instance, instance_data in vrf_data.get("instList", {}).items():
|
|
max_lsa = instance_data["maxLsaInformation"]["maxLsa"]
|
|
max_lsa_threshold = instance_data["maxLsaInformation"]["maxLsaThreshold"]
|
|
num_lsa = get_value(instance_data, "lsaInformation.numLsa")
|
|
if num_lsa > (max_lsa_threshold := round(max_lsa * (max_lsa_threshold / 100))):
|
|
self.result.is_failure(f"Instance: {instance} - Crossed the maximum LSA threshold - Expected: < {max_lsa_threshold} Actual: {num_lsa}")
|