Skip to content

Commit

Permalink
feat(anta): Added the test case to Verify SNMP Notification Host (#838)
Browse files Browse the repository at this point in the history
* iisue_822 Added TC for SNMP Notification host

* issue_822 Handling review comments: updated failure msgs and docstring

* issue_822 Updated after resolved conflicts

* Issue_822: Refactored testcase with latest inputs changes

* Issue_822: Updated unitestcase,docstrings and  added check for incorrect VRF.

* fixing the sonar issues

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Issue_822: Updated testcase to fix sonar issues of max conditional check

* Issue_822: Updated docstrings and test description

* Issue_822: Updated docstrings and variable names

* Issue_822: Fixed pre-commit issues

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Issue_822: Updated docstrings and test failure messages

* Issue_822: Updated Examples from the docstrings

---------

Co-authored-by: VitthalMagadum <[email protected]>
Co-authored-by: Geetanjali.mane <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Guillaume Mulocher <[email protected]>
  • Loading branch information
5 people authored Jan 29, 2025
1 parent d402ed1 commit 9c7a81c
Show file tree
Hide file tree
Showing 5 changed files with 485 additions and 5 deletions.
17 changes: 14 additions & 3 deletions anta/input_models/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,31 @@
from __future__ import annotations

from ipaddress import IPv4Address
from typing import Literal

from pydantic import BaseModel, ConfigDict

from anta.custom_types import Hostname, Interface, SnmpEncryptionAlgorithm, SnmpHashingAlgorithm, SnmpVersion
from anta.custom_types import Hostname, Interface, Port, 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."""
"""IPv4 address or Hostname of the SNMP notification host."""
vrf: str = "default"
"""Optional VRF for SNMP hosts. If not provided, it defaults to `default`."""
"""Optional VRF for SNMP Hosts. If not provided, it defaults to `default`."""
notification_type: Literal["trap", "inform"] = "trap"
"""Type of SNMP notification (trap or inform), it defaults to trap."""
version: SnmpVersion | None = None
"""SNMP protocol version. Required field in the `VerifySnmpNotificationHost` test."""
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,required for SNMP version is v1 or v2c. Can be provided in the `VerifySnmpNotificationHost` test."""
user: str | None = None
"""Optional SNMP user for authentication, required for SNMP version v3. Can be provided in the `VerifySnmpNotificationHost` test."""

def __str__(self) -> str:
"""Return a human-readable string representation of the SnmpHost for reporting.
Expand Down
121 changes: 121 additions & 0 deletions anta/tests/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,127 @@ def test(self) -> None:
self.result.is_failure(f"{user} - Incorrect privacy type - Expected: {user.priv_type} Actual: {act_encryption}")


class VerifySnmpNotificationHost(AntaTest):
"""Verifies the SNMP notification host(s) (SNMP manager) configurations.
This test performs the following checks for each specified host:
1. Verifies that the SNMP host(s) is configured on the device.
2. Verifies that the notification type ("trap" or "inform") matches the expected value.
3. Ensures that UDP port provided matches the expected value.
4. Ensures the following depending on SNMP version:
- For SNMP version v1/v2c, a valid community string is set and matches the expected value.
- For SNMP version v3, a valid user field is set and matches the expected value.
Expected Results
----------------
* Success: The test will pass if all of the following conditions are met:
- The SNMP host(s) is configured on the device.
- The notification type ("trap" or "inform") and UDP port match the expected value.
- Ensures the following depending on SNMP version:
- For SNMP version v1/v2c, a community string is set and it matches the expected value.
- For SNMP version v3, a valid user field is set and matches the expected value.
* Failure: The test will fail if any of the following conditions is met:
- The SNMP host(s) is not configured on the device.
- The notification type ("trap" or "inform") or UDP port do not matches the expected value.
- Ensures the following depending on SNMP version:
- For SNMP version v1/v2c, a community string is not matches the expected value.
- For SNMP version v3, an user field is not matches the expected value.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpNotificationHost:
notification_hosts:
- hostname: spine
vrf: default
notification_type: trap
version: v1
udp_port: 162
community_string: public
- hostname: 192.168.1.100
vrf: default
notification_type: trap
version: v3
udp_port: 162
user: public
```
"""

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 host(s)."""

@field_validator("notification_hosts")
@classmethod
def validate_notification_hosts(cls, notification_hosts: list[SnmpHost]) -> list[SnmpHost]:
"""Validate that all required fields are provided in each SNMP Notification Host."""
for host in notification_hosts:
if host.version is None:
msg = f"{host}; 'version' field missing in the input"
raise ValueError(msg)
if host.version in ["v1", "v2c"] and host.community_string is None:
msg = f"{host} Version: {host.version}; 'community_string' field missing in the input"
raise ValueError(msg)
if host.version == "v3" and host.user is None:
msg = f"{host} Version: {host.version}; 'user' field missing in the input"
raise ValueError(msg)
return notification_hosts

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

# If SNMP is not configured, test fails.
if not (snmp_hosts := get_value(self.instance_commands[0].json_output, "hosts")):
self.result.is_failure("No SNMP host is configured.")
return

for host in self.inputs.notification_hosts:
vrf = "" if host.vrf == "default" else host.vrf
hostname = str(host.hostname)
notification_type = host.notification_type
version = host.version
udp_port = host.udp_port
community_string = host.community_string
user = host.user
default_value = "Not Found"

host_details = next(
(host for host in snmp_hosts if (host.get("hostname") == hostname and host.get("protocolVersion") == version and host.get("vrf") == vrf)), None
)
# If expected SNMP host is not configured with the specified protocol version, test fails.
if not host_details:
self.result.is_failure(f"{host} Version: {version} - Not configured")
continue

# If actual notification type does not match the expected value, test fails.
if notification_type != (actual_notification_type := get_value(host_details, "notificationType", default_value)):
self.result.is_failure(f"{host} - Incorrect notification type - Expected: {notification_type}, Actual: {actual_notification_type}")

# If actual UDP port does not match the expected value, test fails.
if udp_port != (actual_udp_port := get_value(host_details, "port", default_value)):
self.result.is_failure(f"{host} - Incorrect UDP port - Expected: {udp_port}, Actual: {actual_udp_port}")

user_found = user != (actual_user := get_value(host_details, "v3Params.user", default_value))
version_user_check = (version == "v3", user_found)

# If SNMP protocol version is v1 or v2c and actual community string does not match the expected value, test fails.
if version in ["v1", "v2c"] and community_string != (actual_community_string := get_value(host_details, "v1v2cParams.communityString", default_value)):
self.result.is_failure(f"{host} Version: {version} - Incorrect community string - Expected: {community_string}, Actual: {actual_community_string}")

# If SNMP protocol version is v3 and actual user does not match the expected value, test fails.
elif all(version_user_check):
self.result.is_failure(f"{host} Version: {version} - Incorrect user - Expected: {user}, Actual: {actual_user}")


class VerifySnmpSourceInterface(AntaTest):
"""Verifies SNMP source interfaces.
Expand Down
15 changes: 15 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,21 @@ anta.tests.snmp:
- VerifySnmpLocation:
# Verifies the SNMP location of a device.
location: New York
- VerifySnmpNotificationHost:
# Verifies the SNMP notification host(s) (SNMP manager) configurations.
notification_hosts:
- hostname: spine
vrf: default
notification_type: trap
version: v1
udp_port: 162
community_string: public
- hostname: 192.168.1.100
vrf: default
notification_type: trap
version: v3
udp_port: 162
user: public
- VerifySnmpPDUCounters:
# Verifies the SNMP PDU counters.
pdus:
Expand Down
Loading

0 comments on commit 9c7a81c

Please sign in to comment.