diff --git a/anta/tests/services.py b/anta/tests/services.py new file mode 100644 index 000000000..7b8dbc3c9 --- /dev/null +++ b/anta/tests/services.py @@ -0,0 +1,40 @@ +# Copyright (c) 2023-2024 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +""" +Test functions related to the EOS various services settings +""" +from __future__ import annotations + +# Mypy does not understand AntaTest.Input typing +# mypy: disable-error-code=attr-defined +from anta.models import AntaCommand, AntaTest + + +class VerifyHostname(AntaTest): + """ + This class verifies the hostname of a device. + Expected results: + * success: The test will pass if the hostname matches the provided input. + * failure: The test will fail if the hostname does not match the provided input. + """ + + name = "VerifyHostname" + description = "Verifies the hostname of a device." + categories = ["services"] + commands = [AntaCommand(command="show hostname")] + + class Input(AntaTest.Input): + """Defines the input parameters for this test case.""" + + hostname: str + """Expected hostname of the device.""" + + @AntaTest.anta_test + def test(self) -> None: + hostname = self.instance_commands[0].json_output["hostname"] + + if hostname != self.inputs.hostname: + self.result.is_failure(f"Expected `{self.inputs.hostname}` as the hostname, but found `{hostname}` instead.") + else: + self.result.is_success() diff --git a/anta/tests/snmp.py b/anta/tests/snmp.py index 7dd7bf110..39d94245c 100644 --- a/anta/tests/snmp.py +++ b/anta/tests/snmp.py @@ -114,3 +114,63 @@ def test(self) -> None: self.result.is_failure(f"SNMP IPv6 ACL(s) not configured or active in vrf {self.inputs.vrf}: {not_configured_acl_list}") else: self.result.is_success() + + +class VerifySnmpLocation(AntaTest): + """ + This class verifies the SNMP location of a device. + + Expected results: + * success: The test will pass if the SNMP location matches the provided input. + * failure: The test will fail if the SNMP location does not match the provided input. + """ + + name = "VerifySnmpLocation" + description = "Verifies the SNMP location of a device." + categories = ["snmp"] + commands = [AntaCommand(command="show snmp")] + + class Input(AntaTest.Input): + """Defines the input parameters for this test case.""" + + location: str + """Expected SNMP location of the device.""" + + @AntaTest.anta_test + def test(self) -> None: + location = self.instance_commands[0].json_output["location"]["location"] + + if location != self.inputs.location: + self.result.is_failure(f"Expected `{self.inputs.location}` as the location, but found `{location}` instead.") + else: + self.result.is_success() + + +class VerifySnmpContact(AntaTest): + """ + This class verifies the SNMP contact of a device. + + Expected results: + * success: The test will pass if the SNMP contact matches the provided input. + * failure: The test will fail if the SNMP contact does not match the provided input. + """ + + name = "VerifySnmpContact" + description = "Verifies the SNMP contact of a device." + categories = ["snmp"] + commands = [AntaCommand(command="show snmp")] + + class Input(AntaTest.Input): + """Defines the input parameters for this test case.""" + + contact: str + """Expected SNMP contact details of the device.""" + + @AntaTest.anta_test + def test(self) -> None: + contact = self.instance_commands[0].json_output["contact"]["contact"] + + if contact != self.inputs.contact: + self.result.is_failure(f"Expected `{self.inputs.contact}` as the contact, but found `{contact}` instead.") + else: + self.result.is_success() diff --git a/docs/api/tests.md b/docs/api/tests.md index 48fc131f9..b3920633b 100644 --- a/docs/api/tests.md +++ b/docs/api/tests.md @@ -23,6 +23,7 @@ This section describes all the available tests provided by ANTA package. - [Routing BGP](tests.routing.bgp.md) - [Routing OSPF](tests.routing.ospf.md) - [Security](tests.security.md) +- [Services](tests.services.md) - [SNMP](tests.snmp.md) - [Software](tests.software.md) - [STP](tests.stp.md) diff --git a/docs/api/tests.services.md b/docs/api/tests.services.md new file mode 100644 index 000000000..82a7b38ad --- /dev/null +++ b/docs/api/tests.services.md @@ -0,0 +1,13 @@ + + +# ANTA catalog for services tests + +::: anta.tests.services + options: + show_root_heading: false + show_root_toc_entry: false + merge_init_into_class: false diff --git a/examples/tests.yaml b/examples/tests.yaml index e71d390a8..3715adac7 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -227,6 +227,8 @@ anta.tests.security: common_name: Arista Networks Internal IT Root Cert Authority encryption_algorithm: RSA key_size: 4096 + +anta.tests.services: - VerifyBannerLogin: login_banner: | # Copyright (c) 2023-2024 Arista Networks, Inc. @@ -237,6 +239,8 @@ anta.tests.security: # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. + - VerifyHostname: + hostname: s1-spine1 anta.tests.snmp: - VerifySnmpStatus: @@ -247,6 +251,10 @@ anta.tests.snmp: - VerifySnmpIPv6Acl: number: 3 vrf: default + - VerifySnmpLocation: + location: New York + - VerifySnmpContact: + contact: Jon@example.com anta.tests.software: - VerifyEOSVersion: diff --git a/mkdocs.yml b/mkdocs.yml index 4aed5264e..d8d3afdc5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -187,6 +187,7 @@ nav: - BGP: api/tests.routing.bgp.md - OSPF: api/tests.routing.ospf.md - Security: api/tests.security.md + - Services: api/tests.services.md - SNMP: api/tests.snmp.md - STP: api/tests.stp.md - Software: api/tests.software.md diff --git a/tests/units/anta_tests/test_services.py b/tests/units/anta_tests/test_services.py new file mode 100644 index 000000000..bbb11a3d5 --- /dev/null +++ b/tests/units/anta_tests/test_services.py @@ -0,0 +1,32 @@ +# Copyright (c) 2023-2024 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +""" +Tests for anta.tests.services.py +""" +from __future__ import annotations + +from typing import Any + +from anta.tests.services import VerifyHostname +from tests.lib.anta import test # noqa: F401; pylint: disable=W0611 + +DATA: list[dict[str, Any]] = [ + { + "name": "success", + "test": VerifyHostname, + "eos_data": [{"hostname": "s1-spine1", "fqdn": "s1-spine1.fun.aristanetworks.com"}], + "inputs": {"hostname": "s1-spine1"}, + "expected": {"result": "success"}, + }, + { + "name": "failure-incorrect-hostname", + "test": VerifyHostname, + "eos_data": [{"hostname": "s1-spine2", "fqdn": "s1-spine1.fun.aristanetworks.com"}], + "inputs": {"hostname": "s1-spine1"}, + "expected": { + "result": "failure", + "messages": ["Expected `s1-spine1` as the hostname, but found `s1-spine2` instead."], + }, + }, +] diff --git a/tests/units/anta_tests/test_snmp.py b/tests/units/anta_tests/test_snmp.py index 0dd4dac03..700968924 100644 --- a/tests/units/anta_tests/test_snmp.py +++ b/tests/units/anta_tests/test_snmp.py @@ -8,7 +8,7 @@ from typing import Any -from anta.tests.snmp import VerifySnmpIPv4Acl, VerifySnmpIPv6Acl, VerifySnmpStatus +from anta.tests.snmp import VerifySnmpContact, VerifySnmpIPv4Acl, VerifySnmpIPv6Acl, VerifySnmpLocation, VerifySnmpStatus from tests.lib.anta import test # noqa: F401; pylint: disable=W0611 DATA: list[dict[str, Any]] = [ @@ -75,4 +75,54 @@ "inputs": {"number": 1, "vrf": "MGMT"}, "expected": {"result": "failure", "messages": ["SNMP IPv6 ACL(s) not configured or active in vrf MGMT: ['ACL_IPV6_SNMP']"]}, }, + { + "name": "success", + "test": VerifySnmpLocation, + "eos_data": [ + { + "location": {"location": "New York"}, + } + ], + "inputs": {"location": "New York"}, + "expected": {"result": "success"}, + }, + { + "name": "failure-incorrect-location", + "test": VerifySnmpLocation, + "eos_data": [ + { + "location": {"location": "Europe"}, + } + ], + "inputs": {"location": "New York"}, + "expected": { + "result": "failure", + "messages": ["Expected `New York` as the location, but found `Europe` instead."], + }, + }, + { + "name": "success", + "test": VerifySnmpContact, + "eos_data": [ + { + "contact": {"contact": "Jon@example.com"}, + } + ], + "inputs": {"contact": "Jon@example.com"}, + "expected": {"result": "success"}, + }, + { + "name": "failure-incorrect-contact", + "test": VerifySnmpContact, + "eos_data": [ + { + "contact": {"contact": "Jon@example.com"}, + } + ], + "inputs": {"contact": "Bob@example.com"}, + "expected": { + "result": "failure", + "messages": ["Expected `Bob@example.com` as the contact, but found `Jon@example.com` instead."], + }, + }, ]