Skip to content

Commit

Permalink
iisue_822 Added TC for SNMP Notification host
Browse files Browse the repository at this point in the history
  • Loading branch information
VitthalMagadum committed Sep 26, 2024
1 parent 0c6b66c commit d8fb591
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 4 deletions.
127 changes: 124 additions & 3 deletions anta/tests/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
# mypy: disable-error-code=attr-defined
from __future__ import annotations

from typing import TYPE_CHECKING, ClassVar
from ipaddress import IPv4Address
from typing import TYPE_CHECKING, ClassVar, Literal

from anta.custom_types import PositiveInteger
from pydantic import BaseModel, model_validator

from anta.custom_types import Port, PositiveInteger
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value
from anta.tools import get_failed_logs, get_item, get_value

if TYPE_CHECKING:
from anta.models import AntaTemplate
Expand Down Expand Up @@ -237,3 +240,121 @@ def test(self) -> None:
self.result.is_failure(f"Expected `{self.inputs.contact}` as the contact, but found `{contact}` instead.")
else:
self.result.is_success()


class VerifySNMPNotificationHost(AntaTest):
"""Verifies the SNMP notification host (SNMP manager) configurations.
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:
- VerifySNMPNotificationHost:
notification_hosts:
- hostname: 192.168.1.100
vrf: default
notification_type: trap
version: v1
udp_port: 162
community_string: public
user: public
```
"""

name = "VerifySNMPNotificationHost"
description = "Verifies the SNMP notification host (SNMP manager) configurations."
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp notification host", revision=1)]

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

notification_hosts: list[SNMPHost]
"""List of SNMP hosts."""

class SNMPHost(BaseModel):
"""Model for a SNMP Manager."""

hostname: IPv4Address
"""IP address of the SNMP notification host."""
vrf: str = "default"
"""Optional VRF for SNMP Hosts. If not provided, it defaults to `default`."""
notification_type: Literal["trap", "inform"]
"""Type of SNMP notification (trap or inform)."""
version: Literal["v1", "v2c", "v3"]
"""SNMP protocol version."""
udp_port: Port | int = 162
"""UDP port for SNMP. If not provided then defaults to 162."""
community_string: str | None = None
"""Optional SNMP community string for authentication."""
user: str | None = None
"""Optional SNMP user for authentication."""

@model_validator(mode="after")
def validate_inputs(self: BaseModel) -> BaseModel:
"""Validate the inputs provided to the SNMPHost class.
If SNMP version is either v1 or v2c, community string must be provided.
If SNMP version is v3, user must be provided.
"""
if self.version in ["v1", "v2c"] and self.community_string is None:
msg = "Community string must be provided when SNMP Protocol version is either v1 or v2c."
raise ValueError(msg)
if self.version == "v3" and self.user is None:
msg = "User must be provided when SNMP Protocol version is v3."
raise ValueError(msg)
return self

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySNMPNotificationHost."""
failures: str = ""

for host in self.inputs.notification_hosts:
hostname = str(host.hostname)
vrf = host.vrf
version = host.version
notification_type = host.notification_type
udp_port = host.udp_port
community_string = host.community_string
user = host.user

# Verify SNMP host details.
if not (host_details := get_item(self.instance_commands[0].json_output["hosts"], "hostname", hostname)):
failures += f"Details not found for SNMP host '{hostname}'.\n"
continue

# Update expected host details.
expected_host_details = {"vrf": vrf, "notification type": notification_type, "udp port": udp_port}

# Update actual host details.
actual_host_details = {"notification type": host_details.get("notificationType", "Not Found"), "udp port": host_details.get("port", "Not Found")}

# Verify SNMP protocol version.
if version in ["v1", "v2c"]:
expected_host_details["community_string"] = community_string
actual_host_details["community_string"] = host_details.get("v1v2cParams", {}).get("communityString", "Not Found")

if version == "v3":
expected_host_details["user"] = user
actual_host_details["user"] = host_details.get("v3Params", {}).get("user", "Not Found")

# Verify the VRF for SNMP Hosts. If vrf is default then command output consists empty string.
actual_host_details["vrf"] = "default" if (vrf_name := host_details.get("vrf")) == "" else "Not Found" if vrf_name is None else vrf_name

# Collecting failures logs if any.
failure_logs = get_failed_logs(expected_host_details, actual_host_details)
if failure_logs:
failures += f"For SNMP host {hostname}:{failure_logs}\n"

# Check if there are any failures.
if not failures:
self.result.is_success()
else:
self.result.is_failure(failures)
16 changes: 16 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,20 @@ anta.tests.snmp:
location: New York
- VerifySnmpContact:
contact: [email protected]
- VerifySNMPNotificationHost:
notification_hosts:
- hostname: 192.168.1.100
vrf: default
notification_type: trap
version: v3
udp_port: 162
user: public
- hostname: 192.168.1.101
vrf: default
notification_type: trap
version: v2c
udp_port: 162
community_string: public

anta.tests.software:
- VerifyEOSVersion:
Expand Down Expand Up @@ -427,6 +441,8 @@ anta.tests.stp:
instances:
- 10
- 20
- VerifyStpTopologyChanges:
threshold: 10

anta.tests.stun:
- VerifyStunClient:
Expand Down
93 changes: 92 additions & 1 deletion tests/units/anta_tests/test_snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Any

from anta.tests.snmp import VerifySnmpContact, VerifySnmpIPv4Acl, VerifySnmpIPv6Acl, VerifySnmpLocation, VerifySnmpStatus
from anta.tests.snmp import VerifySnmpContact, VerifySnmpIPv4Acl, VerifySnmpIPv6Acl, VerifySnmpLocation, VerifySNMPNotificationHost, VerifySnmpStatus
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -152,4 +152,95 @@
"messages": ["SNMP contact is not configured."],
},
},
{
"name": "success",
"test": VerifySNMPNotificationHost,
"eos_data": [
{
"hosts": [
{
"hostname": "192.168.1.100",
"port": 162,
"vrf": "",
"notificationType": "trap",
"protocolVersion": "v3",
"v3Params": {"user": "public", "securityLevel": "authNoPriv"},
},
{
"hostname": "192.168.1.101",
"port": 162,
"vrf": "",
"notificationType": "trap",
"protocolVersion": "v2c",
"v1v2cParams": {"communityString": "public"},
},
]
}
],
"inputs": {
"notification_hosts": [
{"hostname": "192.168.1.100", "vrf": "default", "notification_type": "trap", "version": "v3", "udp_port": 162, "user": "public"},
{"hostname": "192.168.1.101", "vrf": "default", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {"result": "success"},
},
{
"name": "failure-details-not-found",
"test": VerifySNMPNotificationHost,
"eos_data": [{"hosts": []}],
"inputs": {
"notification_hosts": [
{"hostname": "192.168.1.100", "vrf": "default", "notification_type": "trap", "version": "v3", "udp_port": 162, "user": "public"},
{"hostname": "192.168.1.101", "vrf": "default", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {"result": "failure", "messages": ["Details not found for SNMP host '192.168.1.100'.\nDetails not found for SNMP host '192.168.1.101'.\n"]},
},
{
"name": "failure-incorrect-config",
"test": VerifySNMPNotificationHost,
"eos_data": [
{
"hosts": [
{
"hostname": "192.168.1.100",
"port": 163,
"vrf": "",
"notificationType": "inform",
"protocolVersion": "v3",
"v3Params": {"user": "public1", "securityLevel": "authNoPriv"},
},
{
"hostname": "192.168.1.101",
"port": 163,
"vrf": "MGMT",
"notificationType": "inform",
"protocolVersion": "v2c",
"v1v2cParams": {"communityString": "public1"},
},
]
}
],
"inputs": {
"notification_hosts": [
{"hostname": "192.168.1.100", "vrf": "default", "notification_type": "trap", "version": "v3", "udp_port": 162, "user": "public"},
{"hostname": "192.168.1.101", "vrf": "default", "notification_type": "trap", "version": "v2c", "udp_port": 162, "community_string": "public"},
]
},
"expected": {
"result": "failure",
"messages": [
"For SNMP host 192.168.1.100:\n"
"Expected `trap` as the notification type, but found `inform` instead.\n"
"Expected `162` as the udp port, but found `163` instead.\n"
"Expected `public` as the user, but found `public1` instead.\n"
"For SNMP host 192.168.1.101:\n"
"Expected `default` as the vrf, but found `MGMT` instead.\n"
"Expected `trap` as the notification type, but found `inform` instead.\n"
"Expected `162` as the udp port, but found `163` instead.\n"
"Expected `public` as the community_string, but found `public1` instead.\n"
],
},
},
]

0 comments on commit d8fb591

Please sign in to comment.