Skip to content

Commit

Permalink
feat: Add feature to search config (#634)
Browse files Browse the repository at this point in the history
  • Loading branch information
chetryan authored Apr 19, 2024
1 parent 79011e5 commit c87e62c
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 2 deletions.
11 changes: 11 additions & 0 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ def bgp_multiprotocol_capabilities_abbreviations(value: str) -> str:
return value


def validate_regex(value: str) -> str:
"""Validate that the input value is a valid regex format."""
try:
re.compile(value)
except re.error as e:
msg = f"Invalid regex: {e}"
raise ValueError(msg) from e
return value


# ANTA framework
TestStatus = Literal["unset", "success", "failure", "error", "skipped"]

Expand Down Expand Up @@ -129,3 +139,4 @@ def bgp_multiprotocol_capabilities_abbreviations(value: str) -> str:
Revision = Annotated[int, Field(ge=1, le=99)]
Hostname = Annotated[str, Field(pattern=r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$")]
Port = Annotated[int, Field(ge=1, le=65535)]
RegexString = Annotated[str, AfterValidator(validate_regex)]
56 changes: 56 additions & 0 deletions anta/tests/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
# mypy: disable-error-code=attr-defined
from __future__ import annotations

import re
from typing import TYPE_CHECKING, ClassVar

from anta.custom_types import RegexString
from anta.models import AntaCommand, AntaTest

if TYPE_CHECKING:
Expand Down Expand Up @@ -75,3 +77,57 @@ def test(self) -> None:
self.result.is_success()
else:
self.result.is_failure(command_output)


class VerifyRunningConfigLines(AntaTest):
"""Verifies the given regular expression patterns are present in the running-config.
!!! warning
Since this uses regular expression searches on the whole running-config, it can
drastically impact performance and should only be used if no other test is available.
If possible, try using another ANTA test that is more specific.
Expected Results
----------------
* Success: The test will pass if all the patterns are found in the running-config.
* Failure: The test will fail if any of the patterns are NOT found in the running-config.
Examples
--------
```yaml
anta.tests.configuration:
- VerifyRunningConfigLines:
regex_patterns:
- "^enable password.*$"
- "bla bla"
```
"""

name = "VerifyRunningConfigLines"
description = "Search the Running-Config for the given RegEx patterns."
categories: ClassVar[list[str]] = ["configuration"]
commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show running-config", ofmt="text")]

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

regex_patterns: list[RegexString]
"""List of regular expressions."""

@AntaTest.anta_test
def test(self) -> None:
"""Main test function for VerifyRunningConfigLines."""
failure_msgs = []
command_output = self.instance_commands[0].text_output

for pattern in self.inputs.regex_patterns:
re_search = re.compile(pattern, flags=re.MULTILINE)

if not re_search.search(command_output):
failure_msgs.append(f"'{pattern}'")

if not failure_msgs:
self.result.is_success()
else:
self.result.is_failure("Following patterns were not found: " + ",".join(failure_msgs))
41 changes: 39 additions & 2 deletions tests/units/anta_tests/test_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Any

from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyZeroTouch
from anta.tests.configuration import VerifyRunningConfigDiffs, VerifyRunningConfigLines, VerifyZeroTouch
from tests.lib.anta import test # noqa: F401; pylint: disable=W0611

DATA: list[dict[str, Any]] = [
Expand All @@ -32,5 +32,42 @@
"inputs": None,
"expected": {"result": "success"},
},
{"name": "failure", "test": VerifyRunningConfigDiffs, "eos_data": ["blah blah"], "inputs": None, "expected": {"result": "failure", "messages": ["blah blah"]}},
{
"name": "failure",
"test": VerifyRunningConfigDiffs,
"eos_data": ["blah blah"],
"inputs": None,
"expected": {"result": "failure", "messages": ["blah blah"]},
},
{
"name": "success",
"test": VerifyRunningConfigLines,
"eos_data": ["blah blah"],
"inputs": {"regex_patterns": ["blah"]},
"expected": {"result": "success"},
},
{
"name": "success",
"test": VerifyRunningConfigLines,
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["^enable password .*$", "^.*other line$"]},
"expected": {"result": "success"},
},
{
"name": "failure",
"test": VerifyRunningConfigLines,
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["bla", "bleh"]},
"expected": {"result": "failure", "messages": ["Following patterns were not found: 'bla','bleh'"]},
},
{
"name": "failure-invalid-regex",
"test": VerifyRunningConfigLines,
"eos_data": ["enable password something\nsome other line"],
"inputs": {"regex_patterns": ["["]},
"expected": {
"result": "error",
"messages": ["1 validation error for Input\nregex_patterns.0\n Value error, Invalid regex: unterminated character set at position 0"],
},
},
]

0 comments on commit c87e62c

Please sign in to comment.