Skip to content

ANTA catalog for security tests

Tests

Module related to the EOS various security tests.

VerifyAPIHttpStatus

Verifies if eAPI HTTP server is disabled globally.

Expected Results
  • Success: The test will pass if eAPI HTTP server is disabled globally.
  • Failure: The test will fail if eAPI HTTP server is NOT disabled globally.
Examples
anta.tests.security:
  - VerifyAPIHttpStatus:
Source code in anta/tests/security.py
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
class VerifyAPIHttpStatus(AntaTest):
    """Verifies if eAPI HTTP server is disabled globally.

    Expected Results
    ----------------
    * Success: The test will pass if eAPI HTTP server is disabled globally.
    * Failure: The test will fail if eAPI HTTP server is NOT disabled globally.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyAPIHttpStatus:
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management api http-commands", revision=1)]

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyAPIHttpStatus."""
        command_output = self.instance_commands[0].json_output
        if command_output["enabled"] and not command_output["httpServer"]["running"]:
            self.result.is_success()
        else:
            self.result.is_failure("eAPI HTTP server is enabled globally")

VerifyAPIHttpsSSL

Verifies if the eAPI has a valid SSL profile.

Expected Results
  • Success: The test will pass if the eAPI HTTPS server SSL profile is configured and valid.
  • Failure: The test will fail if the eAPI HTTPS server SSL profile is NOT configured, misconfigured or invalid.
Examples
anta.tests.security:
  - VerifyAPIHttpsSSL:
      profile: default

Inputs

Name Type Description Default
profile str
SSL profile to verify.
-
Source code in anta/tests/security.py
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
239
240
241
242
243
244
245
class VerifyAPIHttpsSSL(AntaTest):
    """Verifies if the eAPI has a valid SSL profile.

    Expected Results
    ----------------
    * Success: The test will pass if the eAPI HTTPS server SSL profile is configured and valid.
    * Failure: The test will fail if the eAPI HTTPS server SSL profile is NOT configured, misconfigured or invalid.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyAPIHttpsSSL:
          profile: default
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management api http-commands", revision=1)]

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

        profile: str
        """SSL profile to verify."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyAPIHttpsSSL."""
        command_output = self.instance_commands[0].json_output
        try:
            if command_output["sslProfile"]["name"] == self.inputs.profile and command_output["sslProfile"]["state"] == "valid":
                self.result.is_success()
            else:
                self.result.is_failure(f"eAPI HTTPS server SSL profile {self.inputs.profile} is misconfigured or invalid")

        except KeyError:
            self.result.is_failure(f"eAPI HTTPS server SSL profile {self.inputs.profile} is not configured")

VerifyAPIIPv4Acl

Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.

Expected Results
  • Success: The test will pass if eAPI has the provided number of IPv4 ACL(s) in the specified VRF.
  • Failure: The test will fail if eAPI has not the right number of IPv4 ACL(s) in the specified VRF.
Examples
anta.tests.security:
  - VerifyAPIIPv4Acl:
      number: 3
      vrf: default

Inputs

Name Type Description Default
number PositiveInteger
The number of expected IPv4 ACL(s).
-
vrf str
The name of the VRF in which to check for eAPI. Defaults to `default` VRF.
'default'
Source code in anta/tests/security.py
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
class VerifyAPIIPv4Acl(AntaTest):
    """Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.

    Expected Results
    ----------------
    * Success: The test will pass if eAPI has the provided number of IPv4 ACL(s) in the specified VRF.
    * Failure: The test will fail if eAPI has not the right number of IPv4 ACL(s) in the specified VRF.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyAPIIPv4Acl:
          number: 3
          vrf: default
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management api http-commands ip access-list summary", revision=1)]

    class Input(AntaTest.Input):
        """Input parameters for the VerifyAPIIPv4Acl test."""

        number: PositiveInteger
        """The number of expected IPv4 ACL(s)."""
        vrf: str = "default"
        """The name of the VRF in which to check for eAPI. Defaults to `default` VRF."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyAPIIPv4Acl."""
        command_output = self.instance_commands[0].json_output
        ipv4_acl_list = command_output["ipAclList"]["aclList"]
        ipv4_acl_number = len(ipv4_acl_list)
        if ipv4_acl_number != self.inputs.number:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - eAPI IPv4 ACL(s) count mismatch - Expected: {self.inputs.number} Actual: {ipv4_acl_number}")
            return

        not_configured_acl = [acl["name"] for acl in ipv4_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]

        if not_configured_acl:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - Following eAPI IPv4 ACL(s) not configured or active: {', '.join(not_configured_acl)}")
        else:
            self.result.is_success()

VerifyAPIIPv6Acl

Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.

Expected Results
  • Success: The test will pass if eAPI has the provided number of IPv6 ACL(s) in the specified VRF.
  • Failure: The test will fail if eAPI has not the right number of IPv6 ACL(s) in the specified VRF.
  • Skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.
Examples
anta.tests.security:
  - VerifyAPIIPv6Acl:
      number: 3
      vrf: default

Inputs

Name Type Description Default
number PositiveInteger
The number of expected IPv6 ACL(s).
-
vrf str
The name of the VRF in which to check for eAPI. Defaults to `default` VRF.
'default'
Source code in anta/tests/security.py
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
class VerifyAPIIPv6Acl(AntaTest):
    """Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.

    Expected Results
    ----------------
    * Success: The test will pass if eAPI has the provided number of IPv6 ACL(s) in the specified VRF.
    * Failure: The test will fail if eAPI has not the right number of IPv6 ACL(s) in the specified VRF.
    * Skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyAPIIPv6Acl:
          number: 3
          vrf: default
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management api http-commands ipv6 access-list summary", revision=1)]

    class Input(AntaTest.Input):
        """Input parameters for the VerifyAPIIPv6Acl test."""

        number: PositiveInteger
        """The number of expected IPv6 ACL(s)."""
        vrf: str = "default"
        """The name of the VRF in which to check for eAPI. Defaults to `default` VRF."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyAPIIPv6Acl."""
        command_output = self.instance_commands[0].json_output
        ipv6_acl_list = command_output["ipv6AclList"]["aclList"]
        ipv6_acl_number = len(ipv6_acl_list)
        if ipv6_acl_number != self.inputs.number:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - eAPI IPv6 ACL(s) count mismatch - Expected: {self.inputs.number} Actual: {ipv6_acl_number}")
            return

        not_configured_acl = [acl["name"] for acl in ipv6_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]

        if not_configured_acl:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - Following eAPI IPv6 ACL(s) not configured or active: {', '.join(not_configured_acl)}")
        else:
            self.result.is_success()

VerifyAPISSLCertificate

Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size.

This test performs the following checks for each certificate:

  1. Validates that the certificate is not expired and meets the configured expiry threshold.
  2. Validates that the certificate Common Name matches the expected one.
  3. Ensures the certificate uses the specified encryption algorithm.
  4. Verifies the certificate key matches the expected key size.
Expected Results
  • Success: If all of the following occur:
    • The certificate’s expiry date exceeds the configured threshold.
    • The certificate’s Common Name matches the input configuration.
    • The encryption algorithm used by the certificate is as expected.
    • The key size of the certificate matches the input configuration.
  • Failure: If any of the following occur:
    • The certificate is expired or set to expire within the defined threshold.
    • The certificate’s common name does not match the expected input.
    • The encryption algorithm is incorrect.
    • The key size does not match the expected input.
Examples
anta.tests.security:
  - VerifyAPISSLCertificate:
      certificates:
        - certificate_name: ARISTA_SIGNING_CA.crt
          expiry_threshold: 30
          common_name: AristaIT-ICA ECDSA Issuing Cert Authority
          encryption_algorithm: ECDSA
          key_size: 256
        - certificate_name: ARISTA_ROOT_CA.crt
          expiry_threshold: 30
          common_name: Arista Networks Internal IT Root Cert Authority
          encryption_algorithm: RSA
          key_size: 4096

Inputs

Name Type Description Default
certificates list[APISSLCertificate]
List of API SSL certificates.
-
Source code in anta/tests/security.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
class VerifyAPISSLCertificate(AntaTest):
    """Verifies the eAPI SSL certificate expiry, common subject name, encryption algorithm and key size.

    This test performs the following checks for each certificate:

      1. Validates that the certificate is not expired and meets the configured expiry threshold.
      2. Validates that the certificate Common Name matches the expected one.
      3. Ensures the certificate uses the specified encryption algorithm.
      4. Verifies the certificate key matches the expected key size.

    Expected Results
    ----------------
    * Success: If all of the following occur:
        - The certificate's expiry date exceeds the configured threshold.
        - The certificate's Common Name matches the input configuration.
        - The encryption algorithm used by the certificate is as expected.
        - The key size of the certificate matches the input configuration.
    * Failure: If any of the following occur:
        - The certificate is expired or set to expire within the defined threshold.
        - The certificate's common name does not match the expected input.
        - The encryption algorithm is incorrect.
        - The key size does not match the expected input.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyAPISSLCertificate:
          certificates:
            - certificate_name: ARISTA_SIGNING_CA.crt
              expiry_threshold: 30
              common_name: AristaIT-ICA ECDSA Issuing Cert Authority
              encryption_algorithm: ECDSA
              key_size: 256
            - certificate_name: ARISTA_ROOT_CA.crt
              expiry_threshold: 30
              common_name: Arista Networks Internal IT Root Cert Authority
              encryption_algorithm: RSA
              key_size: 4096
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [
        AntaCommand(command="show management security ssl certificate", revision=1),
        AntaCommand(command="show clock", revision=1),
    ]

    class Input(AntaTest.Input):
        """Input parameters for the VerifyAPISSLCertificate test."""

        certificates: list[APISSLCertificate]
        """List of API SSL certificates."""
        APISSLCertificate: ClassVar[type[APISSLCertificate]] = APISSLCertificate

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyAPISSLCertificate."""
        # Mark the result as success by default
        self.result.is_success()

        # Extract certificate and clock output
        certificate_output = self.instance_commands[0].json_output
        clock_output = self.instance_commands[1].json_output
        current_timestamp = clock_output["utcTime"]

        # Iterate over each API SSL certificate
        for certificate in self.inputs.certificates:
            # Collecting certificate expiry time and current EOS time.
            # These times are used to calculate the number of days until the certificate expires.
            if not (certificate_data := get_value(certificate_output, f"certificates..{certificate.certificate_name}", separator="..")):
                self.result.is_failure(f"{certificate} - Not found")
                continue

            expiry_time = certificate_data["notAfter"]
            day_difference = (datetime.fromtimestamp(expiry_time, tz=timezone.utc) - datetime.fromtimestamp(current_timestamp, tz=timezone.utc)).days

            # Verify certificate expiry
            if 0 < day_difference < certificate.expiry_threshold:
                self.result.is_failure(
                    f"{certificate} - set to expire within the threshold - Threshold: {certificate.expiry_threshold} days Actual: {day_difference} days"
                )
            elif day_difference < 0:
                self.result.is_failure(f"{certificate} - certificate expired")

            # Verify certificate common subject name, encryption algorithm and key size
            common_name = get_value(certificate_data, "subject.commonName", default="Not found")
            encryp_algo = get_value(certificate_data, "publicKey.encryptionAlgorithm", default="Not found")
            key_size = get_value(certificate_data, "publicKey.size", default="Not found")

            if common_name != certificate.common_name:
                self.result.is_failure(f"{certificate} - incorrect common name - Expected: {certificate.common_name} Actual: {common_name}")

            if encryp_algo != certificate.encryption_algorithm:
                self.result.is_failure(f"{certificate} - incorrect encryption algorithm - Expected: {certificate.encryption_algorithm} Actual: {encryp_algo}")

            if key_size != certificate.key_size:
                self.result.is_failure(f"{certificate} - incorrect public key - Expected: {certificate.key_size} Actual: {key_size}")

VerifyBannerLogin

Verifies the login banner of a device.

Expected Results
  • Success: The test will pass if the login banner matches the provided input.
  • Failure: The test will fail if the login banner does not match the provided input.
Examples
anta.tests.security:
  - VerifyBannerLogin:
      login_banner: |
        # 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.

Inputs

Name Type Description Default
login_banner str
Expected login banner of the device.
-
Source code in anta/tests/security.py
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
class VerifyBannerLogin(AntaTest):
    """Verifies the login banner of a device.

    Expected Results
    ----------------
    * Success: The test will pass if the login banner matches the provided input.
    * Failure: The test will fail if the login banner does not match the provided input.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyBannerLogin:
          login_banner: |
            # 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.
    ```
    """

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

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

        login_banner: str
        """Expected login banner of the device."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBannerLogin."""
        self.result.is_success()
        if not (login_banner := self.instance_commands[0].json_output["loginBanner"]):
            self.result.is_failure("Login banner is not configured")
            return

        # Remove leading and trailing whitespaces from each line
        cleaned_banner = "\n".join(line.strip() for line in self.inputs.login_banner.split("\n"))
        if login_banner != cleaned_banner:
            self.result.is_failure(f"Incorrect login banner configured - Expected: {cleaned_banner} Actual: {login_banner}")

VerifyBannerMotd

Verifies the motd banner of a device.

Expected Results
  • Success: The test will pass if the motd banner matches the provided input.
  • Failure: The test will fail if the motd banner does not match the provided input.
Examples
anta.tests.security:
  - VerifyBannerMotd:
      motd_banner: |
        # 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.

Inputs

Name Type Description Default
motd_banner str
Expected motd banner of the device.
-
Source code in anta/tests/security.py
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
class VerifyBannerMotd(AntaTest):
    """Verifies the motd banner of a device.

    Expected Results
    ----------------
    * Success: The test will pass if the motd banner matches the provided input.
    * Failure: The test will fail if the motd banner does not match the provided input.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyBannerMotd:
          motd_banner: |
            # 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.
    ```
    """

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

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

        motd_banner: str
        """Expected motd banner of the device."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyBannerMotd."""
        self.result.is_success()
        if not (motd_banner := self.instance_commands[0].json_output["motd"]):
            self.result.is_failure("MOTD banner is not configured")
            return

        # Remove leading and trailing whitespaces from each line
        cleaned_banner = "\n".join(line.strip() for line in self.inputs.motd_banner.split("\n"))
        if motd_banner != cleaned_banner:
            self.result.is_failure(f"Incorrect MOTD banner configured - Expected: {cleaned_banner} Actual: {motd_banner}")

VerifyHardwareEntropy

Verifies hardware entropy generation is enabled on device.

Expected Results
  • Success: The test will pass if hardware entropy generation is enabled.
  • Failure: The test will fail if hardware entropy generation is not enabled.
Examples
anta.tests.security:
  - VerifyHardwareEntropy:
Source code in anta/tests/security.py
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
class VerifyHardwareEntropy(AntaTest):
    """Verifies hardware entropy generation is enabled on device.

    Expected Results
    ----------------
    * Success: The test will pass if hardware entropy generation is enabled.
    * Failure: The test will fail if hardware entropy generation is not enabled.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyHardwareEntropy:
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management security")]

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

        # Check if hardware entropy generation is enabled.
        if not command_output.get("hardwareEntropyEnabled"):
            self.result.is_failure("Hardware entropy generation is disabled")
        else:
            self.result.is_success()

VerifyIPSecConnHealth

Verifies all IPv4 security connections.

Expected Results
  • Success: The test will pass if all the IPv4 security connections are established in all vrf.
  • Failure: The test will fail if IPv4 security is not configured or any of IPv4 security connections are not established in any vrf.
Examples
anta.tests.security:
  - VerifyIPSecConnHealth:
Source code in anta/tests/security.py
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
class VerifyIPSecConnHealth(AntaTest):
    """Verifies all IPv4 security connections.

    Expected Results
    ----------------
    * Success: The test will pass if all the IPv4 security connections are established in all vrf.
    * Failure: The test will fail if IPv4 security is not configured or any of IPv4 security connections are not established in any vrf.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyIPSecConnHealth:
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip security connection vrf all")]

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyIPSecConnHealth."""
        self.result.is_success()
        command_output = self.instance_commands[0].json_output["connections"]

        # Check if IP security connection is configured
        if not command_output:
            self.result.is_failure("No IPv4 security connection configured")
            return

        # Iterate over all ipsec connections
        for conn_data in command_output.values():
            state = next(iter(conn_data["pathDict"].values()))
            if state != "Established":
                source = conn_data.get("saddr")
                destination = conn_data.get("daddr")
                vrf = conn_data.get("tunnelNs")
                self.result.is_failure(f"Source: {source} Destination: {destination} VRF: {vrf} - IPv4 security connection not established")

VerifyIPv4ACL

Verifies the configuration of IPv4 ACLs.

This test performs the following checks for each IPv4 ACL:

  1. Validates that the IPv4 ACL is properly configured.
  2. Validates that the sequence entries in the ACL are correctly ordered.
Expected Results
  • Success: If all of the following occur:
    • Any IPv4 ACL entry is not configured.
    • The sequency entries are correctly configured.
  • Failure: If any of the following occur:
    • The IPv4 ACL is not configured.
    • The any IPv4 ACL entry is not configured.
    • The action for any entry does not match the expected input.
Examples
anta.tests.security:
  - VerifyIPv4ACL:
      ipv4_access_lists:
        - name: default-control-plane-acl
          entries:
            - sequence: 10
              action: permit icmp any any
            - sequence: 20
              action: permit ip any any tracked
            - sequence: 30
              action: permit udp any any eq bfd ttl eq 255
        - name: LabTest
          entries:
            - sequence: 10
              action: permit icmp any any
            - sequence: 20
              action: permit tcp any any range 5900 5910

Inputs

Name Type Description Default
ipv4_access_lists list[ACL]
List of IPv4 ACLs to verify.
-
IPv4ACL type[ACL]
To maintain backward compatibility.
ACL
Source code in anta/tests/security.py
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
class VerifyIPv4ACL(AntaTest):
    """Verifies the configuration of IPv4 ACLs.

    This test performs the following checks for each IPv4 ACL:

      1. Validates that the IPv4 ACL is properly configured.
      2. Validates that the sequence entries in the ACL are correctly ordered.

    Expected Results
    ----------------
    * Success: If all of the following occur:
        - Any IPv4 ACL entry is not configured.
        - The sequency entries are correctly configured.
    * Failure: If any of the following occur:
        - The IPv4 ACL is not configured.
        - The any IPv4 ACL entry is not configured.
        - The action for any entry does not match the expected input.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyIPv4ACL:
          ipv4_access_lists:
            - name: default-control-plane-acl
              entries:
                - sequence: 10
                  action: permit icmp any any
                - sequence: 20
                  action: permit ip any any tracked
                - sequence: 30
                  action: permit udp any any eq bfd ttl eq 255
            - name: LabTest
              entries:
                - sequence: 10
                  action: permit icmp any any
                - sequence: 20
                  action: permit tcp any any range 5900 5910
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show ip access-lists", revision=1)]

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

        ipv4_access_lists: list[ACL]
        """List of IPv4 ACLs to verify."""
        IPv4ACL: ClassVar[type[ACL]] = ACL
        """To maintain backward compatibility."""

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

        if not (command_output := self.instance_commands[0].json_output["aclList"]):
            self.result.is_failure("No Access Control List (ACL) configured")
            return

        for access_list in self.inputs.ipv4_access_lists:
            if not (access_list_output := get_item(command_output, "name", access_list.name)):
                self.result.is_failure(f"{access_list} - Not configured")
                continue

            for entry in access_list.entries:
                if not (actual_entry := get_item(access_list_output["sequence"], "sequenceNumber", entry.sequence)):
                    self.result.is_failure(f"{access_list} {entry} - Not configured")
                    continue

                if (act_action := actual_entry["text"]) != entry.action:
                    self.result.is_failure(f"{access_list} {entry} - action mismatch - Expected: {entry.action} Actual: {act_action}")

VerifySSHIPv4Acl

Verifies if the SSHD agent has the right number IPv4 ACL(s) configured for a specified VRF.

Expected Results
  • Success: The test will pass if the SSHD agent has the provided number of IPv4 ACL(s) in the specified VRF.
  • Failure: The test will fail if the SSHD agent has not the right number of IPv4 ACL(s) in the specified VRF.
Examples
anta.tests.security:
  - VerifySSHIPv4Acl:
      number: 3
      vrf: default

Inputs

Name Type Description Default
number PositiveInteger
The number of expected IPv4 ACL(s).
-
vrf str
The name of the VRF in which to check for the SSHD agent. Defaults to `default` VRF.
'default'
Source code in anta/tests/security.py
 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 VerifySSHIPv4Acl(AntaTest):
    """Verifies if the SSHD agent has the right number IPv4 ACL(s) configured for a specified VRF.

    Expected Results
    ----------------
    * Success: The test will pass if the SSHD agent has the provided number of IPv4 ACL(s) in the specified VRF.
    * Failure: The test will fail if the SSHD agent has not the right number of IPv4 ACL(s) in the specified VRF.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifySSHIPv4Acl:
          number: 3
          vrf: default
    ```
    """

    description = "Verifies if the SSHD agent has IPv4 ACL(s) configured."
    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management ssh ip access-list summary", revision=1)]

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

        number: PositiveInteger
        """The number of expected IPv4 ACL(s)."""
        vrf: str = "default"
        """The name of the VRF in which to check for the SSHD agent. Defaults to `default` VRF."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifySSHIPv4Acl."""
        self.result.is_success()
        command_output = self.instance_commands[0].json_output
        ipv4_acl_list = command_output["ipAclList"]["aclList"]
        ipv4_acl_number = len(ipv4_acl_list)
        if ipv4_acl_number != self.inputs.number:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - SSH IPv4 ACL(s) count mismatch - Expected: {self.inputs.number} Actual: {ipv4_acl_number}")
            return

        not_configured_acl = [acl["name"] for acl in ipv4_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]

        if not_configured_acl:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - Following SSH IPv4 ACL(s) not configured or active: {', '.join(not_configured_acl)}")

VerifySSHIPv6Acl

Verifies if the SSHD agent has the right number IPv6 ACL(s) configured for a specified VRF.

Expected Results
  • Success: The test will pass if the SSHD agent has the provided number of IPv6 ACL(s) in the specified VRF.
  • Failure: The test will fail if the SSHD agent has not the right number of IPv6 ACL(s) in the specified VRF.
Examples
anta.tests.security:
  - VerifySSHIPv6Acl:
      number: 3
      vrf: default

Inputs

Name Type Description Default
number PositiveInteger
The number of expected IPv6 ACL(s).
-
vrf str
The name of the VRF in which to check for the SSHD agent. Defaults to `default` VRF.
'default'
Source code in anta/tests/security.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
class VerifySSHIPv6Acl(AntaTest):
    """Verifies if the SSHD agent has the right number IPv6 ACL(s) configured for a specified VRF.

    Expected Results
    ----------------
    * Success: The test will pass if the SSHD agent has the provided number of IPv6 ACL(s) in the specified VRF.
    * Failure: The test will fail if the SSHD agent has not the right number of IPv6 ACL(s) in the specified VRF.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifySSHIPv6Acl:
          number: 3
          vrf: default
    ```
    """

    description = "Verifies if the SSHD agent has IPv6 ACL(s) configured."
    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management ssh ipv6 access-list summary", revision=1)]

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

        number: PositiveInteger
        """The number of expected IPv6 ACL(s)."""
        vrf: str = "default"
        """The name of the VRF in which to check for the SSHD agent. Defaults to `default` VRF."""

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifySSHIPv6Acl."""
        self.result.is_success()
        command_output = self.instance_commands[0].json_output
        ipv6_acl_list = command_output["ipv6AclList"]["aclList"]
        ipv6_acl_number = len(ipv6_acl_list)
        if ipv6_acl_number != self.inputs.number:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - SSH IPv6 ACL(s) count mismatch - Expected: {self.inputs.number} Actual: {ipv6_acl_number}")
            return

        not_configured_acl = [acl["name"] for acl in ipv6_acl_list if self.inputs.vrf not in acl["configuredVrfs"] or self.inputs.vrf not in acl["activeVrfs"]]

        if not_configured_acl:
            self.result.is_failure(f"VRF: {self.inputs.vrf} - Following SSH IPv6 ACL(s) not configured or active: {', '.join(not_configured_acl)}")

VerifySSHStatus

Verifies if the SSHD agent is disabled in the default VRF.

Expected Results
  • Success: The test will pass if the SSHD agent is disabled in the default VRF.
  • Failure: The test will fail if the SSHD agent is NOT disabled in the default VRF.
Examples
anta.tests.security:
  - VerifySSHStatus:
Source code in anta/tests/security.py
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
52
53
class VerifySSHStatus(AntaTest):
    """Verifies if the SSHD agent is disabled in the default VRF.

    Expected Results
    ----------------
    * Success: The test will pass if the SSHD agent is disabled in the default VRF.
    * Failure: The test will fail if the SSHD agent is NOT disabled in the default VRF.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifySSHStatus:
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show management ssh", ofmt="text")]

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

        try:
            line = next(line for line in command_output.split("\n") if line.startswith("SSHD status"))
        except StopIteration:
            self.result.is_failure("Could not find SSH status in returned output")
            return
        status = line.split()[-1]

        if status == "disabled":
            self.result.is_success()
        else:
            self.result.is_failure(line)

VerifySpecificIPSecConn

Verifies the IPv4 security connections.

This test performs the following checks for each peer:

  1. Validates that the VRF is configured.
  2. Checks for the presence of IPv4 security connections for the specified peer.
  3. For each relevant peer:
    • If source and destination addresses are provided, verifies the security connection for the specific path exists and is Established.
    • If no addresses are provided, verifies that all security connections associated with the peer are Established.
Expected Results
  • Success: If all checks pass for all specified IPv4 security connections.
  • Failure: If any of the following occur:
    • No IPv4 security connections are found for the peer
    • The security connection is not established for the specified path or any of the peer connections is not established when no path is specified.
Examples
anta.tests.security:
  - VerifySpecificIPSecConn:
      ip_security_connections:
        - peer: 10.255.0.1
        - peer: 10.255.0.2
          vrf: default
          connections:
            - source_address: 100.64.3.2
              destination_address: 100.64.2.2
            - source_address: 172.18.3.2
              destination_address: 172.18.2.2

Inputs

Name Type Description Default
ip_security_connections list[IPSecPeer]
List of IP4v security peers.
-
IPSecPeers type[IPSecPeers]
To maintain backward compatibility.
IPSecPeers
Source code in anta/tests/security.py
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
class VerifySpecificIPSecConn(AntaTest):
    """Verifies the IPv4 security connections.

    This test performs the following checks for each peer:

      1. Validates that the VRF is configured.
      2. Checks for the presence of IPv4 security connections for the specified peer.
      3. For each relevant peer:
        - If source and destination addresses are provided, verifies the security connection for the specific path exists and is `Established`.
        - If no addresses are provided, verifies that all security connections associated with the peer are `Established`.

    Expected Results
    ----------------
    * Success: If all checks pass for all specified IPv4 security connections.
    * Failure: If any of the following occur:
        - No IPv4 security connections are found for the peer
        - The security connection is not established for the specified path or any of the peer connections is not established when no path is specified.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifySpecificIPSecConn:
          ip_security_connections:
            - peer: 10.255.0.1
            - peer: 10.255.0.2
              vrf: default
              connections:
                - source_address: 100.64.3.2
                  destination_address: 100.64.2.2
                - source_address: 172.18.3.2
                  destination_address: 172.18.2.2
    ```
    """

    categories: ClassVar[list[str]] = ["security"]
    commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaTemplate(template="show ip security connection vrf {vrf} path peer {peer}", revision=2)]

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

        ip_security_connections: list[IPSecPeer]
        """List of IP4v security peers."""
        IPSecPeers: ClassVar[type[IPSecPeers]] = IPSecPeers
        """To maintain backward compatibility."""

    def render(self, template: AntaTemplate) -> list[AntaCommand]:
        """Render the template for each input IP Sec connection."""
        return [template.render(peer=conn.peer, vrf=conn.vrf) for conn in self.inputs.ip_security_connections]

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

        for command_output, input_peer in zip(self.instance_commands, self.inputs.ip_security_connections):
            conn_output = command_output.json_output["connections"]
            conn_input = input_peer.connections
            vrf = input_peer.vrf

            # Check if IPv4 security connection is configured
            if not conn_output:
                self.result.is_failure(f"{input_peer} - Not configured")
                continue

            # If connection details are not provided then check all connections of a peer
            if conn_input is None:
                for conn_data in conn_output.values():
                    state = next(iter(conn_data["pathDict"].values()))
                    if state != "Established":
                        source = conn_data.get("saddr")
                        destination = conn_data.get("daddr")
                        self.result.is_failure(f"{input_peer} Source: {source} Destination: {destination} - Connection down - Expected: Established Actual: {state}")
                continue

            # Create a dictionary of existing connections for faster lookup
            existing_connections = {
                (conn_data.get("saddr"), conn_data.get("daddr"), conn_data.get("tunnelNs")): next(iter(conn_data["pathDict"].values()))
                for conn_data in conn_output.values()
            }
            for connection in conn_input:
                source_input = str(connection.source_address)
                destination_input = str(connection.destination_address)

                if (source_input, destination_input, vrf) in existing_connections:
                    existing_state = existing_connections[(source_input, destination_input, vrf)]
                    if existing_state != "Established":
                        failure = f"Expected: Established Actual: {existing_state}"
                        self.result.is_failure(f"{input_peer} Source: {source_input} Destination: {destination_input} - Connection down - {failure}")
                else:
                    self.result.is_failure(f"{input_peer} Source: {source_input} Destination: {destination_input} - Connection not found.")

VerifyTelnetStatus

Verifies if Telnet is disabled in the default VRF.

Expected Results
  • Success: The test will pass if Telnet is disabled in the default VRF.
  • Failure: The test will fail if Telnet is NOT disabled in the default VRF.
Examples
anta.tests.security:
  - VerifyTelnetStatus:
Source code in anta/tests/security.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
class VerifyTelnetStatus(AntaTest):
    """Verifies if Telnet is disabled in the default VRF.

    Expected Results
    ----------------
    * Success: The test will pass if Telnet is disabled in the default VRF.
    * Failure: The test will fail if Telnet is NOT disabled in the default VRF.

    Examples
    --------
    ```yaml
    anta.tests.security:
      - VerifyTelnetStatus:
    ```
    """

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

    @AntaTest.anta_test
    def test(self) -> None:
        """Main test function for VerifyTelnetStatus."""
        command_output = self.instance_commands[0].json_output
        if command_output["serverState"] == "disabled":
            self.result.is_success()
        else:
            self.result.is_failure("Telnet status for Default VRF is enabled")

Input models

Module containing input models for security tests.

ACL

Model for an Access Control List (ACL).

Name Type Description Default
name str
Name of the ACL.
-
entries list[ACLEntry]
List of the ACL entries.
-
IPv4ACLEntry type[ACLEntry]
To maintain backward compatibility.
ACLEntry
Source code in anta/input_models/security.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class ACL(BaseModel):
    """Model for an Access Control List (ACL)."""

    model_config = ConfigDict(extra="forbid")
    name: str
    """Name of the ACL."""
    entries: list[ACLEntry]
    """List of the ACL entries."""
    IPv4ACLEntry: ClassVar[type[ACLEntry]] = ACLEntry
    """To maintain backward compatibility."""

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

        Examples
        --------
        - ACL name: Test
        """
        return f"ACL name: {self.name}"

ACLEntry

Model for an Access Control List (ACL) entry.

Name Type Description Default
sequence int
Sequence number of the ACL entry, used to define the order of processing. Must be between 1 and 4294967295.
Field(ge=1, le=4294967295)
action str
Action of the ACL entry. Example: `deny ip any any`.
-
Source code in anta/input_models/security.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
class ACLEntry(BaseModel):
    """Model for an Access Control List (ACL) entry."""

    model_config = ConfigDict(extra="forbid")
    sequence: int = Field(ge=1, le=4294967295)
    """Sequence number of the ACL entry, used to define the order of processing. Must be between 1 and 4294967295."""
    action: str
    """Action of the ACL entry. Example: `deny ip any any`."""

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

        Examples
        --------
        - Sequence: 10
        """
        return f"Sequence: {self.sequence}"

APISSLCertificate

Model for an API SSL certificate.

Name Type Description Default
certificate_name str
The name of the certificate to be verified.
-
expiry_threshold int
The expiry threshold of the certificate in days.
-
common_name str
The Common Name of the certificate.
-
encryption_algorithm EncryptionAlgorithm
The encryption algorithm used by the certificate.
-
key_size RsaKeySize | EcdsaKeySize
The key size (in bits) of the encryption algorithm.
-

validate_inputs

validate_inputs() -> Self

Validate the key size provided to the APISSLCertificates class.

If encryption_algorithm is RSA then key_size should be in {2048, 3072, 4096}.

If encryption_algorithm is ECDSA then key_size should be in {256, 384, 521}.

Source code in anta/input_models/security.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@model_validator(mode="after")
def validate_inputs(self) -> Self:
    """Validate the key size provided to the APISSLCertificates class.

    If encryption_algorithm is RSA then key_size should be in {2048, 3072, 4096}.

    If encryption_algorithm is ECDSA then key_size should be in {256, 384, 521}.
    """
    if self.encryption_algorithm == "RSA" and self.key_size not in get_args(RsaKeySize):
        msg = f"`{self.certificate_name}` key size {self.key_size} is invalid for RSA encryption. Allowed sizes are {get_args(RsaKeySize)}."
        raise ValueError(msg)

    if self.encryption_algorithm == "ECDSA" and self.key_size not in get_args(EcdsaKeySize):
        msg = f"`{self.certificate_name}` key size {self.key_size} is invalid for ECDSA encryption. Allowed sizes are {get_args(EcdsaKeySize)}."
        raise ValueError(msg)

    return self
Source code in anta/input_models/security.py
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
class APISSLCertificate(BaseModel):
    """Model for an API SSL certificate."""

    model_config = ConfigDict(extra="forbid")
    certificate_name: str
    """The name of the certificate to be verified."""
    expiry_threshold: int
    """The expiry threshold of the certificate in days."""
    common_name: str
    """The Common Name of the certificate."""
    encryption_algorithm: EncryptionAlgorithm
    """The encryption algorithm used by the certificate."""
    key_size: RsaKeySize | EcdsaKeySize
    """The key size (in bits) of the encryption algorithm."""

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

        Examples
        --------
        - Certificate: SIGNING_CA.crt
        """
        return f"Certificate: {self.certificate_name}"

    @model_validator(mode="after")
    def validate_inputs(self) -> Self:
        """Validate the key size provided to the APISSLCertificates class.

        If encryption_algorithm is RSA then key_size should be in {2048, 3072, 4096}.

        If encryption_algorithm is ECDSA then key_size should be in {256, 384, 521}.
        """
        if self.encryption_algorithm == "RSA" and self.key_size not in get_args(RsaKeySize):
            msg = f"`{self.certificate_name}` key size {self.key_size} is invalid for RSA encryption. Allowed sizes are {get_args(RsaKeySize)}."
            raise ValueError(msg)

        if self.encryption_algorithm == "ECDSA" and self.key_size not in get_args(EcdsaKeySize):
            msg = f"`{self.certificate_name}` key size {self.key_size} is invalid for ECDSA encryption. Allowed sizes are {get_args(EcdsaKeySize)}."
            raise ValueError(msg)

        return self

IPSecConn

Details of an IPv4 security connection for a peer.

Name Type Description Default
source_address IPv4Address
The IPv4 address of the source in the security connection.
-
destination_address IPv4Address
The IPv4 address of the destination in the security connection.
-
Source code in anta/input_models/security.py
46
47
48
49
50
51
52
53
class IPSecConn(BaseModel):
    """Details of an IPv4 security connection for a peer."""

    model_config = ConfigDict(extra="forbid")
    source_address: IPv4Address
    """The IPv4 address of the source in the security connection."""
    destination_address: IPv4Address
    """The IPv4 address of the destination in the security connection."""

IPSecPeer

IPSec (Internet Protocol Security) model represents the details of an IPv4 security peer.

Name Type Description Default
peer IPv4Address
The IPv4 address of the security peer.
-
vrf str
VRF context. Defaults to `default`.
'default'
connections list[IPSecConn] | None
A list of IPv4 security connections associated with the peer. Defaults to None.
None
Source code in anta/input_models/security.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class IPSecPeer(BaseModel):
    """IPSec (Internet Protocol Security) model represents the details of an IPv4 security peer."""

    model_config = ConfigDict(extra="forbid")
    peer: IPv4Address
    """The IPv4 address of the security peer."""
    vrf: str = "default"
    """VRF context. Defaults to `default`."""
    connections: list[IPSecConn] | None = None
    """A list of IPv4 security connections associated with the peer. Defaults to None."""

    def __str__(self) -> str:
        """Return a string representation of the IPSecPeer model. Used in failure messages.

        Examples
        --------
        - Peer: 1.1.1.1 VRF: default
        """
        return f"Peer: {self.peer} VRF: {self.vrf}"

IPSecPeers

Alias for the IPSecPeers model to maintain backward compatibility.

When initialized, it will emit a deprecation warning and call the IPSecPeer model.

TODO: Remove this class in ANTA v2.0.0.

Source code in anta/input_models/security.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class IPSecPeers(IPSecPeer):  # pragma: no cover
    """Alias for the IPSecPeers model to maintain backward compatibility.

    When initialized, it will emit a deprecation warning and call the IPSecPeer model.

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

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

IPv4ACL

Alias for the ACL model to maintain backward compatibility.

When initialized, it will emit a deprecation warning and call the ACL model.

TODO: Remove this class in ANTA v2.0.0.

Source code in anta/input_models/security.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
class IPv4ACL(ACL):  # pragma: no cover
    """Alias for the ACL model to maintain backward compatibility.

    When initialized, it will emit a deprecation warning and call the ACL model.

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

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