From b9d98a7d766e94e459bf0aa2c9720c41f45e2ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthieu=20T=C3=A2che?= Date: Wed, 19 Jul 2023 12:30:39 +0200 Subject: [PATCH] Update doc for advanced usage --- anta/device.py | 14 ++-- anta/inventory/__init__.py | 4 +- anta/reporter/__init__.py | 2 +- docs/advanced_usages/as-python-lib.md | 94 ++++++++++++++------------- mkdocs.yml | 2 +- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/anta/device.py b/anta/device.py index 5e71cf656..23fec2173 100644 --- a/anta/device.py +++ b/anta/device.py @@ -60,7 +60,7 @@ class AntaDevice(ABC): An implementation of this class needs must override the abstract coroutines `collect()` and `refresh()`. - Instance attributes: + Attributes: name: Device name is_online: True if the device IP is reachable and a port can be open established: True if remote command execution succeeds @@ -136,9 +136,9 @@ async def refresh(self) -> None: Update attributes of an AntaDevice instance. This coroutine must update the following attributes of AntaDevice: - - is_online: When the device IP is reachable and a port can be open - - established: When a command execution succeeds - - hw_model: The hardware model of the device + - `is_online`: When the device IP is reachable and a port can be open + - `established`: When a command execution succeeds + - `hw_model`: The hardware model of the device """ async def copy(self, sources: List[Path], destination: Path, direction: Literal["to", "from"] = "from") -> None: @@ -158,7 +158,7 @@ class AsyncEOSDevice(AntaDevice): """ Implementation of AntaDevice for EOS using aio-eapi. - Instance attributes: + Attributes: name: Device name is_online: True if the device IP is reachable and a port can be open established: True if remote command execution succeeds @@ -189,12 +189,12 @@ def __init__( # pylint: disable=R0913 password: Password to connect to eAPI and SSH name: Device name enable_password: Password used to gain privileged access on EOS - proto: eAPI protocol. Value can be 'http' or 'https' port: eAPI port. Defaults to 80 is proto is 'http' or 443 if proto is 'https'. ssh_port: SSH port - insecure: Disable SSH Host Key validation tags: List of tags for this device timeout: Timeout value in seconds for outgoing connections. Default to 10 secs. + insecure: Disable SSH Host Key validation + proto: eAPI protocol. Value can be 'http' or 'https' """ if name is None: name = f"{host}:{port}" diff --git a/anta/inventory/__init__.py b/anta/inventory/__init__.py index 25fc2f051..bcfacbfdd 100644 --- a/anta/inventory/__init__.py +++ b/anta/inventory/__init__.py @@ -122,8 +122,8 @@ def parse( ) -> AntaInventory: # pylint: disable=too-many-arguments """ - Create an AntaInventory object from an inventory file. - Instantiate AntaDevice objects using the AsyncEOSDevice subclass. + Create an AntaInventory instance from an inventory file. + The inventory devices are AsyncEOSDevice instances. Args: inventory_file (str): Path to inventory YAML file where user has described his inputs diff --git a/anta/reporter/__init__.py b/anta/reporter/__init__.py index 403b8c3ad..b5dc0b4da 100644 --- a/anta/reporter/__init__.py +++ b/anta/reporter/__init__.py @@ -194,7 +194,7 @@ def report_summary_hosts( "# of skipped", "# of failure", "# of errors", - "List of failed ortest case", + "List of failed or test case", ] table = self._build_headers(headers=headers, table=table) for host_read in result_manager.get_hosts(): diff --git a/docs/advanced_usages/as-python-lib.md b/docs/advanced_usages/as-python-lib.md index b5ba2d0c0..8a913a716 100644 --- a/docs/advanced_usages/as-python-lib.md +++ b/docs/advanced_usages/as-python-lib.md @@ -1,53 +1,67 @@ -# How to use ANTA as a Python Library +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. -ANTA has been built to allow user to embeded its tools in your own application. This section describes how you can leverage ANTA modules to help you create your own NRFU solution. +!!! tip + If you are unfamiliar with asyncio, refer to the Python documentation relevant to your Python version - https://docs.python.org/3/library/asyncio.html -## Inventory Manager +## [AntaDevice](../api/device.md#anta.device.AntaDevice) Abstract Class -AntaInventory class is in charge of creating a list of hosts with their information and an eAPI session ready to be consummed. To do that, it connects to all devices to check reachability and ensure eAPI is running. +A device is represented in ANTA as a instance of a subclass of the [AntaDevice](../api/device.md### ::: anta.device.AntaDevice) abstract class. +There are few abstract methods that needs to be implemented by child classes: -```python -from anta.inventory import AntaInventory +- The [collect()](../api/device.md#anta.device.AntaDevice.collect) coroutine is in charge of collecting outputs of [AntaCommand](../api/models.md#anta.models.AntaCommand) instances. +- The [refresh()](../api/device.md#anta.device.AntaDevice.refresh) coroutine is in charge of updating attributes of the [AntaDevice](../api/device.md### ::: anta.device.AntaDevice) instance. These attributes are used by [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) to filter out unreachable devices or by [AntaTest](../api/models.md#anta.models.AntaTest) to skip devices based on their hardware models. + +The [copy()](../api/device.md#anta.device.AntaDevice.copy) coroutine is used to copy files to and from the device. It does not need to be implemented if tests are not using it. + +### [AsyncEOSDevice](../api/device.md#anta.device.AsyncEOSDevice) Class + +The [AsyncEOSDevice](../api/device.md#anta.device.AsyncEOSDevice) class is an implementation of [AntaDevice](../api/device.md#anta.device.AntaDevice) for Arista EOS. +It uses the [aio-eapi](https://github.com/jeremyschulman/aio-eapi) eAPI client and the [AsyncSSH](https://github.com/ronf/asyncssh) library. + +- The [collect()](../api/device.md#anta.device.AsyncEOSDevice.collect) coroutine collects [AntaCommand](../api/models.md#anta.models.AntaCommand) outputs using eAPI. +- The [refresh()](../api/device.md#anta.device.AsyncEOSDevice.refresh) coroutine tries to open a TCP connection on the eAPI port and update the `is_online` attribute accordingly. If the TCP connection succeeds, it sends a `show version` command to gather the hardware model of the device and updates the `established` and `hw_model` attributes. +- The [copy()](../api/device.md#anta.device.AsyncEOSDevice.copy) coroutine copies files to and from the device using the SCP protocol. + +## [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) Class + +The [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) class is a subclass of the standard Python type [dict](https://docs.python.org/3/library/stdtypes.html#dict). The keys of this dictionary are the device names, the values are [AntaDevice](../api/device.md#anta.device.AntaDevice) instances. + + +[AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) provides methods to interact with the ANTA inventory: + +- The [add_device()](../api/inventory.md#anta.inventory.AntaInventory.add_device) method adds an [AntaDevice](../api/device.md### ::: anta.device.AntaDevice) instance to the inventory. Adding an entry to [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) with a key different from the device name is not allowed. +- The [get_inventory()](../api/inventory.md#anta.inventory.AntaInventory.get_inventory) returns a new [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) instance with filtered out devices based on the method inputs. +- The [connect_inventory()](../api/inventory.md#anta.inventory.AntaInventory.connect_inventory) coroutine will execute the [refresh()](../api/device.md#anta.device.AntaDevice.refresh) coroutines of all the devices in the inventory. +- The [parse()](../api/inventory.md#anta.inventory.AntaInventory.parse) static method creates an [AntaInventory](../api/inventory.md#anta.inventory.AntaInventory) instance from a YAML file and returns it. The devices are [AsyncEOSDevice](../api/device.md#anta.device.AsyncEOSDevice) instances. -inventory = AntaInventory.parse( - inventory_file="inventory.yml", - username="username", - password="password", - enable_password="enable", - timeout=1, - insecure=False, -) -``` -Then it is easy to get all devices or only active devices with the following method: +To parse a YAML inventory file and print the devices connection status: ```python """ Example """ -# This is needed to run the script for python < 3.10 for typing annotations -from __future__ import annotations - import asyncio from anta.inventory import AntaInventory -async def main_loop(inv: AntaInventory) -> None: +async def main(inv: AntaInventory) -> None: """ - Take an inventory and a list of commands and: + Take an AntaInventory and: 1. try to connect to every device in the inventory - 2. print a message for every device where connection could not be established + 2. print a message for every device connection status """ await inv.connect_inventory() - # Print a list of devices that could not be connected to - for device in inv.get_inventory(established_only=False).values(): - if device.established is False: + for device in inv.values(): + if device.established: + print(f"Device {device.name} is online") + else: print(f"Could not connect to device {device.name}") if __name__ == "__main__": - # Create the inventory + # Create the AntaInventory instance inventory = AntaInventory.parse( inventory_file="inv.yml", username="arista", @@ -55,12 +69,14 @@ if __name__ == "__main__": timeout=15, ) - # Run the main asyncio entry point - res = asyncio.run(main_loop(inventory, commands)) + # Run the main coroutine + res = asyncio.run(main(inventory)) ``` +??? note "How to create your inventory file" + Please visit this [dedicated section](../usage-inventory-catalog.md) for how to use inventory and catalog files. -To run an EOS commands list on the reachable devices from the inventory +To run an EOS commands list on the reachable devices from the inventory: ```python """ Example @@ -69,18 +85,17 @@ Example from __future__ import annotations import asyncio -from copy import deepcopy from pprint import pprint from anta.inventory import AntaInventory from anta.models import AntaCommand -async def main_loop(inv: AntaInventory, commands: list[str]) -> dict[str, list[AntaCommand]]: +async def main(inv: AntaInventory, commands: list[str]) -> dict[str, list[AntaCommand]]: """ - Take an inventory and a list of commands and: + Take an AntaInventory and a list of commands as string and: 1. try to connect to every device in the inventory - 2. collect the results of the commands towards each device + 2. collect the results of the commands from each device Returns: a dictionary where key is the device name and the value is the list of AntaCommand ran towards the device @@ -91,7 +106,6 @@ async def main_loop(inv: AntaInventory, commands: list[str]) -> dict[str, list[A coros = [] # dict to keep track of the commands per device result_dict = {} - print("DONE") for name, device in inv.get_inventory(established_only=True).items(): anta_commands = [AntaCommand(command=command, ofmt="json") for command in commands] result_dict[name] = anta_commands @@ -104,7 +118,7 @@ async def main_loop(inv: AntaInventory, commands: list[str]) -> dict[str, list[A if __name__ == "__main__": - # Create the inventory + # Create the AntaInventory instance inventory = AntaInventory.parse( inventory_file="inv.yml", username="arista", @@ -116,19 +130,11 @@ if __name__ == "__main__": commands = ["show version", "show ip bgp summary"] # Run the main asyncio entry point - res = asyncio.run(main_loop(inventory, commands)) + res = asyncio.run(main(inventory, commands)) pprint(res) ``` -!!! tip - If you are unfamiliar with asyncio, refer to the Python documentation relevant to your Python version - https://docs.python.org/3/library/asyncio.html - -You can find the ANTA Inventory module [here](../api/inventory.md). - -??? note "How to create your inventory file" - Please visit this [dedicated section](../usage-inventory-catalog.md) for how to use inventory and catalog files. - ## Use tests from ANTA diff --git a/mkdocs.yml b/mkdocs.yml index 19cf7ae75..873f27d1f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -162,7 +162,7 @@ nav: - Helpers: cli/debug.md - Advanced Usages: - Create your own Library: advanced_usages/custom-tests.md - - ANTA as python lib: advanced_usages/as-python-lib.md + - ANTA as a Python Library: advanced_usages/as-python-lib.md - Tests Catalog Documentation: - Overview: api/tests.md - AAA: api/tests.aaa.md