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 groups #886

Merged
merged 23 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fea9674
issue_853 Added TC for SNMP groups
Oct 16, 2024
212147d
Merge branch 'main' into issue_853
vitthalmagadum Nov 29, 2024
cb1d3d7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 29, 2024
de48bd8
Merge branch 'main' into issue_853
geetanjalimanegslab Jan 16, 2025
c383c57
Issue_853: Refactored testcase with input model changes
geetanjalimanegslab Jan 16, 2025
68a5acb
Issue_853: UUpdated docstrings,test failure message and optimized code
geetanjalimanegslab Jan 17, 2025
7886dbd
Issue_853: Updated test failure message
geetanjalimanegslab Jan 17, 2025
819c92b
Merge branch 'main' into issue_853
geetanjalimanegslab Jan 21, 2025
c12f970
Issue_853: Optimized code, added UT for view configuration check
geetanjalimanegslab Jan 21, 2025
69308b9
Issue_853: Updated testcase docstrings and fixed Cognitive Complexity…
geetanjalimanegslab Jan 21, 2025
05f1c99
Issue_853: fixed Cognitive Complexity issue
geetanjalimanegslab Jan 21, 2025
8d3d1ab
Issue_853: optimized code to fix the congnitive complexity issues
geetanjalimanegslab Jan 21, 2025
0e25fd6
Issue_853: optimized code, updated test failure messages, added snmpa…
geetanjalimanegslab Jan 23, 2025
9150e74
Issue_853: updated input model unit testcase with snmp auth version c…
geetanjalimanegslab Jan 23, 2025
08a4154
Issue_853: Added unit testcases for snmp group input model
geetanjalimanegslab Jan 23, 2025
5a5a6d8
Issue_853: Updated test failure msga nd custom_types.py file
geetanjalimanegslab Jan 23, 2025
ed0c903
Refactor: Better annotation
gmuloc Jan 28, 2025
fbe1756
Issue_853: Updated customtypes, and thier unit test for snmpv3 prefix
geetanjalimanegslab Jan 29, 2025
8822b0d
Merge branch 'main' into issue_853
geetanjalimanegslab Jan 30, 2025
488b4cb
Merge branch 'main' into issue_853
carl-baillargeon Feb 6, 2025
6e33fb2
Merge branch 'main' into issue_853
carl-baillargeon Feb 6, 2025
51b576e
Update anta/input_models/snmp.py
carl-baillargeon Feb 6, 2025
eaeb329
Updated snmp_v3_prefix
carl-baillargeon Feb 6, 2025
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
30 changes: 24 additions & 6 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,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"]
SnmpPdu = Literal["inGetPdus", "inGetNextPdus", "inSetPdus", "outGetResponsePdus", "outTrapPdus"]
SnmpErrorCounter = Literal[
"inVersionErrs", "inBadCommunityNames", "inBadCommunityUses", "inParseErrs", "outTooBigErrs", "outNoSuchNameErrs", "outBadValueErrs", "outGeneralErrs"
]
IPv4RouteType = Literal[
"connected",
"static",
Expand Down Expand Up @@ -259,8 +255,30 @@ def validate_regex(value: str) -> str:
"Route Cache Route",
"CBF Leaked Route",
]
DynamicVlanSource = Literal["dmf", "dot1x", "dynvtep", "evpn", "mlag", "mlagsync", "mvpn", "swfwd", "vccbfd"]
LogSeverityLevel = Literal["alerts", "critical", "debugging", "emergencies", "errors", "informational", "notifications", "warnings"]


########################################
# SNMP
########################################
def snmp_v3_prefix(auth_type: str) -> str:
carl-baillargeon marked this conversation as resolved.
Show resolved Hide resolved
"""Prefix the SNMP authentication type with 'v3' if it is known."""
snmp_auth_types = ["auth", "priv", "noauth"]

if auth_type.lower() in snmp_auth_types and auth_type.lower() == "noauth":
return "v3NoAuth"
if auth_type.lower() in snmp_auth_types:
return f"v3{auth_type.title()}"
msg = f"SNMP authentication type `{auth_type}` is not supported, supported types are {', '.join(snmp_auth_types)}"
raise ValueError(msg)
carl-baillargeon marked this conversation as resolved.
Show resolved Hide resolved


SnmpVersion = Literal["v1", "v2c", "v3"]
SnmpHashingAlgorithm = Literal["MD5", "SHA", "SHA-224", "SHA-256", "SHA-384", "SHA-512"]
SnmpEncryptionAlgorithm = Literal["AES-128", "AES-192", "AES-256", "DES"]
DynamicVlanSource = Literal["dmf", "dot1x", "dynvtep", "evpn", "mlag", "mlagsync", "mvpn", "swfwd", "vccbfd"]
LogSeverityLevel = Literal["alerts", "critical", "debugging", "emergencies", "errors", "informational", "notifications", "warnings"]
SnmpPdu = Literal["inGetPdus", "inGetNextPdus", "inSetPdus", "outGetResponsePdus", "outTrapPdus"]
SnmpErrorCounter = Literal[
"inVersionErrs", "inBadCommunityNames", "inBadCommunityUses", "inParseErrs", "outTooBigErrs", "outNoSuchNameErrs", "outBadValueErrs", "outGeneralErrs"
]
SnmpVersionV3AuthType = Annotated[Literal["auth", "priv", "noauth"], AfterValidator(snmp_v3_prefix)]
28 changes: 27 additions & 1 deletion anta/input_models/snmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from pydantic import BaseModel, ConfigDict

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


class SnmpHost(BaseModel):
Expand Down Expand Up @@ -72,3 +72,29 @@ def __str__(self) -> str:
- Source Interface: Ethernet1 VRF: default
"""
return f"Source Interface: {self.interface} VRF: {self.vrf}"


class SnmpGroup(BaseModel):
"""Model for a SNMP group."""
carl-baillargeon marked this conversation as resolved.
Show resolved Hide resolved

group_name: str
"""SNMP group name."""
version: SnmpVersion
"""SNMP protocol version."""
read_view: str | None = None
"""Optional field, View to restrict read access."""
write_view: str | None = None
"""Optional field, View to restrict write access."""
notify_view: str | None = None
"""Optional field, View to restrict notifications."""
authentication: SnmpVersionV3AuthType | None = None
"""SNMPv3 authentication settings. Required when version is v3."""
carl-baillargeon marked this conversation as resolved.
Show resolved Hide resolved

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

Examples
--------
- Group: Test_Group Version: v2c
"""
return f"Group: {self.group_name}, Version: {self.version}"
82 changes: 81 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 SnmpHost, SnmpSourceInterface, SnmpUser
from anta.input_models.snmp import SnmpGroup, SnmpHost, SnmpSourceInterface, SnmpUser
from anta.models import AntaCommand, AntaTest
from anta.tools import get_value

Expand Down Expand Up @@ -543,3 +543,83 @@ def test(self) -> None:
self.result.is_failure(f"{interface_details} - Not configured")
elif actual_interface != interface_details.interface:
self.result.is_failure(f"{interface_details} - Incorrect source interface - Actual: {actual_interface}")


class VerifySnmpGroup(AntaTest):
"""Verifies the SNMP group configurations for specified version(s).

This test performs the following checks:

1. Verifies that the SNMP group is configured for the specified version.
2. For SNMP version 3, verify that the security model matches the expected value.
3. Ensures that SNMP group configurations, including read, write, and notify views, align with version-specific requirements.

Expected Results
----------------
* Success: The test will pass if the provided SNMP group and all specified parameters are correctly configured.
* Failure: The test will fail if the provided SNMP group is not configured or if any specified parameter is not correctly configured.

Examples
--------
```yaml
anta.tests.snmp:
- VerifySnmpGroup:
snmp_groups:
- group_name: Group1
gmuloc marked this conversation as resolved.
Show resolved Hide resolved
version: v1
read_view: group_read_1
write_view: group_write_1
notify_view: group_notify_1
- group_name: Group2
version: v3
read_view: group_read_2
write_view: group_write_2
notify_view: group_notify_2
authentication: priv
```
"""

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

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

snmp_groups: list[SnmpGroup]
"""List of SNMP groups."""

@field_validator("snmp_groups")
@classmethod
def validate_snmp_groups(cls, snmp_groups: list[SnmpGroup]) -> list[SnmpGroup]:
"""Validate the inputs provided to the SnmpGroup class."""
for snmp_group in snmp_groups:
if snmp_group.version == "v3" and snmp_group.authentication is None:
msg = f"{snmp_group}: `authentication` field is required for `version: v3`"
raise ValueError(msg)
return snmp_groups

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifySnmpGroup."""
self.result.is_success()
for group in self.inputs.snmp_groups:
# Verify SNMP group details.
if not (group_details := get_value(self.instance_commands[0].json_output, f"groups.{group.group_name}.versions.{group.version}")):
self.result.is_failure(f"{group} - Not configured")
continue

view_types = [view_type for view_type in ["read", "write", "notify"] if getattr(group, f"{view_type}_view")]
# Verify SNMP views, the read, write and notify settings aligning with version-specific requirements.
for view_type in view_types:
expected_view = getattr(group, f"{view_type}_view")
# Verify actual view is configured.
if group_details.get(f"{view_type}View") == "":
self.result.is_failure(f"{group} View: {view_type} - Not configured")
elif (act_view := group_details.get(f"{view_type}View")) != expected_view:
self.result.is_failure(f"{group} - Incorrect {view_type.title()} view - Expected: {expected_view}, Actual: {act_view}")
elif not group_details.get(f"{view_type}ViewConfig"):
self.result.is_failure(f"{group}, {view_type.title()} View: {expected_view} - Not configured")

# For version v3, verify that the security model aligns with the expected value.
if group.version == "v3" and (actual_auth := group_details.get("secModel")) != group.authentication:
self.result.is_failure(f"{group} - Incorrect security model - Expected: {group.authentication}, Actual: {actual_auth}")
14 changes: 14 additions & 0 deletions examples/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,20 @@ anta.tests.snmp:
# Verifies the SNMP error counters.
error_counters:
- inVersionErrs
- VerifySnmpGroup:
# Verifies the SNMP group configurations for specified version(s).
snmp_groups:
- group_name: Group1
version: v1
read_view: group_read_1
write_view: group_write_1
notify_view: group_notify_1
- group_name: Group2
version: v3
read_view: group_read_2
write_view: group_write_2
notify_view: group_notify_2
authentication: priv
- VerifySnmpHostLogging:
# Verifies SNMP logging configurations.
hosts:
Expand Down
Loading
Loading