Skip to content

Create your own Library

Create your own custom tests

ANTA is not only a CLI with a collection of built-in tests, it is also a framework you can extend by building your own tests library.

For that, you need to create your own Python package as described in this hitchhiker’s guide to package Python code. We assume it is well known and we won’t focus on this aspect. Thus, your package must be impartable by ANTA hence available in $PYTHONPATH by any method.

Generic approach

To make it easier for everyone, it is recommended to apply always same structure to all code:

# Optional for easy coding
from typing import Optional
import logging

# Import ANTA components
from anta.inventory.models import InventoryDevice
from anta.result_manager.models import TestResult
from anta.tests import anta_test

# Instantiate logger
logger = logging.getLogger(__name__)

# define a function with at least one decorator to mark as a test
@anta.test
async def verify_dynamic_vlan(device: InventoryDevice,
    result: TestResult, minimum: Optional[int] = None) -> TestResult:

    # Create docstring
    # [...]

    # Create your own logic

    # Check if test option is correct
    if not minimum:
        result.is_skipped("verify_dynamic_vlan was run without minimum value set")
        return result

    # Send command to device
    response = await device.session.cli(command="show vlan", ofmt="json")

    # Optionaly log response for futur debug
    logger.debug(f"query result is: {response}")

    # Do your test: In this example we count number of vlans with field dynamic set to true
    num_dyn_vlan = len([ vlan for vlan,data in response['vlans'].items() if data['dynamic'] is True])
    if num_dyn_vlan >= minimum:
        result.is_success()
    else:
        result.is_failure(f"Device has {num_dyn_vlan} configured, we expect at least {minimum}")

    return result

Python imports

Mandatory imports

The following elements have to be imported:

  • InventoryDevice: Where the eAPI session lives. It is used to send commands over HTTP/HTTPS define in your test.
  • TestResult: Structure used to facilitate test result. It provides helpers like is_success, is_failure, is_skipped.
  • anta_test: A python decorator to mark function as a test and inject all the base requirements to run test and capture Exceptions.
from anta.inventory.models import InventoryDevice
from anta.result_manager.models import TestResult
from anta.tests import anta_test


@anta.test
async def verify_dynamic_vlan(...)
Optional ANTA imports

Besides these 3 main imports, anta provides some additional and optional decorators:

  • anta.test.skip_on_platforms: To skip a test for a function not available for some platform
  • anta.tests.check_bgp_family_enable: To run tests only if specific BGP family is active.
from anta.decorators import skip_on_platforms


@skip_on_platforms(["cEOSLab", "VEOS-LAB"])
@anta_test
async def verify_unified_forwarding_table_mode(...)
Optional python imports

And finally, you are free to import any other python library you may want to use in your package.

logging function

It is strongly recommended to import logging to help development process and being able to log some outputs usefull for test development.

Code for a test

Function definition

The code here can be very simple as well as very complex and will depend of what you expect to do. But in all situation, the same baseline can be leverage:

  • Function parameters:
    • async in front of the function the function signature defines it as a coroutine and enable parallel execution.
    • device: InventoryDevice: Device information (mandatory)
    • result: TestResult: A result structure to save test (mandatory)
    • Any option your test may require. In our example, we use minimum: Optional[int]
async def verify_dynamic_vlan(
    device: InventoryDevice,
    result: TestResult,
    minimum: Optional[int] = None
) -> TestResult:
Function documentation

We recommend to add a docstring to document your test. As an example, the following syntax rendering for documentation is working pretty well:

docstring is optional

Of course this section is optional, but we strongly believe in documentation to make the code easier to maintain over time.

"""
< Short description >

< Optional long description >

< A list of inputs >
Args:
    device (InventoryDevice): InventoryDevice instance containing all devices information.
    minimum (int): Minimum number of dynamic vlans expected.

< Description of returned values in TestResult object >
Returns:
    TestResult instance with
    * result = "unset" if the test has not been executed
    * result = "skipped" if the `minimum` parameter is  missing
    * result = "success" if device has more than minimum dynamic vlans
    * result = "failure" otherwise.
    * result = "error" if any exception is caught
"""

You can see output using mkdocstring-python here: tests.software.md

Check inputs

If your test has some user inputs, you first have to validate the supplied values are valid. If it is not valid, we expect TestResult to return skipped with a custom message.

# Check if test option is correct
if not minimum:
    result.is_skipped("verify_dynamic_vlan was run without minimum value set")
    return result
Implement your logic

Here you implement your own logic. In general, the first action is to send command to devices and capture its response.

In the example below, we request the list of vlans configured on device and then count all the vlans marked as dynamic

# Send command to device
response = await device.session.cli(command="show vlan", ofmt="json")

# Do your test: In this example we count number of vlans with field dynamic set to true
num_dyn_vlan = len([ vlan for vlan,data in response['vlans'].items() if data['dynamic'] is True])
if num_dyn_vlan >= minimum:
    result.is_success()
else:
    result.is_failure(f"Device has {num_dyn_vlan} configured, we expect at least {minimum}")

return result

As you can see there is no error management to do in your code. Everything is packaged in anta_tests and below is a simple example of error captured with an incorrect JSON key in the code above:

ERROR    Exception raised for test verify_dynamic_vlan (on device 192.168.0.10) - KeyError ('vlans')

Get stack trace for debugging

If you want to access to the full exception stack, you can run your test with logging level set to DEBUG. With ANTA cli, it is available with following option:

$ anta nrfu text --catalog test_custom.yml --log-level debug

Create your catalog

It is very similar to what is documented in catalog section but you have to use your own package name.

Let say the custom catalog is anta_titom73 and the test is configured in anta_titom73.dc_project, the test catalog would look like:

anta_titom73.dc_project:
  - verify_dynamic_vlan:
      minimum: 1
And now you can run your NRFU tests with the CLI:

anta nrfu text --catalog test_custom.yml
spine01 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)
spine02 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)
leaf01 :: verify_dynamic_vlan :: SUCCESS
leaf02 :: verify_dynamic_vlan :: SUCCESS
leaf03 :: verify_dynamic_vlan :: SUCCESS
leaf04 :: verify_dynamic_vlan :: SUCCESS

Install your python package

Anta uses Python path to access to your test. So it is critical to have your tests library installed correctly as explained at the begining of this page.


Last update: April 7, 2023