Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anta): Added the test case to verify SNMP Logging Configuration #849

Merged
merged 7 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion anta/input_models/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,30 @@

from __future__ import annotations

from ipaddress import IPv4Address

from pydantic import BaseModel, ConfigDict

from anta.custom_types import SnmpEncryptionAlgorithm, SnmpHashingAlgorithm, SnmpVersion
from anta.custom_types import Hostname, SnmpEncryptionAlgorithm, SnmpHashingAlgorithm, SnmpVersion


class SnmpHost(BaseModel):
"""Model for a SNMP host."""

model_config = ConfigDict(extra="forbid")
hostname: IPv4Address | Hostname
"""IPv4 address or hostname of the SNMP notification host."""
vrf: str = "default"
"""Optional VRF for SNMP hosts. If not provided, it defaults to `default`."""

def __str__(self) -> str:
"""Return a human-readable string representation of the SnmpHost for reporting.

Examples
--------
- Host: 192.168.1.100 VRF: default
"""
return f"Host: {self.hostname} VRF: {self.vrf}"


class SnmpUser(BaseModel):
Expand Down
73 changes: 72 additions & 1 deletion anta/tests/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from pydantic import field_validator

from anta.custom_types import PositiveInteger, SnmpErrorCounter, SnmpPdu
from anta.input_models.snmp import SnmpUser
from anta.input_models.snmp import SnmpHost, SnmpUser
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value

Expand Down Expand Up @@ -344,6 +344,77 @@ def test(self) -> None:
self.result.is_failure(f"The following SNMP error counters are not found or have non-zero error counters:\n{error_counters_not_ok}")


class VerifySnmpHostLogging(AntaTest):
"""Verifies SNMP logging configurations.

This test performs the following checks:

1. SNMP logging is enabled globally.
2. For each specified SNMP host:
- Host exists in configuration.
- Host's VRF assignment matches expected value.

Expected Results
----------------
* Success: The test will pass if all of the following conditions are met:
- SNMP logging is enabled on the device.
- All specified hosts are configured with correct VRF assignments.
* Failure: The test will fail if any of the following conditions is met:
- SNMP logging is disabled on the device.
- SNMP host not found in configuration.
- Host's VRF assignment doesn't match expected value.

Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpHostLogging:
hosts:
- hostname: 192.168.1.100
vrf: default
- hostname: 192.168.1.103
vrf: MGMT
```
"""

categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp", revision=1)]

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

hosts: list[SnmpHost]
"""List of SNMP hosts."""

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

command_output = self.instance_commands[0].json_output.get("logging", {})
# If SNMP logging is disabled, test fails.
if not command_output.get("loggingEnabled"):
self.result.is_failure("SNMP logging is disabled")
return

host_details = command_output.get("hosts", {})

for host in self.inputs.hosts:
hostname = str(host.hostname)
vrf = host.vrf
actual_snmp_host = host_details.get(hostname, {})

# If SNMP host is not configured on the device, test fails.
if not actual_snmp_host:
self.result.is_failure(f"{host} - Not configured")
continue

# If VRF is not matches the expected value, test fails.
actual_vrf = "default" if (vrf_name := actual_snmp_host.get("vrf")) == "" else vrf_name
if actual_vrf != vrf:
self.result.is_failure(f"{host} - Incorrect VRF - Actual: {actual_vrf}")


class VerifySnmpUser(AntaTest):
"""Verifies the SNMP user configurations.

Expand Down
7 changes: 7 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,13 @@ anta.tests.snmp:
# Verifies the SNMP error counters.
error_counters:
- inVersionErrs
- VerifySnmpHostLogging:
# Verifies SNMP logging configurations.
hosts:
- hostname: 192.168.1.100
vrf: default
- hostname: 192.168.1.103
vrf: MGMT
- VerifySnmpIPv4Acl:
# Verifies if the SNMP agent has IPv4 ACL(s) configured.
number: 3
Expand Down
52 changes: 52 additions & 0 deletions tests/units/anta_tests/test_snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from anta.tests.snmp import (
VerifySnmpContact,
VerifySnmpErrorCounters,
VerifySnmpHostLogging,
VerifySnmpIPv4Acl,
VerifySnmpIPv6Acl,
VerifySnmpLocation,
Expand Down Expand Up @@ -320,6 +321,57 @@
],
},
},
{
"name": "success",
"test": VerifySnmpHostLogging,
"eos_data": [
{
"logging": {
"loggingEnabled": True,
"hosts": {
"192.168.1.100": {"port": 162, "vrf": ""},
"192.168.1.101": {"port": 162, "vrf": "MGMT"},
"snmp-server-01": {"port": 162, "vrf": "default"},
},
}
}
],
"inputs": {
"hosts": [
{"hostname": "192.168.1.100", "vrf": "default"},
{"hostname": "192.168.1.101", "vrf": "MGMT"},
{"hostname": "snmp-server-01", "vrf": "default"},
]
},
"expected": {"result": "success"},
},
{
"name": "failure-logging-disabled",
"test": VerifySnmpHostLogging,
"eos_data": [{"logging": {"loggingEnabled": False}}],
"inputs": {"hosts": [{"hostname": "192.168.1.100", "vrf": "default"}, {"hostname": "192.168.1.101", "vrf": "MGMT"}]},
"expected": {"result": "failure", "messages": ["SNMP logging is disabled"]},
},
{
"name": "failure-mismatch-vrf",
"test": VerifySnmpHostLogging,
"eos_data": [{"logging": {"loggingEnabled": True, "hosts": {"192.168.1.100": {"port": 162, "vrf": "MGMT"}, "192.168.1.101": {"port": 162, "vrf": "Test"}}}}],
"inputs": {"hosts": [{"hostname": "192.168.1.100", "vrf": "default"}, {"hostname": "192.168.1.101", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"messages": ["Host: 192.168.1.100 VRF: default - Incorrect VRF - Actual: MGMT", "Host: 192.168.1.101 VRF: MGMT - Incorrect VRF - Actual: Test"],
},
},
{
"name": "failure-host-not-configured",
"test": VerifySnmpHostLogging,
"eos_data": [{"logging": {"loggingEnabled": True, "hosts": {"192.168.1.100": {"port": 162, "vrf": "MGMT"}, "192.168.1.103": {"port": 162, "vrf": "Test"}}}}],
"inputs": {"hosts": [{"hostname": "192.168.1.101", "vrf": "default"}, {"hostname": "192.168.1.102", "vrf": "MGMT"}]},
"expected": {
"result": "failure",
"messages": ["Host: 192.168.1.101 VRF: default - Not configured", "Host: 192.168.1.102 VRF: MGMT - Not configured"],
},
},
{
"name": "success",
"test": VerifySnmpUser,
Expand Down
Loading