Skip to content

ANTA catalog for services tests

Tests

Module related to the EOS various services tests.

VerifyDNSLookup

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.
Examples
anta.tests.services:
  - VerifyDNSLookup:
      domain_names:
        - arista.com
        - www.google.com
        - arista.ca

Inputs

Name Type Description Default
domain_names list[str]
List of domain names.
-
Source code in anta/tests/services.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
class VerifyDNSLookup(AntaTest):
    """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.

    Examples
    --------
    ```yaml
    anta.tests.services:
      - VerifyDNSLookup:
          domain_names:
            - arista.com
            - www.google.com
            - arista.ca
    ```
    """

    description = "Verifies the DNS name to IP address resolution."
    categories: ClassVar[list[str]] = ["services"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaTemplate(template="bash timeout 10 nslookup {domain}", revision=1)]

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

        domain_names: list[str]
        """List of domain names."""

    def render(self, template: AntaTemplate) -> list[AntaCommand]:
        """Render the template for each domain name in the input list."""
        return [template.render(domain=domain_name) for domain_name in self.inputs.domain_names]

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyDNSLookup."""
        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)}")

VerifyDNSServers

Verifies if the DNS (Domain Name Service) servers are correctly configured.

This test performs the following checks for each specified DNS Server:

  1. Confirming correctly registered with a valid IPv4 or IPv6 address with the designated VRF.
  2. Ensuring an appropriate priority level.
Expected Results
  • Success: The test will pass if the DNS server specified in the input is configured with the correct VRF and priority.
  • Failure: The test will fail if any of the following conditions are met:
    • The provided DNS server is not configured.
    • The provided DNS server with designated VRF and priority does not match the expected information.
Examples
anta.tests.services:
  - VerifyDNSServers:
      dns_servers:
        - server_address: 10.14.0.1
          vrf: default
          priority: 1
        - server_address: 10.14.0.11
          vrf: MGMT
          priority: 0

Inputs

Name Type Description Default
dns_servers list[DnsServer]
List of DNS servers to verify.
-
Source code in anta/tests/services.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
class VerifyDNSServers(AntaTest):
    """Verifies if the DNS (Domain Name Service) servers are correctly configured.

    This test performs the following checks for each specified DNS Server:

      1. Confirming correctly registered with a valid IPv4 or IPv6 address with the designated VRF.
      2. Ensuring an appropriate priority level.

    Expected Results
    ----------------
    * Success: The test will pass if the DNS server specified in the input is configured with the correct VRF and priority.
    * Failure: The test will fail if any of the following conditions are met:
        - The provided DNS server is not configured.
        - The provided DNS server with designated VRF and priority does not match the expected information.

    Examples
    --------
    ```yaml
    anta.tests.services:
      - VerifyDNSServers:
          dns_servers:
            - server_address: 10.14.0.1
              vrf: default
              priority: 1
            - server_address: 10.14.0.11
              vrf: MGMT
              priority: 0
    ```
    """

    categories: ClassVar[list[str]] = ["services"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip name-server", revision=1)]

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

        dns_servers: list[DnsServer]
        """List of DNS servers to verify."""
        DnsServer: ClassVar[type[DnsServer]] = DnsServer

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyDNSServers."""
        self.result.is_success()

        command_output = self.instance_commands[0].json_output["nameServerConfigs"]
        for server in self.inputs.dns_servers:
            address = str(server.server_address)
            vrf = server.vrf
            priority = server.priority
            input_dict = {"ipAddr": address, "vrf": vrf}

            # Check if the DNS server is configured with specified VRF.
            if (output := get_dict_superset(command_output, input_dict)) is None:
                self.result.is_failure(f"{server} - Not configured")
                continue

            # Check if the DNS server priority matches with expected.
            if output["priority"] != priority:
                self.result.is_failure(f"{server} - Incorrect priority - Priority: {output['priority']}")

VerifyErrdisableRecovery

Verifies the error disable recovery functionality.

This test performs the following checks for each specified error disable reason:

  1. Verifying if the specified error disable reason exists.
  2. Checking if the recovery timer status matches the expected enabled/disabled state.
  3. Validating that the timer interval matches the configured value.
Expected Results
  • Success: The test will pass if:
    • The specified error disable reason exists.
    • The recovery timer status matches the expected state.
    • The timer interval matches the configured value.
  • Failure: The test will fail if:
    • The specified error disable reason does not exist.
    • The recovery timer status does not match the expected state.
    • The timer interval does not match the configured value.
Examples
anta.tests.services:
  - VerifyErrdisableRecovery:
      reasons:
        - reason: acl
          interval: 30
          status: Enabled
        - reason: bpduguard
          interval: 30
          status: Enabled

Inputs

Name Type Description Default
reasons list[ErrdisableRecovery]
List of errdisable reasons.
-
Source code in anta/tests/services.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
class VerifyErrdisableRecovery(AntaTest):
    """Verifies the error disable recovery functionality.

    This test performs the following checks for each specified error disable reason:

      1. Verifying if the specified error disable reason exists.
      2. Checking if the recovery timer status matches the expected enabled/disabled state.
      3. Validating that the timer interval matches the configured value.

    Expected Results
    ----------------
    * Success: The test will pass if:
        - The specified error disable reason exists.
        - The recovery timer status matches the expected state.
        - The timer interval matches the configured value.
    * Failure: The test will fail if:
        - The specified error disable reason does not exist.
        - The recovery timer status does not match the expected state.
        - The timer interval does not match the configured value.

    Examples
    --------
    ```yaml
    anta.tests.services:
      - VerifyErrdisableRecovery:
          reasons:
            - reason: acl
              interval: 30
              status: Enabled
            - reason: bpduguard
              interval: 30
              status: Enabled
    ```
    """

    categories: ClassVar[list[str]] = ["services"]
    # NOTE: Only `text` output format is supported for this command
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show errdisable recovery", ofmt="text")]

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

        reasons: list[ErrdisableRecovery]
        """List of errdisable reasons."""
        ErrDisableReason: ClassVar[type[ErrdisableRecovery]] = ErrDisableReason

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyErrdisableRecovery."""
        self.result.is_success()

        # Skip header and last empty line
        command_output = self.instance_commands[0].text_output.split("\n")[2:-1]

        # Collecting the actual errdisable reasons for faster lookup
        errdisable_reasons = [
            {"reason": reason, "status": status, "interval": interval}
            for line in command_output
            if line.strip()  # Skip empty lines
            for reason, status, interval in [line.split(None, 2)]  # Unpack split result
        ]

        for error_reason in self.inputs.reasons:
            if not (reason_output := get_item(errdisable_reasons, "reason", error_reason.reason)):
                self.result.is_failure(f"{error_reason} - Not found")
                continue

            if not all(
                [
                    error_reason.status == (act_status := reason_output["status"]),
                    error_reason.interval == (act_interval := int(reason_output["interval"])),
                ]
            ):
                self.result.is_failure(f"{error_reason} - Incorrect configuration - Status: {act_status} Interval: {act_interval}")

VerifyHostname

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.
Examples
anta.tests.services:
  - VerifyHostname:
      hostname: s1-spine1

Inputs

Name Type Description Default
hostname str
Expected hostname of the device.
-
Source code in anta/tests/services.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class VerifyHostname(AntaTest):
    """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.

    Examples
    --------
    ```yaml
    anta.tests.services:
      - VerifyHostname:
          hostname: s1-spine1
    ```
    """

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

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

        hostname: str
        """Expected hostname of the device."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyHostname."""
        hostname = self.instance_commands[0].json_output["hostname"]

        if hostname != self.inputs.hostname:
            self.result.is_failure(f"Incorrect Hostname - Expected: {self.inputs.hostname} Actual: {hostname}")
        else:
            self.result.is_success()

Input models

Module containing input models for services tests.

DnsServer

Model for a DNS server configuration.

Name Type Description Default
server_address IPv4Address | IPv6Address
The IPv4 or IPv6 address of the DNS server.
-
vrf str
The VRF instance in which the DNS server resides. Defaults to 'default'.
'default'
priority int
The priority level of the DNS server, ranging from 0 to 4. Lower values indicate a higher priority, with 0 being the highest and 4 the lowest.
Field(ge=0, le=4)
Source code in anta/input_models/services.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class DnsServer(BaseModel):
    """Model for a DNS server configuration."""

    model_config = ConfigDict(extra="forbid")
    server_address: IPv4Address | IPv6Address
    """The IPv4 or IPv6 address of the DNS server."""
    vrf: str = "default"
    """The VRF instance in which the DNS server resides. Defaults to 'default'."""
    priority: int = Field(ge=0, le=4)
    """The priority level of the DNS server, ranging from 0 to 4. Lower values indicate a higher priority, with 0 being the highest and 4 the lowest."""

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

        Examples
        --------
        Server 10.0.0.1 (VRF: default, Priority: 1)
        """
        return f"Server {self.server_address} VRF: {self.vrf} Priority: {self.priority}"

ErrDisableReason

Alias for the ErrdisableRecovery model to maintain backward compatibility.

When initialised, it will emit a deprecation warning and call the ErrdisableRecovery model.

TODO: Remove this class in ANTA v2.0.0.

Source code in anta/input_models/services.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class ErrDisableReason(ErrdisableRecovery):  # pragma: no cover
    """Alias for the ErrdisableRecovery model to maintain backward compatibility.

    When initialised, it will emit a deprecation warning and call the ErrdisableRecovery model.

    TODO: Remove this class in ANTA v2.0.0.
    """

    def __init__(self, **data: Any) -> None:  # noqa: ANN401
        """Initialize the ErrdisableRecovery class, emitting a depreciation warning."""
        warn(
            message="ErrDisableReason model is deprecated and will be removed in ANTA v2.0.0. Use the ErrdisableRecovery model instead.",
            category=DeprecationWarning,
            stacklevel=2,
        )
        super().__init__(**data)

ErrdisableRecovery

Model for the error disable recovery functionality.

Name Type Description Default
reason ErrDisableReasons
Name of the error disable reason.
-
status Literal['Enabled', 'Disabled']
Operational status of the reason. Defaults to 'Enabled'.
'Enabled'
interval int
Timer interval of the reason in seconds.
Field(ge=30, le=86400)
Source code in anta/input_models/services.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class ErrdisableRecovery(BaseModel):
    """Model for the error disable recovery functionality."""

    model_config = ConfigDict(extra="forbid")
    reason: ErrDisableReasons
    """Name of the error disable reason."""
    status: Literal["Enabled", "Disabled"] = "Enabled"
    """Operational status of the reason. Defaults to 'Enabled'."""
    interval: int = Field(ge=30, le=86400)
    """Timer interval of the reason in seconds."""

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

        Examples
        --------
        Reason: acl Status: Enabled Interval: 300
        """
        return f"Reason: {self.reason} Status: {self.status} Interval: {self.interval}"