anta/anta/tests/snmp.py
Daniel Baumann afeccccd6a
Merging upstream version 1.2.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-05 11:55:22 +01:00

341 lines
12 KiB
Python

# 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 related to the EOS various SNMP tests."""
# Mypy does not understand AntaTest.Input typing
# mypy: disable-error-code=attr-defined
from __future__ import annotations
from typing import TYPE_CHECKING, ClassVar, get_args
from anta.custom_types import PositiveInteger, SnmpErrorCounter, SnmpPdu
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value
if TYPE_CHECKING:
from anta.models import AntaTemplate
class VerifySnmpStatus(AntaTest):
"""Verifies whether the SNMP agent is enabled in a specified VRF.
Expected Results
----------------
* Success: The test will pass if the SNMP agent is enabled in the specified VRF.
* Failure: The test will fail if the SNMP agent is disabled in the specified VRF.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpStatus:
vrf: default
```
"""
description = "Verifies if the SNMP agent is enabled."
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpStatus test."""
vrf: str = "default"
"""The name of the VRF in which to check for the SNMP agent. Defaults to `default` VRF."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpStatus."""
command_output = self.instance_commands[0].json_output
if command_output["enabled"] and self.inputs.vrf in command_output["vrfs"]["snmpVrfs"]:
self.result.is_success()
else:
self.result.is_failure(f"SNMP agent disabled in vrf {self.inputs.vrf}")
class VerifySnmpIPv4Acl(AntaTest):
"""Verifies if the SNMP agent has the right number IPv4 ACL(s) configured for a specified VRF.
Expected Results
----------------
* Success: The test will pass if the SNMP agent has the provided number of IPv4 ACL(s) in the specified VRF.
* Failure: The test will fail if the SNMP agent has not the right number of IPv4 ACL(s) in the specified VRF.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpIPv4Acl:
number: 3
vrf: default
```
"""
description = "Verifies if the SNMP agent has IPv4 ACL(s) configured."
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp ipv4 access-list summary", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpIPv4Acl test."""
number: PositiveInteger
"""The number of expected IPv4 ACL(s)."""
vrf: str = "default"
"""The name of the VRF in which to check for the SNMP agent. Defaults to `default` VRF."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpIPv4Acl."""
command_output = self.instance_commands[0].json_output
ipv4_acl_list = command_output["ipAclList"]["aclList"]
ipv4_acl_number = len(ipv4_acl_list)
if ipv4_acl_number != self.inputs.number:
self.result.is_failure(f"Expected {self.inputs.number} SNMP IPv4 ACL(s) in vrf {self.inputs.vrf} but got {ipv4_acl_number}")
return
not_configured_acl = [acl["name"] for acl in ipv4_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]
if not_configured_acl:
self.result.is_failure(f"SNMP IPv4 ACL(s) not configured or active in vrf {self.inputs.vrf}: {not_configured_acl}")
else:
self.result.is_success()
class VerifySnmpIPv6Acl(AntaTest):
"""Verifies if the SNMP agent has the right number IPv6 ACL(s) configured for a specified VRF.
Expected Results
----------------
* Success: The test will pass if the SNMP agent has the provided number of IPv6 ACL(s) in the specified VRF.
* Failure: The test will fail if the SNMP agent has not the right number of IPv6 ACL(s) in the specified VRF.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpIPv6Acl:
number: 3
vrf: default
```
"""
description = "Verifies if the SNMP agent has IPv6 ACL(s) configured."
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp ipv6 access-list summary", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpIPv6Acl test."""
number: PositiveInteger
"""The number of expected IPv6 ACL(s)."""
vrf: str = "default"
"""The name of the VRF in which to check for the SNMP agent. Defaults to `default` VRF."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpIPv6Acl."""
command_output = self.instance_commands[0].json_output
ipv6_acl_list = command_output["ipv6AclList"]["aclList"]
ipv6_acl_number = len(ipv6_acl_list)
if ipv6_acl_number != self.inputs.number:
self.result.is_failure(f"Expected {self.inputs.number} SNMP IPv6 ACL(s) in vrf {self.inputs.vrf} but got {ipv6_acl_number}")
return
acl_not_configured = [acl["name"] for acl in ipv6_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]
if acl_not_configured:
self.result.is_failure(f"SNMP IPv6 ACL(s) not configured or active in vrf {self.inputs.vrf}: {acl_not_configured}")
else:
self.result.is_success()
class VerifySnmpLocation(AntaTest):
"""Verifies the SNMP location of a device.
Expected Results
----------------
* Success: The test will pass if the SNMP location matches the provided input.
* Failure: The test will fail if the SNMP location does not match the provided input.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpLocation:
location: New York
```
"""
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpLocation test."""
location: str
"""Expected SNMP location of the device."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpLocation."""
# Verifies the SNMP location is configured.
if not (location := get_value(self.instance_commands[0].json_output, "location.location")):
self.result.is_failure("SNMP location is not configured.")
return
# Verifies the expected SNMP location.
if location != self.inputs.location:
self.result.is_failure(f"Expected `{self.inputs.location}` as the location, but found `{location}` instead.")
else:
self.result.is_success()
class VerifySnmpContact(AntaTest):
"""Verifies the SNMP contact of a device.
Expected Results
----------------
* Success: The test will pass if the SNMP contact matches the provided input.
* Failure: The test will fail if the SNMP contact does not match the provided input.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpContact:
contact: Jon@example.com
```
"""
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpContact test."""
contact: str
"""Expected SNMP contact details of the device."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpContact."""
# Verifies the SNMP contact is configured.
if not (contact := get_value(self.instance_commands[0].json_output, "contact.contact")):
self.result.is_failure("SNMP contact is not configured.")
return
# Verifies the expected SNMP contact.
if contact != self.inputs.contact:
self.result.is_failure(f"Expected `{self.inputs.contact}` as the contact, but found `{contact}` instead.")
else:
self.result.is_success()
class VerifySnmpPDUCounters(AntaTest):
"""Verifies the SNMP PDU counters.
By default, all SNMP PDU counters will be checked for any non-zero values.
An optional list of specific SNMP PDU(s) can be provided for granular testing.
Expected Results
----------------
* Success: The test will pass if the SNMP PDU counter(s) are non-zero/greater than zero.
* Failure: The test will fail if the SNMP PDU counter(s) are zero/None/Not Found.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpPDUCounters:
pdus:
- outTrapPdus
- inGetNextPdus
```
"""
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpPDUCounters test."""
pdus: list[SnmpPdu] | None = None
"""Optional list of SNMP PDU counters to be verified. If not provided, test will verifies all PDU counters."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpPDUCounters."""
snmp_pdus = self.inputs.pdus
command_output = self.instance_commands[0].json_output
# Verify SNMP PDU counters.
if not (pdu_counters := get_value(command_output, "counters")):
self.result.is_failure("SNMP counters not found.")
return
# In case SNMP PDUs not provided, It will check all the update error counters.
if not snmp_pdus:
snmp_pdus = list(get_args(SnmpPdu))
failures = {pdu: value for pdu in snmp_pdus if (value := pdu_counters.get(pdu, "Not Found")) == "Not Found" or value == 0}
# Check if any failures
if not failures:
self.result.is_success()
else:
self.result.is_failure(f"The following SNMP PDU counters are not found or have zero PDU counters:\n{failures}")
class VerifySnmpErrorCounters(AntaTest):
"""Verifies the SNMP error counters.
By default, all error counters will be checked for any non-zero values.
An optional list of specific error counters can be provided for granular testing.
Expected Results
----------------
* Success: The test will pass if the SNMP error counter(s) are zero/None.
* Failure: The test will fail if the SNMP error counter(s) are non-zero/not None/Not Found or is not configured.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpErrorCounters:
error_counters:
- inVersionErrs
- inBadCommunityNames
"""
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]
class Input(AntaTest.Input):
"""Input model for the VerifySnmpErrorCounters test."""
error_counters: list[SnmpErrorCounter] | None = None
"""Optional list of SNMP error counters to be verified. If not provided, test will verifies all error counters."""
@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpErrorCounters."""
error_counters = self.inputs.error_counters
command_output = self.instance_commands[0].json_output
# Verify SNMP PDU counters.
if not (snmp_counters := get_value(command_output, "counters")):
self.result.is_failure("SNMP counters not found.")
return
# In case SNMP error counters not provided, It will check all the error counters.
if not error_counters:
error_counters = list(get_args(SnmpErrorCounter))
error_counters_not_ok = {counter: value for counter in error_counters if (value := snmp_counters.get(counter))}
# Check if any failures
if not error_counters_not_ok:
self.result.is_success()
else:
self.result.is_failure(f"The following SNMP error counters are not found or have non-zero error counters:\n{error_counters_not_ok}")