Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/.markdownlint-cli2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,46 @@ config:
# Strong style should be consistent
style: "asterisk"

# Python object references are resolved by mkdocs-autorefs during the strict
# Zensical build. MD052 only supports exact labels; its undefined-reference parser lowercases
# these labels and removes underscores.
# Keep ordinary undefined reference links checked; register new object IDs here.
MD052:
ignored_labels:
- x # Preserve the default task-list exemption.
- anta.catalog.antacatalog
- anta.constants.knowneoserrors
- anta.customtypes
- anta.device.antadevice
- anta.device.antadevice.collect
- anta.device.antadevice.copy
- anta.device.antadevice.disconnect
- anta.device.antadevice.refresh
- anta.device.asynceosdevice
- anta.device.asynceosdevice.collect
- anta.device.asynceosdevice.copy
- anta.device.asynceosdevice.refresh
- anta.inventory.antainventory
- anta.inventory.antainventory.adddevice
- anta.inventory.antainventory.connectinventory
- anta.inventory.antainventory.disconnectinventory
- anta.inventory.antainventory.getinventory
- anta.inventory.antainventory.parse
- anta.models.antacommand
- anta.models.antatemplate
- anta.models.antatemplate.render
- anta.models.antatest
- anta.models.antatest.input
- anta.models.antatest.input.resultoverwrite
- anta.models.antatest.render
- anta.models.antatest.test
- anta.reporter.csvreporter.reportcsv
- anta.reporter.jinjareporter.reportjinja
- anta.reporter.mdreporter.mdreportgenerator
- anta.resultmanager.models.antateststatus
- anta.resultmanager.models.testresult
- anta.tests.hardware.verifytemperature

# MD059/descriptive-link-text
# Link text should be descriptive - by default this rule flags when a link text is "here"
MD059: false
113 changes: 78 additions & 35 deletions .github/zensical-migration.md

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions docs/advanced_usages/as-python-lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ tags:
~ that can be found in the LICENSE file.
-->

<!-- markdownlint-disable MD052 -->
<!-- Python object references below are resolved by mkdocs-autorefs. -->

ANTA is a Python library that can be used in user applications. This section describes how you can leverage ANTA Python modules to help you create your own NRFU solution.

!!! tip
Expand Down
6 changes: 3 additions & 3 deletions docs/advanced_usages/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ ANTA is a streamlined Python framework designed for efficient interaction with n

## Configuration

The `_init_cache()` method of the [AntaDevice](../api/device.md#anta.device.AntaDevice) abstract class initializes the cache. Child classes can override this method to tweak the cache configuration:
The `_init_cache()` method of the [AntaDevice][anta.device.AntaDevice] abstract class initializes the cache. Child classes can override this method to tweak the cache configuration:

## Cache key design

The cache is initialized per `AntaDevice` and uses the following cache key design:

`<device_name>:<uid>`

The `uid` is an attribute of [AntaCommand](../api/commands.md#anta.models.AntaCommand), which is a unique identifier generated from the command, version, revision and output format.
The `uid` is an attribute of [AntaCommand][anta.models.AntaCommand], which is a unique identifier generated from the command, version, revision and output format.

Each UID has its own asyncio lock. This design allows coroutines that need to access the cache for different UIDs to do so concurrently. The locks are managed by the `AntaCache.locks` dictionary.

Expand Down Expand Up @@ -71,7 +71,7 @@ There might be scenarios where caching is not wanted. You can disable caching in

This approach effectively disables caching for **ALL** commands sent to devices targeted by the `disable_cache` key.

3. For tests developers, caching can be disabled for a specific [`AntaCommand`](../api/commands.md#anta.models.AntaCommand) or [`AntaTemplate`](../api/commands.md#anta.models.AntaTemplate) by setting the `use_cache` attribute to `False`. That means the command output will always be collected on the device and therefore, never use caching.
3. For tests developers, caching can be disabled for a specific [`AntaCommand`][anta.models.AntaCommand] or [`AntaTemplate`][anta.models.AntaTemplate] by setting the `use_cache` attribute to `False`. That means the command output will always be collected on the device and therefore, never use caching.

By default, caching is enabled with `disable_cache=False` at the device level and `use_cache=True` at the command or template level.

Expand Down
44 changes: 22 additions & 22 deletions docs/advanced_usages/custom-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ANTA is not only a Python library with a CLI and a collection of built-in tests,

A test is a Python class where a test function is defined and will be run by the framework.

ANTA provides an abstract class [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest). This class does the heavy lifting and provide the logic to define, collect and test data. The code below is an example of a simple test in ANTA, which is an [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) subclass:
ANTA provides an abstract class [AntaTest][anta.models.AntaTest]. This class does the heavy lifting and provide the logic to define, collect and test data. The code below is an example of a simple test in ANTA, which is an [AntaTest][anta.models.AntaTest] subclass:

````python
from typing import ClassVar
Expand Down Expand Up @@ -63,18 +63,18 @@ class VerifyTemperature(AntaTest):
self.result.is_failure(f"Device temperature exceeds acceptable limits. Current system status: '{temperature_status}'")
````

[AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) also provide more advanced capabilities like [AntaCommand](../api/commands.md#anta.models.AntaCommand) templating using the [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) class or test inputs definition and validation using [AntaTest.Input](../api/tests/anta_test.md#anta.models.AntaTest.Input) [pydantic](https://docs.pydantic.dev/latest/) model. This will be discussed in the sections below.
[AntaTest][anta.models.AntaTest] also provide more advanced capabilities like [AntaCommand][anta.models.AntaCommand] templating using the [AntaTemplate][anta.models.AntaTemplate] class or test inputs definition and validation using [AntaTest.Input][anta.models.AntaTest.Input] [pydantic](https://docs.pydantic.dev/latest/) model. This will be discussed in the sections below.

## AntaTest structure

Full AntaTest API documentation is available in the [API documentation section](../api/tests/anta_test.md#anta.models.AntaTest)
Full AntaTest API documentation is available in the [API documentation section][anta.models.AntaTest]

### Class Attributes

- `name` (`str`, `optional`): Name of the test. Used during reporting. By default set to the Class name.
- `description` (`str`, `optional`): A human readable description of your test. By default set to the first line of the docstring.
- `categories` (`list[str]`): A list of categories in which the test belongs.
- `commands` (`[list[AntaCommand | AntaTemplate]]`): A list of command to collect from devices. This list **must** be a list of [AntaCommand](../api/commands.md#anta.models.AntaCommand) or [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) instances. Rendering [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) instances will be discussed later.
- `commands` (`[list[AntaCommand | AntaTemplate]]`): A list of command to collect from devices. This list **must** be a list of [AntaCommand][anta.models.AntaCommand] or [AntaTemplate][anta.models.AntaTemplate] instances. Rendering [AntaTemplate][anta.models.AntaTemplate] instances will be discussed later.

!!! info
All these class attributes are mandatory. If any attribute is missing, a `NotImplementedError` exception will be raised during class instantiation.
Expand All @@ -98,21 +98,21 @@ Full AntaTest API documentation is available in the [API documentation section](

- **Logger object**

ANTA already provides comprehensive logging at every steps of a test execution. The [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) class also provides a `logger` attribute that is a Python logger specific to the test instance. See [Python documentation](https://docs.python.org/3/library/logging.html) for more information.
ANTA already provides comprehensive logging at every steps of a test execution. The [AntaTest][anta.models.AntaTest] class also provides a `logger` attribute that is a Python logger specific to the test instance. See [Python documentation](https://docs.python.org/3/library/logging.html) for more information.

- **AntaDevice object**

Even if `device` is not a private attribute, you should not need to access this object in your code.

### Test Inputs

[AntaTest.Input](../api/tests/anta_test.md#anta.models.AntaTest.Input) is a [pydantic model](https://docs.pydantic.dev/latest/usage/models/) that allow test developers to define their test inputs. [pydantic](https://docs.pydantic.dev/latest/) provides out of the box [error handling](https://docs.pydantic.dev/latest/usage/models/#error-handling) for test input validation based on the type hints defined by the test developer.
[AntaTest.Input][anta.models.AntaTest.Input] is a [pydantic model](https://docs.pydantic.dev/latest/usage/models/) that allow test developers to define their test inputs. [pydantic](https://docs.pydantic.dev/latest/) provides out of the box [error handling](https://docs.pydantic.dev/latest/usage/models/#error-handling) for test input validation based on the type hints defined by the test developer.

The base definition of [AntaTest.Input](../api/tests/anta_test.md#anta.models.AntaTest.Input) provides common test inputs for all [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) instances:
The base definition of [AntaTest.Input][anta.models.AntaTest.Input] provides common test inputs for all [AntaTest][anta.models.AntaTest] instances:

#### Input model

Full `Input` model documentation is available in [API documentation section](../api/tests/anta_test.md#anta.models.AntaTest.Input)
Full `Input` model documentation is available in [API documentation section][anta.models.AntaTest.Input]

::: anta.models.AntaTest.Input
options:
Expand All @@ -130,7 +130,7 @@ Full `Input` model documentation is available in [API documentation section](../

#### ResultOverwrite model

Full `ResultOverwrite` model documentation is available in [API documentation section](../api/tests/anta_test.md#anta.models.AntaTest.Input.ResultOverwrite)
Full `ResultOverwrite` model documentation is available in [API documentation section][anta.models.AntaTest.Input.ResultOverwrite]

::: anta.models.AntaTest.Input.ResultOverwrite
options:
Expand All @@ -150,31 +150,31 @@ Full `ResultOverwrite` model documentation is available in [API documentation se

### Methods

- [test(self) -> None](../api/tests/anta_test.md#anta.models.AntaTest.test): This is an abstract method that **must** be implemented. It contains the test logic that can access the collected command outputs using the `instance_commands` instance attribute, access the test inputs using the `inputs` instance attribute and **must** set the `result` instance attribute accordingly. It must be implemented using the `AntaTest.anta_test` decorator that provides logging and will collect commands before executing the `test()` method.
- [render(self, template: AntaTemplate) -> list[AntaCommand]](../api/tests/anta_test.md#anta.models.AntaTest.render): This method only needs to be implemented if [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) instances are present in the `commands` class attribute. It will be called for every [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) occurrence and **must** return a list of [AntaCommand](../api/commands.md#anta.models.AntaCommand) using the [AntaTemplate.render()](../api/commands.md#anta.models.AntaTemplate.render) method. It can access test inputs using the `inputs` instance attribute.
- [test(self) -> None][anta.models.AntaTest.test]: This is an abstract method that **must** be implemented. It contains the test logic that can access the collected command outputs using the `instance_commands` instance attribute, access the test inputs using the `inputs` instance attribute and **must** set the `result` instance attribute accordingly. It must be implemented using the `AntaTest.anta_test` decorator that provides logging and will collect commands before executing the `test()` method.
- [render(self, template: AntaTemplate) -> list[AntaCommand]][anta.models.AntaTest.render]: This method only needs to be implemented if [AntaTemplate][anta.models.AntaTemplate] instances are present in the `commands` class attribute. It will be called for every [AntaTemplate][anta.models.AntaTemplate] occurrence and **must** return a list of [AntaCommand][anta.models.AntaCommand] using the [AntaTemplate.render()][anta.models.AntaTemplate.render] method. It can access test inputs using the `inputs` instance attribute.

## Test execution

Below is a high level description of the test execution flow in ANTA:

1. ANTA will parse the test catalog to get the list of [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) subclasses to instantiate and their associated input values. We consider a single [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) subclass in the following steps.
1. ANTA will parse the test catalog to get the list of [AntaTest][anta.models.AntaTest] subclasses to instantiate and their associated input values. We consider a single [AntaTest][anta.models.AntaTest] subclass in the following steps.

2. ANTA will instantiate the [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) subclass and a single device will be provided to the test instance. The `Input` model defined in the class will also be instantiated at this moment. If any [ValidationError](https://docs.pydantic.dev/latest/errors/errors/) is raised, the test execution will be stopped.
2. ANTA will instantiate the [AntaTest][anta.models.AntaTest] subclass and a single device will be provided to the test instance. The `Input` model defined in the class will also be instantiated at this moment. If any [ValidationError](https://docs.pydantic.dev/latest/errors/errors/) is raised, the test execution will be stopped.

3. If there is any [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) instance in the `commands` class attribute, [render()](../api/tests/anta_test.md#anta.models.AntaTest.render) will be called for every occurrence. At this moment, the `instance_commands` attribute has been initialized. If any rendering error occurs, the test execution will be stopped.
3. If there is any [AntaTemplate][anta.models.AntaTemplate] instance in the `commands` class attribute, [render()][anta.models.AntaTest.render] will be called for every occurrence. At this moment, the `instance_commands` attribute has been initialized. If any rendering error occurs, the test execution will be stopped.

4. The `AntaTest.anta_test` decorator will collect the commands from the device and update the `instance_commands` attribute with the outputs. If any collection error occurs, the test execution will be stopped.

5. The [test()](../api/tests/anta_test.md#anta.models.AntaTest.test) method is executed.
5. The [test()][anta.models.AntaTest.test] method is executed.

## Writing an AntaTest subclass

In this section, we will go into all the details of writing an [AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) subclass.
In this section, we will go into all the details of writing an [AntaTest][anta.models.AntaTest] subclass.

### Class definition

Import [anta.models.AntaTest](../api/tests/anta_test.md#anta.models.AntaTest) and define your own class.
Define the mandatory class attributes using [anta.models.AntaCommand](../api/commands.md#anta.models.AntaCommand), [anta.models.AntaTemplate](../api/commands.md#anta.models.AntaTemplate) or both.
Import [anta.models.AntaTest][anta.models.AntaTest] and define your own class.
Define the mandatory class attributes using [anta.models.AntaCommand][anta.models.AntaCommand], [anta.models.AntaTemplate][anta.models.AntaTemplate] or both.

!!! note
Caching can be disabled per `AntaCommand` or `AntaTemplate` by setting the `use_cache` argument to `False`. For more details about how caching is implemented in ANTA, please refer to [Caching in ANTA](../advanced_usages/caching.md).
Expand Down Expand Up @@ -256,7 +256,7 @@ class <YourTestName>(AntaTest):
```

To define an input field type, refer to the [pydantic documentation](https://docs.pydantic.dev/latest/usage/types/types/) about types.
You can also leverage [anta.custom_types](../api/tests/types.md) that provides reusable types defined in ANTA tests.
You can also leverage [anta.custom_types][anta.custom_types] that provides reusable types defined in ANTA tests.

Regarding required, optional and nullable fields, refer to this [documentation](https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields) on how to define them.

Expand All @@ -265,7 +265,7 @@ Regarding required, optional and nullable fields, refer to this [documentation](

### Template rendering

Define the `render()` method if you have [AntaTemplate](../api/commands.md#anta.models.AntaTemplate) instances in your `commands` class attribute:
Define the `render()` method if you have [AntaTemplate][anta.models.AntaTemplate] instances in your `commands` class attribute:

```python
class <YourTestName>(AntaTest):
Expand All @@ -274,7 +274,7 @@ class <YourTestName>(AntaTest):
return [template.render(<template param>=input_value) for input_value in self.inputs.<input_field>]
```

You can access test inputs and render as many [AntaCommand](../api/commands.md#anta.models.AntaCommand) as desired.
You can access test inputs and render as many [AntaCommand][anta.models.AntaCommand] as desired.

### Test definition

Expand All @@ -294,7 +294,7 @@ The logic usually includes the following different stages:
2. If needed, access the test inputs using the `self.inputs` instance attribute and write your conditional logic.
3. Set the `result` instance attribute to reflect the test result by either calling `self.result.is_success()` or `self.result.is_failure("<FAILURE REASON>")`. Sometimes, setting the test result to `skipped` using `self.result.is_skipped("<SKIPPED REASON>")` can make sense (e.g. testing the OSPF neighbor states but no neighbor was found). However, you should not need to catch any exception and set the test result to `error` since the error handling is done by the framework, see below.

The example below is based on the [VerifyTemperature](../api/tests/hardware.md#anta.tests.hardware.VerifyTemperature) test.
The example below is based on the [VerifyTemperature][anta.tests.hardware.VerifyTemperature] test.

```python
class VerifyTemperature(AntaTest):
Expand Down
2 changes: 1 addition & 1 deletion docs/cli/nrfu.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ The template `./custom_template.j2` is a simple Jinja2 template:
{% endfor %}
```

The Jinja2 template has access to all `TestResult` elements and their values, as described in this [documentation](../api/result.md#anta.result_manager.models.TestResult).
The Jinja2 template has access to all `TestResult` elements and their values, as described in this [documentation][anta.result_manager.models.TestResult].

You can also save the report result to a file using the `--output` option:

Expand Down
2 changes: 1 addition & 1 deletion docs/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ Create an [AntaInventory][anta.inventory.AntaInventory] and add an

`autorefs` resolves these identifiers to their documented locations without hard-coding the API page path or heading anchor. Keep ordinary Markdown links for guides and other authored pages.

After adding or changing an object reference, run `zensical build --strict` and check that the link opens the intended object in the rendered site.
Register new object identifiers under `MD052.ignored_labels` in `.github/.markdownlint-cli2.yaml` (lowercase with underscores removed, as reported by MD052). This central list allows autorefs syntax while keeping MD052 enabled for ordinary reference links; it does not validate Python objects. After adding or changing an object reference, run `zensical build --strict` and check that the link opens the intended object in the rendered site.

### Generated CLI snippets

Expand Down
Loading
Loading