diff --git a/anta/tests/services.py b/anta/tests/services.py index 7b8dbc3c9..0c8265043 100644 --- a/anta/tests/services.py +++ b/anta/tests/services.py @@ -6,9 +6,12 @@ """ from __future__ import annotations +from typing import List + +from anta.models import AntaCommand, AntaTemplate, AntaTest + # Mypy does not understand AntaTest.Input typing # mypy: disable-error-code=attr-defined -from anta.models import AntaCommand, AntaTest class VerifyHostname(AntaTest): @@ -38,3 +41,40 @@ def test(self) -> None: self.result.is_failure(f"Expected `{self.inputs.hostname}` as the hostname, but found `{hostname}` instead.") else: self.result.is_success() + + +class VerifyDNSLookup(AntaTest): + """ + This class verifies the DNS (Domain name service) name to IP address resolution. + + Expected Results: + * success: The test will pass if a domain name is resolved to an IP address. + * failure: The test will fail if a domain name does not resolve to an IP address. + * error: This test will error out if a domain name is invalid. + """ + + name = "VerifyDNSLookup" + description = "Verifies the DNS name to IP address resolution." + categories = ["services"] + commands = [AntaTemplate(template="bash timeout 10 nslookup {domain}")] + + class Input(AntaTest.Input): + """Inputs for the VerifyDNSLookup test.""" + + domain_names: List[str] + """List of domain names""" + + def render(self, template: AntaTemplate) -> list[AntaCommand]: + return [template.render(domain=domain_name) for domain_name in self.inputs.domain_names] + + @AntaTest.anta_test + def test(self) -> None: + self.result.is_success() + failed_domains = [] + for command in self.instance_commands: + domain = command.params["domain"] + output = command.json_output["messages"][0] + if f"Can't find {domain}: No answer" in output: + failed_domains.append(domain) + if failed_domains: + self.result.is_failure(f"The following domain(s) are not resolved to an IP address: {', '.join(failed_domains)}") diff --git a/examples/tests.yaml b/examples/tests.yaml index a55bb93e7..e03461333 100644 --- a/examples/tests.yaml +++ b/examples/tests.yaml @@ -241,6 +241,11 @@ anta.tests.services: # that can be found in the LICENSE file. - VerifyHostname: hostname: s1-spine1 + - VerifyDNSLookup: + domain_names: + - arista.com + - www.google.com + - arista.ca anta.tests.snmp: - VerifySnmpStatus: diff --git a/tests/units/anta_tests/test_services.py b/tests/units/anta_tests/test_services.py index bbb11a3d5..76a903cb7 100644 --- a/tests/units/anta_tests/test_services.py +++ b/tests/units/anta_tests/test_services.py @@ -8,7 +8,7 @@ from typing import Any -from anta.tests.services import VerifyHostname +from anta.tests.services import VerifyDNSLookup, VerifyHostname from tests.lib.anta import test # noqa: F401; pylint: disable=W0611 DATA: list[dict[str, Any]] = [ @@ -29,4 +29,30 @@ "messages": ["Expected `s1-spine1` as the hostname, but found `s1-spine2` instead."], }, }, + { + "name": "success", + "test": VerifyDNSLookup, + "eos_data": [ + { + "messages": [ + "Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\tarista.com\nAddress: 151.101.130.132\nName:\tarista.com\n" + "Address: 151.101.2.132\nName:\tarista.com\nAddress: 151.101.194.132\nName:\tarista.com\nAddress: 151.101.66.132\n\n" + ] + }, + {"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\twww.google.com\nAddress: 172.217.12.100\n\n"]}, + ], + "inputs": {"domain_names": ["arista.com", "www.google.com"]}, + "expected": {"result": "success"}, + }, + { + "name": "failure", + "test": VerifyDNSLookup, + "eos_data": [ + {"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\n*** Can't find arista.ca: No answer\n\n"]}, + {"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\nName:\twww.google.com\nAddress: 172.217.12.100\n\n"]}, + {"messages": ["Server:\t\t127.0.0.1\nAddress:\t127.0.0.1#53\n\nNon-authoritative answer:\n*** Can't find google.ca: No answer\n\n"]}, + ], + "inputs": {"domain_names": ["arista.ca", "www.google.com", "google.ca"]}, + "expected": {"result": "failure", "messages": ["The following domain(s) are not resolved to an IP address: arista.ca, google.ca"]}, + }, ]