Skip to content

Commit

Permalink
Added TC for SNMP user
Browse files Browse the repository at this point in the history
  • Loading branch information
VitthalMagadum committed Oct 11, 2024
1 parent 387c281 commit 8dd61b3
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 3 deletions.
3 changes: 3 additions & 0 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,6 @@ def validate_regex(value: str) -> str:
]
BgpUpdateError = Literal["inUpdErrWithdraw", "inUpdErrIgnore", "inUpdErrDisableAfiSafi", "disabledAfiSafi", "lastUpdErrTime"]
BfdProtocol = Literal["bgp", "isis", "lag", "ospf", "ospfv3", "pim", "route-input", "static-bfd", "static-route", "vrrp", "vxlan"]
SnmpVersion = Literal["v1", "v2c", "v3"]
HashingAlgorithms = Literal["MD5", "SHA", "SHA-224", "SHA-256", "SHA-384", "SHA-512"]
EncryptionAlgorithms = Literal["AES-128", "AES-192", "AES-256", "DES"]
106 changes: 104 additions & 2 deletions anta/tests/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

from typing import TYPE_CHECKING, ClassVar

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

from anta.custom_types import EncryptionAlgorithms, HashingAlgorithms, PositiveInteger, SnmpVersion
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value
from anta.tools import get_failed_logs, get_value

if TYPE_CHECKING:
from anta.models import AntaTemplate
Expand Down Expand Up @@ -237,3 +239,103 @@ 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 VerifySnmpUser(AntaTest):
"""Verifies the SNMP user configurations for specified version(s).
- Verifies that the valid user name and group name.
- Ensures that the SNMP v3 security model, the user authentication and privacy settings aligning with version-specific requirements.
Expected Results
----------------
* Success: The test will pass if the provided SNMP user and all specified parameters are correctly configured.
* Failure: The test will fail if the provided SNMP user is not configured or specified parameters are not correctly configured.
Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpUser:
users:
- username: test
group_name: test_group
security_model: v3
authentication_type: MD5
priv_type: AES-128
```
"""

name = "VerifySnmpUser"
description = "Verifies the SNMP user configurations for specified version(s)."
categories: ClassVar[list[str]] = ["snmp"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show snmp user", revision=1)]

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

users: list[SnmpUser]
"""List of SNMP users."""

class SnmpUser(BaseModel):
"""Model for a SNMP User."""

username: str
"""SNMP user name."""
group_name: str
"""SNMP group for the user."""
security_model: SnmpVersion
"""SNMP protocol version.."""
authentication_type: HashingAlgorithms | None = None
"""User authentication settings."""
priv_type: EncryptionAlgorithms | None = None
"""User privacy settings."""

@model_validator(mode="after")
def validate_inputs(self: BaseModel) -> BaseModel:
"""Validate the inputs provided to the SnmpUser class."""
if self.security_model in ["v1", "v2c"] and (self.authentication_type or self.priv_type) is not None:
msg = "SNMP versions 1 and 2c, do not support encryption or advanced authentication."
raise ValueError(msg)
return self

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

for user in self.inputs.users:
username = user.username
group_name = user.group_name
security_model = user.security_model
authentication_type = user.authentication_type
priv_type = user.priv_type

# Verify SNMP host details.
if not (user_details := get_value(self.instance_commands[0].json_output, f"usersByVersion.{security_model}.users.{username}")):
failures += f"SNMP user '{username}' is not configured with security model '{security_model}'.\n"
continue

# Update expected host details.
expected_user_details = {"user group": group_name}

# Update actual host details.
actual_user_details = {"user group": user_details.get("groupName", "Not Found")}

if authentication_type:
expected_user_details["authentication type"] = authentication_type
actual_user_details["authentication type"] = user_details.get("v3Params", {}).get("authType", "Not Found")

if priv_type:
expected_user_details["privacy type"] = priv_type
actual_user_details["privacy type"] = user_details.get("v3Params", {}).get("privType", "Not Found")

# Collecting failures logs if any.
failure_logs = get_failed_logs(expected_user_details, actual_user_details)
if failure_logs:
failures += f"For SNMP user {username}:{failure_logs}\n"

# Check if there are any failures.
if failures:
self.result.is_failure(failures)
11 changes: 11 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,17 @@ anta.tests.snmp:
location: New York
- VerifySnmpContact:
contact: [email protected]
- VerifySnmpUser:
users:
- username: Test1
group_name: TestGroup1
security_model: v3
authentication_type: MD5
priv_type: AES-128
- username: Test2
group_name: TestGroup2
security_model: v3
authentication_type: SHA-256

anta.tests.software:
- VerifyEOSVersion:
Expand Down
134 changes: 133 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, VerifySnmpStatus, VerifySnmpUser
from tests.units.anta_tests import test

DATA: list[dict[str, Any]] = [
Expand Down Expand Up @@ -152,4 +152,136 @@
"messages": ["SNMP contact is not configured."],
},
},
{
"name": "success",
"test": VerifySnmpUser,
"eos_data": [
{
"usersByVersion": {
"v1": {
"users": {
"Test1": {
"groupName": "TestGroup1",
},
}
},
"v2c": {
"users": {
"Test2": {
"groupName": "TestGroup2",
},
}
},
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup3",
"v3Params": {"authType": "SHA-384", "privType": "AES-128"},
},
"Test4": {"groupName": "TestGroup3", "v3Params": {"authType": "SHA-512", "privType": "AES-192"}},
}
},
}
}
],
"inputs": {
"users": [
{"username": "Test1", "group_name": "TestGroup1", "security_model": "v1"},
{"username": "Test2", "group_name": "TestGroup2", "security_model": "v2c"},
{"username": "Test3", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-384", "priv_type": "AES-128"},
{"username": "Test4", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-512", "priv_type": "AES-192"},
]
},
"expected": {"result": "success"},
},
{
"name": "failure-not-configured",
"test": VerifySnmpUser,
"eos_data": [
{
"usersByVersion": {
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup3",
"v3Params": {"authType": "SHA-384", "privType": "AES-128"},
},
}
},
}
}
],
"inputs": {
"users": [
{"username": "Test1", "group_name": "TestGroup1", "security_model": "v1"},
{"username": "Test2", "group_name": "TestGroup2", "security_model": "v2c"},
{"username": "Test3", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-384", "priv_type": "AES-128"},
{"username": "Test4", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-512", "priv_type": "AES-192"},
]
},
"expected": {
"result": "failure",
"messages": [
"SNMP user 'Test1' is not configured with security model 'v1'.\n"
"SNMP user 'Test2' is not configured with security model 'v2c'.\n"
"SNMP user 'Test4' is not configured with security model 'v3'."
],
},
},
{
"name": "failure-incorrect-configure",
"test": VerifySnmpUser,
"eos_data": [
{
"usersByVersion": {
"v1": {
"users": {
"Test1": {
"groupName": "TestGroup2",
},
}
},
"v2c": {
"users": {
"Test2": {
"groupName": "TestGroup1",
},
}
},
"v3": {
"users": {
"Test3": {
"groupName": "TestGroup4",
"v3Params": {"authType": "SHA-512", "privType": "AES-192"},
},
"Test4": {"groupName": "TestGroup4", "v3Params": {"authType": "SHA-384", "privType": "AES-128"}},
}
},
}
}
],
"inputs": {
"users": [
{"username": "Test1", "group_name": "TestGroup1", "security_model": "v1"},
{"username": "Test2", "group_name": "TestGroup2", "security_model": "v2c"},
{"username": "Test3", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-384", "priv_type": "AES-128"},
{"username": "Test4", "group_name": "TestGroup3", "security_model": "v3", "authentication_type": "SHA-512", "priv_type": "AES-192"},
]
},
"expected": {
"result": "failure",
"messages": [
"For SNMP user Test1:\nExpected `TestGroup1` as the user group, but found `TestGroup2` instead.\n"
"For SNMP user Test2:\nExpected `TestGroup2` as the user group, but found `TestGroup1` instead.\n"
"For SNMP user Test3:\n"
"Expected `TestGroup3` as the user group, but found `TestGroup4` instead.\n"
"Expected `SHA-384` as the authentication type, but found `SHA-512` instead.\n"
"Expected `AES-128` as the privacy type, but found `AES-192` instead.\n"
"For SNMP user Test4:\n"
"Expected `TestGroup3` as the user group, but found `TestGroup4` instead.\n"
"Expected `SHA-512` as the authentication type, but found `SHA-384` instead.\n"
"Expected `AES-192` as the privacy type, but found `AES-128` instead."
],
},
},
]

0 comments on commit 8dd61b3

Please sign in to comment.