def report_summary_tests(
self,
result_manager: ResultManager,
testcase: Optional[str] = None,
diff --git a/search/search_index.json b/search/search_index.json
index 4b92e101a..7fed3ae87 100644
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#anta-documentation","title":"ANTA Documentation","text":"This website provides generic documentation related to the Arista Network Test Automation framework (ANTA)
"},{"location":"#arista-network-test-automation-anta-framework","title":"Arista Network Test Automation (ANTA) Framework","text":"This repository is a Python package to automate tests on Arista devices.
- The package name is ANTA, which stands for Arista Network Test Automation.
- This package provides a set of tests to validate the state of your network.
- This package can be imported in Python scripts:
- To automate NRFU (Network Ready For Use) test on a preproduction network
- To automate tests on a live network (periodically or on demand)
This repository comes with a cli to run Arista Network Test Automation (ANTA) framework using your preferred shell:
# Install ANTA\npip install anta\n\n# Run ANTA cli\n$ anta\nUsage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --username TEXT Username to connect to EOS [env var: ANTA_USERNAME]\n--password TEXT Password to connect to EOS [env var: ANTA_PASSWORD]\n--timeout INTEGER Connection timeout (default 5) [env var: ANTA_TIMEOUT]\n--enable-password TEXT Enable password if required to connect [env var: ANTA_ENABLE_PASSWORD]\n-i, --inventory PATH Path to your inventory file [env var: ANTA_INVENTORY]\n--timeout INTEGER Connection timeout (default 5) [env var: ANTA_TIMEOUT]\n--help Show this message and exit.\n\nCommands:\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n
"},{"location":"contribution/","title":"Contributions","text":""},{"location":"contribution/#how-to-contribute-to-anta","title":"How to contribute to ANTA","text":"Contribution model is based on a fork-model. Don\u2019t push to arista-netdevops-community/anta directly. Always do a branch in your repository and create a PR.
To help development, open your PR as soon as possible even in draft mode. It helps other to know on what you are working on and avoid duplicate PRs.
"},{"location":"contribution/#install-repository","title":"Install repository","text":"Run these commands to install:
- The package ANTA and its dependencies
- ANTA cli executable.
# Clone repository\ngit clone https://github.com/arista-netdevops-community/anta.git\ncd network-test-automation\n\n# Install module in editable mode\npip install -e .\n
Run these commands to verify:
# Check python installation\n$ pip list\n\n# Check version using cli\n$ anta --version\nanta, version 0.5.0\n
"},{"location":"contribution/#install-development-requirements","title":"Install development requirements","text":"Run pip to install anta and its developement tools.
pip install 'anta[dev]'\n
This command has to be done after you install repository with commands provided in previous section.
Then, tox is configued with few environment to run CI locally:
tox list\ndefault environments:\nclean -> run the test driver with /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/bin/python3.9\npy38 -> run the test driver with py38\npy39 -> run the test driver with py39\npy310 -> run the test driver with py310\nlint -> check the code style\ntype -> check typing\nreport -> run the test driver with /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/bin/python3.9\n\nadditional environments:\n3.8 -> run the test driver with 3.8\n3.9 -> run the test driver with 3.9\n3.10 -> run the test driver with 3.10\n
"},{"location":"contribution/#code-linting","title":"Code linting","text":"tox -e lint\n[...]\nlint: commands[0]> flake8 --max-line-length=165 --config=/dev/null anta\nlint: commands[1]> flake8 --max-line-length=165 --config=/dev/null scripts\nlint: commands[2]> flake8 --max-line-length=165 --config=/dev/null tests\nlint: commands[3]> pylint anta\n\n--------------------------------------------------------------------\nYour code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)\n\nlint: commands[4]> pylint scripts\n\n-------------------------------------------------------------------\nYour code has been rated at 10.00/10 (previous run: 7.15/10, +2.85)\n\n.pkg: _exit> python /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/lib/python3.9/site-packages/\\\npyproject_api/_backend.py True setuptools.build_meta\n lint: OK (28.37=setup[7.03]+cmd[0.38,0.23,0.25,11.07,9.41] seconds)\ncongratulations :) (28.45 seconds)\n
"},{"location":"contribution/#code-typing","title":"Code Typing","text":"tox -e type\n\ntype: commands[0]> mypy --config-file=pyproject.toml anta\nSuccess: no issues found in 38 source files\ntype: commands[1]> mypy --config-file=pyproject.toml scripts\nSuccess: no issues found in 6 source files\n.pkg: _exit> python /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/lib/python3.9/site-packages/\\\npyproject_api/_backend.py True setuptools.build_meta\n type: OK (28.80=setup[24.54]+cmd[3.35,0.90] seconds)\ncongratulations :) (28.89 seconds)\n
"},{"location":"contribution/#unit-tests","title":"Unit tests","text":"To keep high quality code, we require to provide a Pytest for every tests implemented in ANTA.
All submodule should have its own pytest section under tests/units/anta_tests/<submodule-name>
. In this directory, you should have 3 files:
__init__.py
: Just because it is used as a python module data.py
: Where all your parametrize go. So all your test information should be located here test_exc.py
: Pytest file with test definition.
A pytest definition should be similar to this template:
# -*- coding: utf-8 -*-\n\n\"\"\"\nTests for anta.tests.hardware.py\n\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport logging\nfrom typing import Any\nfrom unittest.mock import MagicMock\n\nimport pytest\n\nfrom anta.tests.hardware import VerifyAdverseDrops\nfrom tests.lib.utils import generate_test_ids_list\n\nfrom .data import INPUT_<TEST_NAME>\n\n@pytest.mark.parametrize(\"test_data\", INPUT_<TEST_NAME>, ids=generate_test_ids_list(INPUT_<TEST_NAME>))\ndef test_<TEST_CASE>(mocked_device: MagicMock, test_data: Any) -> None:\n\"\"\"Check <TEST_CASE>.\"\"\"\n\n logging.info(f\"Mocked device is: {mocked_device.host}\")\n logging.info(f\"Mocked HW is: {mocked_device.hw_model}\")\n\n test = <TEST_CASE>(mocked_device, eos_data=test_data[\"eos_data\"])\n asyncio.run(test.test())\n\n logging.debug(f\"test result is: {test.result}\")\n\n assert str(test.result.name) == mocked_device.name\n assert test.result.result == test_data[\"expected_result\"]\n
The mocked_device
object is a fixture defined in Pytest to represent an InventoryDevice and the parametrize test_data
is a list of dictionries with structure:
INPUT_RUNNING_CONFIG: List[Dict[str, Any]] = [\n # Test Case #1\n {\n \"name\": \"failure\",\n \"eos_data\": [\"blah blah\"],\n \"side_effect\": None,\n \"expected_result\": \"failure\",\n \"expected_messages\": [\"blah blah\"]\n },\n # Test Case #2\n {\n ...\n },\n]\n
Where we have:
name
: Name of the test displayed by Pytest eos_data
: a list of data coming from EOS. side_effect
: defined for futur use. expected_result
: Result we expect for this test expected_messages
: Optional messages we expect for the test.
Use Anta CLI to get test data
To complete this block, you can use anta debug
commands to get AntaTestCommand
output to use in your test.
"},{"location":"contribution/#git-pre-commit-hook","title":"Git Pre-commit hook","text":"pip install pre-commit\npre-commit install\n
When running a commit or a pre-commit check:
\u276f echo \"import foobaz\" > test.py && git add test.py\n\u276f pre-commit\npylint...................................................................Failed\n- hook id: pylint\n- exit code: 22\n\n************* Module test\ntest.py:1:0: C0114: Missing module docstring (missing-module-docstring)\ntest.py:1:0: E0401: Unable to import 'foobaz' (import-error)\ntest.py:1:0: W0611: Unused import foobaz (unused-import)\n
"},{"location":"contribution/#test-your-documentation","title":"Test your documentation","text":"Writing documentation is crucial but managing links can be cumbersome. To be sure there is no 404, you can use muffet
with this cli:
muffet -c 2 --color=always http://127.0.0.1:8000 -e fonts.gstatic.com\n
"},{"location":"contribution/#continuous-integration","title":"Continuous Integration","text":"GitHub actions is used to test git pushes and pull requests. The workflows are defined in this directory. We can view the result here
"},{"location":"getting-started/","title":"Getting Started","text":""},{"location":"getting-started/#getting-started","title":"Getting Started","text":"This section shows how to use ANTA with basic configuration. All examples are based on Arista Test Drive (ATD) topology you can access by reaching out to your prefered SE.
"},{"location":"getting-started/#installation","title":"Installation","text":"The easiest way to intall ANTA package is to run Python (>=3.8
) and its pip package to install:
pip install anta\n
For more details about how to install package, please see the requirements and intallation section.
"},{"location":"getting-started/#configure-arista-eos-devices","title":"Configure Arista EOS devices","text":"First, you need to configure your management interface
vrf instance MGMT\n!\ninterface Management0\n description oob_management\n vrf MGMT\n ip address 192.168.0.10/24\n!\n
Then, configure access to eAPI:
!\nmanagement api http-commands\n protocol https port 443\n no shutdown\n vrf MGMT\n no shutdown\n !\n!\n
"},{"location":"getting-started/#create-your-inventory","title":"Create your inventory","text":"First, we need to list devices we want to test. You can create a file manually with this format:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
You can read more details about how to build your inventory here
"},{"location":"getting-started/#test-catalog","title":"Test Catalog","text":"To test your network, it is important to define a test catalog to list all the tests to run against your inventory. Test catalog references python functions into a yaml file. This file can be loaded by anta.loader.py
The structure to follow is like:
<anta_tests_submodule>:\n- <anta_tests_submodule function name>:\n<test function option>:\n<test function option value>\n
You can read more details about how to build your catalog here
Here is an example for basic things:
# Load anta.tests.software\nanta.tests.software:\n- VerifyEosVersion: # Verifies the device is running one of the allowed EOS version.\nversions: # List of allowed EOS versions.\n- 4.25.4M\n- 4.26.1F\n- '4.28.3M-28837868.4283M (engineering build)'\n- VerifyTerminAttrVersion:\nversions:\n- v1.22.1\n\nanta.tests.system:\n- VerifyUptime: # Verifies the device uptime is higher than a value.\nminimum: 1\n- VerifyNtp:\n- VerifySyslog:\n\nanta.tests.mlag:\n- VerifyMlagStatus:\n- VerifyMlagInterface:\n- VerifyMlagConfigSanity:\n\nanta.tests.configuration:\n- VerifyZeroTouch: # Verifies ZeroTouch is disabled.\n- VerifyRunningConfigDiffs:\n
"},{"location":"getting-started/#test-your-network","title":"Test your network","text":"To test EOS devices, this package comes with a generic CLI entrypoint to run tests in your network. It requires an inventory file as well as a test catalog.
This entrypoint has multiple options to manage test coverage and reporting.
# Generic ANTA options\n$ anta\nUsage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --version Show the version and exit.\n --username TEXT Username to connect to EOS [env var: ANTA_USERNAME;\nrequired]\n--password TEXT Password to connect to EOS [env var: ANTA_PASSWORD;\nrequired]\n--timeout INTEGER Connection timeout (default 5) [env var:\n ANTA_TIMEOUT]\n--enable-password TEXT Enable password if required to connect [env var:\n ANTA_ENABLE_PASSWORD]\n-i, --inventory PATH Path to your inventory file [env var:\n ANTA_INVENTORY; required]\n--help Show this message and exit.\n\nCommands:\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n\n\n\n# NRFU part of ANTA\n$ anta nrfu\nUsage: anta nrfu [OPTIONS] COMMAND [ARGS]...\n\n Run NRFU against inventory devices\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n json ANTA command to check network state with JSON result\n table ANTA command to check network states with table result\n text ANTA command to check network states with text result\n
Default output is a table format listing all test results, and it can be changed to a report per test case or per host
"},{"location":"getting-started/#default-report-using-table","title":"Default report using table","text":"anta \\\n--username tom \\\n--password arista123 \\\n--enable-password t \\\n--inventory .personal/inventory_atd.yml \\\nnrfu table --tags leaf --catalog .personal/tests-bases.yml\n\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/tests-bases.yml \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n All tests results\n\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 Device IP \u2503 Test Name \u2503 Test Status \u2503 Message(s) \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 leaf01 \u2502 VerifyEosVersion \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyTerminAttrVersion \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyUptime \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyNtp \u2502 failure \u2502 not sync with NTP server (NTP is disabled.) \u2502\n\u2502 leaf01 \u2502 VerifySyslog \u2502 failure \u2502 Device has some log messages with a severity WARNING or higher \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n
"},{"location":"getting-started/#report-in-text-mode","title":"Report in text mode","text":"$ anta \\\n --username tom \\\n --password arista123 \\\n --enable-password t \\\n --inventory .personal/inventory_atd.yml \\\n nrfu text --tags leaf --catalog .personal/tests-bases.yml\n\nleaf01 :: VerifyEosVersion :: SUCCESS\nleaf01 :: VerifyTerminAttrVersion :: SUCCESS\nleaf01 :: VerifyUptime :: SUCCESS\nleaf01 :: VerifyNtp :: FAILURE (not sync with NTP server (NTP is disabled.))\nleaf01 :: VerifySyslog :: FAILURE (Device has some log messages with a severity WARNING or higher)\n...\n
"},{"location":"getting-started/#report-per-host","title":"Report per host","text":"$ anta \\\n--username tom \\\n--password arista123 \\\n--enable-password t \\\n--inventory .personal/inventory_atd.yml \\\nnrfu json --tags leaf --catalog .personal/tests-bases.yml\n\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 JSON results of all tests \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n[\n{\n\"name\": \"leaf01\",\n \"test\": \"VerifyEosVersion\",\n \"result\": \"success\",\n \"messages\": \"[]\"\n},\n {\n\"name\": \"leaf01\",\n \"test\": \"VerifyTerminAttrVersion\",\n \"result\": \"success\",\n \"messages\": \"[]\"\n},\n...\n]\n
You can find more information under the usage section of the website
"},{"location":"requirements-and-installation/","title":"Installation","text":""},{"location":"requirements-and-installation/#anta-requirements","title":"ANTA Requirements","text":""},{"location":"requirements-and-installation/#python-version","title":"Python version","text":"Python 3 (>=3.8
and =<3.10
) is required:
python --version\nPython 3.9.9\n
"},{"location":"requirements-and-installation/#install-anta-package","title":"Install ANTA package","text":"This installation will deploy tests collection, scripts and all their Python requirements.
The ANTA package and the cli require some packages that are not part of the Python standard library. They are indicated in the pyproject.toml file
"},{"location":"requirements-and-installation/#install-from-pypi-server","title":"Install from Pypi server","text":"pip install anta\n
"},{"location":"requirements-and-installation/#install-anta-from-github","title":"Install ANTA from github","text":"pip install git+https://github.com/arista-netdevops-community/anta.git\n
You can even specify the branch, tag or commit:
<anta-repository>@<cool-feature-branch>
<anta-repository>@<cool-tag>
<anta-repository>@<cool-hash>
"},{"location":"requirements-and-installation/#check-installation","title":"Check installation","text":"Run these commands to verify:
# Check ANTA has been installed in your python path\npip list | grep anta\n\n# Check scripts are in your $PATH\n# Path may differ but it means CLI is in your path\nwhich anta\n/home/tom/.pyenv/shims/anta\n\n# Chck ANTA version\nanta --version\nanta, version 0.5.0\n
"},{"location":"requirements-and-installation/#eos-requirements","title":"EOS Requirements","text":"To get ANTA working, your Arista EOS devices must have the following configuration (assuming you connect to the device using Management interface in MGMT VRF):
configure\n!\nvrf instance MGMT\n!\ninterface Management1\n description oob_management\n vrf MGMT\n ip address 10.73.1.105/24\n!\nend\n
Enable eAPI on the MGMT vrf:
configure\n!\nmanagement api http-commands\n protocol https port 443\n no shutdown\n vrf MGMT\n no shutdown\n!\nend\n
Now the swicth accepts on port 443 in the MGMT VRF HTTPS requests containing a list of CLI commands.
Run these EOS commands to verify:
show management http-server\nshow management api http-commands\n
"},{"location":"usage-inventory-catalog/","title":"Inventory & Tests catalog","text":""},{"location":"usage-inventory-catalog/#inventory-catalog-definition","title":"Inventory & Catalog definition","text":"This page describes how to create an inventory and a tests catalog.
"},{"location":"usage-inventory-catalog/#create-an-inventory-file","title":"Create an inventory file","text":"anta
cli needs an inventory file to list all devices to tests. This inventory is a YAML file with the folowing keys:
anta_inventory:\nhosts:\n- host: < ip address value >\nport: < TCP port for eAPI. Default is 443 (Optional)>\nname: < name to display in report. Default is host:port (Optional) >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\nnetworks:\n- network: < network using CIDR notation >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\nranges:\n- start: < first ip address value of the range >\nend: < last ip address value of the range >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\n
Your inventory file can be based on any of these 3 keys and shall start with anta_inventory
key. A full description of inventory model is available in API documentation
The next output is an inventory example:
---\nanta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\nnetworks:\n- network: '192.168.110.0/24'\ntags: ['fabric', 'leaf']\nranges:\n- start: 10.0.0.9\nend: 10.0.0.11\ntags: ['fabric', 'l2leaf']\n
"},{"location":"usage-inventory-catalog/#test-catalog","title":"Test Catalog","text":"In addition to your inventory file, you also have to define a catalog of tests to execute against all your devices. This catalogue list all your tests and their parameters.
Its format is a YAML file and keys are tests functions inherited from the python path. Let\u2019s take an example below:
"},{"location":"usage-inventory-catalog/#default-tests-catalog","title":"Default tests catalog","text":"All tests are located under anta.tests
module and are categorised per family (one submodule). So to run test for software version, you can do:
anta.tests.software:\n- VerifyEosVersion:\n
It will load the test VerifyEosVersion
located in anta.tests.software
. But since this function has parameters, we will create a catalog with the following structure:
anta.tests.software:\n- VerifyEosVersion:\n# List of allowed EOS versions.\nversions:\n- 4.25.4M\n- 4.26.1F\n
To get a list of all available tests and their respective parameters, you can read the tests section of this website.
The following example gives a very minimal tests catalog you can use in almost any situation
---\n# Load anta.tests.software\nanta.tests.software:\n# Verifies the device is running one of the allowed EOS version.\n- VerifyEosVersion:\n# List of allowed EOS versions.\nversions:\n- 4.25.4M\n- 4.26.1F\n\n# Load anta.tests.system\nanta.tests.system:\n# Verifies the device uptime is higher than a value.\n- VerifyUptime:\nminimum: 1\n\n# Load anta.tests.configuration\nanta.tests.configuration:\n# Verifies ZeroTouch is disabled.\n- VerifyZeroTouch:\n- VerifyRunningConfigDiffs:\n
If your test is based on AntaTestTemplate
, you have to provide inputs for EOS CLI template by using tpl_options
list:
anta.tests.routing.bgp:\n- VerifyBGPIPv4UnicastCount:\nnumber: 3\ntemplate_params:\n- vrf: default\n- vrf: customer-01\n
Which is required for the following test definition:
class VerifyBGPIPv4UnicastCount(AntaTest):\n\"\"\"\n ...\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastCount\"\n description = \"...\"\n categories = [\"routing\", \"bgp\"]\n template = AntaTestTemplate(template=\"show bgp ipv4 unicast summary vrf {vrf}\")\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n pass\n
"},{"location":"usage-inventory-catalog/#custom-tests-catalog","title":"Custom tests catalog","text":"In case you want to leverage your own tests collection, you can use the following syntax:
<your package name>:\n- <your test in your package name>:\n
So for instance, it could be:
titom73.tests.system:\n- VerifyPlatform:\ntype: ['cEOS-LAB']\n
How to create custom tests
To create your custom tests, you should refer to this following documentation
"},{"location":"advanced_usages/as-python-lib/","title":"ANTA as python lib","text":""},{"location":"advanced_usages/as-python-lib/#how-to-use-anta-as-a-python-library","title":"How to use ANTA as a Python Library","text":"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.
"},{"location":"advanced_usages/as-python-lib/#inventory-manager","title":"Inventory Manager","text":"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.
from anta.inventory import AntaInventory\n\ninventory = AntaInventory.parse(\n inventory_file=\"inventory.yml\",\n username=\"username\",\n password=\"password\",\n enable_password=\"enable\",\n timeout=1,\n)\n
Then it is easy to get all devices or only active devices with the following method:
# print the non reachable devices\nfor device in inventory.get_inventory(established_only=False):\n if device.established is False:\n print(f\"Could not connect to device {device.host}\")\n\n# run an EOS commands list on the reachable devices from the inventory\nfor device in inventory.get_inventory(established_only=True):\n device.session.runCmds(\n 1, [\"show version\", \"show ip bgp summary\"]\n )\n
You can find the ANTA Inventory module here.
How to create your inventory file Please visit this dedicated section for how to use inventory and catalog files.
"},{"location":"advanced_usages/as-python-lib/#use-tests-from-anta","title":"Use tests from ANTA","text":"All the test functions are based on the exact same input and returns a generic structure with different information.
"},{"location":"advanced_usages/as-python-lib/#test-input","title":"Test input","text":"Any test input is based on an InventoryDevice
object and a list of options. Here is an example to check uptime and check it is higher than minimum
option.
def verify_uptime(device: InventoryDevice, minimum: int = None) -> TestResult:\n
In general, InventoryDevice
is an object created by AntaInventory
. But it can be manually generated by following required data model.
Here is an example of a list of InventoryDevice
[\n {\n \"InventoryDevice(host=IPv4Address('192.168.0.17')\",\n \"username='ansible'\",\n \"password='ansible'\",\n \"session=<ServerProxy for ansible:ansible@192.168.0.17/command-api>\",\n \"url='https://ansible:ansible@192.168.0.17/command-api'\",\n \"established=True\",\n \"is_online=True\",\n \"hw_model=cEOS-LAB\",\n },\n\n {\n \"InventoryDevice(host=IPv4Address('192.168.0.2')\",\n \"username='ansible'\",\n \"password='ansible'\",\n \"session=None\",\n \"url='https://ansible:ansible@192.168.0.2/command-api'\",\n \"established=False\"\n \"is_online=False\",\n \"tags\": ['dc1', 'spine', 'pod01'],\n \"hw_model=unset\",\n }\n]\n
"},{"location":"advanced_usages/as-python-lib/#test-output","title":"Test output","text":"All tests return a TestResult structure with the following elements:
result
: Can be success
, skipped
, failure
, error
and report result of the test host
: IP address of the tested device test
: Test name runs on host
message
: Optional message returned by the test.
"},{"location":"advanced_usages/as-python-lib/#test-structure","title":"Test structure","text":"All tests are built on a class named AntaTest
which provides a complete toolset for a test:
- Object creation
- Test definition
- TestResult definition
- Abstracted method to collect data
This approach means each time you create a test it will be based on this AntaTest
class. Besides that, you will have to provide some elements:
name
: Name of the test description
: A human readable description of your test categories
: a list of categories to sort test. commands
: a list of command to run. This list must be a list of AntaTestCommand
which is described in the next part of this document.
Here is an example of a hardware test related to device temperature:
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
When you run the test, object will automatically call its anta.models.AntaTest.collect()
method to get device output. This method does a loop to call anta.inventory.models.InventoryDevice.collect()
methods which is in charge of managing device connection and how to get data.
run test offline You can also pass eos data directly to your test if you want to validate data collected in a different workflow. An example is provided below just for information:
test = VerifyTemperature(mocked_device, eos_data=test_data[\"eos_data\"])\nasyncio.run(test.test())\n
test function is always the same and must be defined with the @AntaTest.anta_test
decorator. This function takes at least one argument which is a anta.inventory.models.InventoryDevice
object and can have multiple additional parameters depending of your test definition. All parameters must come with a default value and the test function should validate the parameters values.
class VerifyTemperature(AntaTest):\n ...\n @AntaTest.anta_test\n def test(self) -> None:\n pass\n\nclass VerifyTransceiversManufacturers(AntaTest):\n ...\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n pass\n
The test itself does not return any value, but the result is directly availble from your object and exposes a anta.result_manager.models.TestResult
object with result, name of the test and optional messages.
from anta.tests.hardware import VerifyTemperature\n\ntest = VerifyTemperature(mocked_device, eos_data=test_data[\"eos_data\"])\nasyncio.run(test.test())\nassert test.result.result == \"success\"\n
"},{"location":"advanced_usages/as-python-lib/#commands-for-test","title":"Commands for test","text":"To make it easier to get data, ANTA defines 2 different classes to manage commands to send to device:
"},{"location":"advanced_usages/as-python-lib/#antamodelsantatestcommand","title":"anta.models.AntaTestCommand
","text":"Abstract a command with following information:
- Command to run,
- Ouput format expected
- eAPI version
- Output of the command
Usage example:
from anta.models import AntaTestCommand\n\ncmd1 = AntaTestCommand(command=\"show zerotouch\")\ncmd2 = AntaTestCommand(command=\"show running-config diffs\", ofmt=\"text\")\n
"},{"location":"advanced_usages/as-python-lib/#antamodelsantatesttemplate","title":"anta.models.AntaTestTemplate
","text":"Because some command can require more dynamic than just a command with no parameter provided by user, ANTA supports command template: you define a template in your test class and user provide parameters when creating test object.
class RunArbitraryTemplateCommand(AntaTest):\n\"\"\"\n Run an EOS command and return result\n Based on AntaTest to build relevant output for pytest\n \"\"\"\n\n name = \"Run aributrary EOS command\"\n description = \"To be used only with anta debug commands\"\n template = AntaTestTemplate(template=\"show interfaces {ifd}\")\n categories = [\"debug\"]\n\n @AntaTest.anta_test\n def test(self) -> None:\n errdisabled_interfaces = [interface for interface, value in response[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n ...\n\n\nparams = [{\"ifd\": \"Ethernet2\"}, {\"ifd\": \"Ethernet49/1\"}]\nrun_command1 = RunArbitraryTemplateCommand(device_anta, params)\n
In this example, test waits for interfaces to check from user setup and will only check for interfaces in params
"},{"location":"advanced_usages/custom-tests/","title":"Create your own Library","text":""},{"location":"advanced_usages/custom-tests/#create-your-own-custom-tests","title":"Create your own custom tests","text":"This documentation applies for both create tests in ANTA package or your custom package.
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\u2019s guide to package Python code. We assume it is well known and we won\u2019t focus on this aspect. Thus, your package must be impartable by ANTA hence available in $PYTHONPATH
by any method.
"},{"location":"advanced_usages/custom-tests/#generic-approach","title":"Generic approach","text":"ANTA comes with a class to use to build test. This class provides all the toolset required to define, collect and test data. The next code is an example of how to use ANTA to build a test
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"advanced_usages/custom-tests/#python-imports","title":"Python imports","text":""},{"location":"advanced_usages/custom-tests/#mandatory-imports","title":"Mandatory imports","text":"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. anta.models.AntaTest
: class that gives you all the tooling for your test anta.models.AntaTestCommand
: A class to abstract an Arista EOS command
from anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n ...\n\n @AntaTest.anta_test\n def test(self) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#optional-anta-imports","title":"Optional ANTA imports","text":"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\n\n\nclass VerifyTransceiversManufacturers(AntaTest):\n ...\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#optional-python-imports","title":"Optional python imports","text":"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.
If your test development is part of a pull request for ANTA, it is stringly advised to also import typing
since our code testing requires to be compatible with Mypy.
"},{"location":"advanced_usages/custom-tests/#code-for-a-test","title":"Code for a test","text":"A test is a python class where a test function is defined and will be run by the framework. So first you need to declare your class and then define your test function.
"},{"location":"advanced_usages/custom-tests/#create-test-class","title":"Create Test Class","text":"To create class, you have to provide 4 elements:
Metadata information
name
: Name of the test description
: A human readable description of your test categories
: a list of categories to sort test.
Commands to run
commands
: a list of command to run. This list must be a list of AntaTestCommand
which is described in the next part of this document. template
: a command template (AntaTestTemplate
) to run where variables are provided during test execution.
It is either commands
or template
. But not both.
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass <YourTestName>(AntaTest):\n\"\"\"\n <a docstring description of your test>\n \"\"\"\n\n name = \"YourTestName\" # should be your class name\n description = \"<test description in human reading format>\"\n categories = [\"<a list of arbitrary categories>\"]\n commands = [\n AntaTestCommand(\n command=\"<eos command to run>\",\n ofmt=\"<command format output>\",\n version=\"<eapi version to use>\"\n )\n ]\n
This class will inherit methods from AntaTest and specfically the __init__(self,...)
method to build your object. This function takes following arguments when you instantiate an object:
device (InventoryDevice)
: Device object where to test happens. template_params
: If template is used in the test definition, then we provide data to build list of commands. eos_data
: Potential EOS data to pass if we don\u2019t want to connect to device to grab data. labels
: a list of labels. It is not used yet and it is for futur use.
"},{"location":"advanced_usages/custom-tests/#function-definition","title":"Function definition","text":"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:
class <YourTestName>(AntaTest):\n ...\n def test(self) -> None:\n pass\n
If you want to support option in your test, just declare your options in your test method:
class <YourTestName>(AntaTest):\n ...\n def test(self, my_param1: str) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#check-inputs","title":"Check inputs","text":"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\nif not minimum:\n self.result.is_skipped(\"verify_dynamic_vlan was run without minimum value set\")\nelse:\n ...\n
"},{"location":"advanced_usages/custom-tests/#implement-your-logic","title":"Implement your logic","text":"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
# Grab data for your command\ncommand_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n# Do your test: In this example we count number of vlans with field dynamic set to true\nnum_dyn_vlan = len([ vlan for vlan,data in command_output['vlans'].items() if command_output['dynamic'] is True])\nif num_dyn_vlan >= minimum:\n self.result.is_success()\nelse:\n self.result.is_failure(f\"Device has {num_dyn_vlan} configured, we expect at least {minimum}\")\n
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')\n
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\n
"},{"location":"advanced_usages/custom-tests/#create-your-catalog","title":"Create your catalog","text":"This section is required only if you are not merging your development into ANTA. Otherwise, just follow contribution guide.
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:\n- VerifyFeatureX:\nminimum: 1\n
And now you can run your NRFU tests with the CLI: anta nrfu text --catalog test_custom.yml\nspine01 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)\nspine02 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)\nleaf01 :: verify_dynamic_vlan :: SUCCESS\nleaf02 :: verify_dynamic_vlan :: SUCCESS\nleaf03 :: verify_dynamic_vlan :: SUCCESS\nleaf04 :: verify_dynamic_vlan :: SUCCESS\n
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.
"},{"location":"api/device/","title":"AntaDevice Abstracted class","text":""},{"location":"api/device/#antadevice-abstracted-class","title":"AntaDevice Abstracted class","text":" Bases: ABC
Abstract class representing a device in ANTA. An implementation of this class needs must override the abstract coroutines collect()
and refresh()
.
Instance 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 hw_model: Hardware model of the device tags: List of tags for this device
Source code in anta/device.py
class AntaDevice(ABC):\n\"\"\"\n Abstract class representing a device in ANTA.\n An implementation of this class needs must override the abstract coroutines `collect()` and\n `refresh()`.\n\n Instance attributes:\n name: Device name\n is_online: True if the device IP is reachable and a port can be open\n established: True if remote command execution succeeds\n hw_model: Hardware model of the device\n tags: List of tags for this device\n \"\"\"\n\n def __init__(self, name: str, tags: Optional[List[str]] = None) -> None:\n\"\"\"\n Constructor of AntaDevice\n\n Args:\n name: Device name\n tags: List of tags for this device\n \"\"\"\n self.name: str = name\n self.hw_model: Optional[str] = None\n self.tags: List[str] = tags if tags is not None else []\n self.is_online: bool = False\n self.established: bool = False\n\n # Ensure tag 'all' is always set\n if DEFAULT_TAG not in self.tags:\n self.tags.append(DEFAULT_TAG)\n\n def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield \"name\", self.name\n yield \"tags\", self.tags\n yield \"hw_model\", self.hw_model\n yield \"is_online\", self.is_online\n yield \"established\", self.established\n\n @abstractmethod\n def __eq__(self, other: object) -> bool:\n\"\"\"\n AntaDevice equality depends on the class implementation.\n \"\"\"\n\n @abstractmethod\n async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output.\n This abstract coroutine can be used to implement any command collection method\n for a device in ANTA.\n\n The `collect()` implementation needs to populate the `output` attribute\n of the `AntaTestCommand` object passed as argument.\n\n If a failure occurs, the `collect()` implementation is expected to catch the\n exception and implement proper logging, the `output` attribute of the\n `AntaTestCommand` object passed as argument would be `None` in this case.\n\n Args:\n command: the command to collect\n \"\"\"\n\n async def collect_commands(self, commands: List[AntaTestCommand]) -> None:\n\"\"\"\n Collect multiple commands.\n\n Args:\n commands: the commands to collect\n \"\"\"\n await asyncio.gather(*(self.collect(command=command) for command in commands))\n\n @abstractmethod\n async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AntaDevice instance.\n\n This coroutine must update the following attributes of AntaDevice:\n - is_online: When the device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n\n async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device, usually through SCP.\n It is not mandatory to implement this for a valid AntaDevice subclass.\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n raise NotImplementedError(f\"copy() method has not been implemented in {self.__class__.__name__} definition\")\n
"},{"location":"api/device/#anta.device.AntaDevice.__eq__","title":"__eq__(other)
abstractmethod
","text":"AntaDevice equality depends on the class implementation.
Source code in anta/device.py
@abstractmethod\ndef __eq__(self, other: object) -> bool:\n\"\"\"\n AntaDevice equality depends on the class implementation.\n \"\"\"\n
"},{"location":"api/device/#anta.device.AntaDevice.__init__","title":"__init__(name, tags=None)
","text":"Constructor of AntaDevice
Parameters:
Name Type Description Default name
str
Device name
required tags
Optional[List[str]]
List of tags for this device
None
Source code in anta/device.py
def __init__(self, name: str, tags: Optional[List[str]] = None) -> None:\n\"\"\"\n Constructor of AntaDevice\n\n Args:\n name: Device name\n tags: List of tags for this device\n \"\"\"\n self.name: str = name\n self.hw_model: Optional[str] = None\n self.tags: List[str] = tags if tags is not None else []\n self.is_online: bool = False\n self.established: bool = False\n\n # Ensure tag 'all' is always set\n if DEFAULT_TAG not in self.tags:\n self.tags.append(DEFAULT_TAG)\n
"},{"location":"api/device/#anta.device.AntaDevice.__rich_repr__","title":"__rich_repr__()
","text":"Implements Rich Repr Protocol https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol
Source code in anta/device.py
def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield \"name\", self.name\n yield \"tags\", self.tags\n yield \"hw_model\", self.hw_model\n yield \"is_online\", self.is_online\n yield \"established\", self.established\n
"},{"location":"api/device/#anta.device.AntaDevice.collect","title":"collect(command)
async
abstractmethod
","text":"Collect device command output. This abstract coroutine can be used to implement any command collection method for a device in ANTA.
The collect()
implementation needs to populate the output
attribute of the AntaTestCommand
object passed as argument.
If a failure occurs, the collect()
implementation is expected to catch the exception and implement proper logging, the output
attribute of the AntaTestCommand
object passed as argument would be None
in this case.
Parameters:
Name Type Description Default command
AntaTestCommand
the command to collect
required Source code in anta/device.py
@abstractmethod\nasync def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output.\n This abstract coroutine can be used to implement any command collection method\n for a device in ANTA.\n\n The `collect()` implementation needs to populate the `output` attribute\n of the `AntaTestCommand` object passed as argument.\n\n If a failure occurs, the `collect()` implementation is expected to catch the\n exception and implement proper logging, the `output` attribute of the\n `AntaTestCommand` object passed as argument would be `None` in this case.\n\n Args:\n command: the command to collect\n \"\"\"\n
"},{"location":"api/device/#anta.device.AntaDevice.collect_commands","title":"collect_commands(commands)
async
","text":"Collect multiple commands.
Parameters:
Name Type Description Default commands
List[AntaTestCommand]
the commands to collect
required Source code in anta/device.py
async def collect_commands(self, commands: List[AntaTestCommand]) -> None:\n\"\"\"\n Collect multiple commands.\n\n Args:\n commands: the commands to collect\n \"\"\"\n await asyncio.gather(*(self.collect(command=command) for command in commands))\n
"},{"location":"api/device/#anta.device.AntaDevice.copy","title":"copy(sources, destination, direction='from')
async
","text":"Copy files to and from the device, usually through SCP. It is not mandatory to implement this for a valid AntaDevice subclass.
Parameters:
Name Type Description Default sources
List[Path]
List of files to copy to or from the device.
required destination
Path
Local or remote destination when copying the files. Can be a folder.
required direction
Literal['to', 'from']
Defines if this coroutine copies files to or from the device.
'from'
Source code in anta/device.py
async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device, usually through SCP.\n It is not mandatory to implement this for a valid AntaDevice subclass.\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n raise NotImplementedError(f\"copy() method has not been implemented in {self.__class__.__name__} definition\")\n
"},{"location":"api/device/#anta.device.AntaDevice.refresh","title":"refresh()
async
abstractmethod
","text":"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
Source code in anta/device.py
@abstractmethod\nasync def refresh(self) -> None:\n\"\"\"\n Update attributes of an AntaDevice instance.\n\n This coroutine must update the following attributes of AntaDevice:\n - is_online: When the device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n
"},{"location":"api/device/#asynceosdevice-class","title":"AsyncEOSDevice class","text":" Bases: AntaDevice
Implementation of AntaDevice for EOS using aio-eapi.
Instance 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 hw_model: Hardware model of the device tags: List of tags for this device
Source code in anta/device.py
class AsyncEOSDevice(AntaDevice):\n\"\"\"\n Implementation of AntaDevice for EOS using aio-eapi.\n\n Instance attributes:\n name: Device name\n is_online: True if the device IP is reachable and a port can be open\n established: True if remote command execution succeeds\n hw_model: Hardware model of the device\n tags: List of tags for this device\n \"\"\"\n\n # Hardware model definition in show version\n HW_MODEL_KEY: str = \"modelName\"\n # Maximum concurrent SSH connections opened with EOS\n MAX_SSH_CONNECTIONS: int = 10\n\n def __init__( # pylint: disable=R0913\n self,\n host: str,\n username: str,\n password: str,\n name: Optional[str] = None,\n enable_password: Optional[str] = None,\n port: Optional[int] = None,\n ssh_port: Optional[int] = 22,\n tags: Optional[List[str]] = None,\n timeout: Optional[float] = None,\n insecure: bool = False,\n proto: Literal[\"http\", \"https\"] = \"https\",\n ) -> None:\n\"\"\"\n Constructor of AsyncEOSDevice\n\n Args:\n host: Device FQDN or IP\n username: Username to connect to eAPI and SSH\n password: Password to connect to eAPI and SSH\n name: Device name\n enable_password: Password used to gain privileged access on EOS\n proto: eAPI protocol. Value can be 'http' or 'https'\n port: eAPI port. Defaults to 80 is proto is 'http' or 443 if proto is 'https'.\n ssh_port: SSH port\n insecure: Disable SSH Host Key validation\n tags: List of tags for this device\n timeout: Timeout value in seconds for outgoing connections. Default to 10 secs.\n \"\"\"\n if name is None:\n name = f\"{host}:{port}\"\n super().__init__(name, tags)\n self._enable_password = enable_password\n self._session: Device = Device(host=host, port=port, username=username, password=password, proto=proto, timeout=timeout)\n ssh_params: Dict[str, Any] = {}\n if insecure:\n ssh_params.update({\"known_hosts\": None})\n self._ssh_opts: SSHClientConnectionOptions = SSHClientConnectionOptions(host=host, port=ssh_port, username=username, password=password, **ssh_params)\n\n def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield from super().__rich_repr__()\n yield \"host\", self._session.host\n yield \"eapi_port\", self._session.port\n yield \"username\", self._ssh_opts.username\n yield \"password\", self._ssh_opts.password\n yield \"enable_password\", self._enable_password\n yield \"insecure\", self._ssh_opts.known_hosts is None\n if __DEBUG__:\n yield \"_session\", vars(self._session)\n yield \"_ssh_opts\", vars(self._ssh_opts)\n\n def __eq__(self, other: object) -> bool:\n\"\"\"\n Two AsyncEOSDevice objects are equal if the hostname and the port are the same.\n This covers the use case of port forwarding when the host is localhost and the devices have different ports.\n \"\"\"\n if not isinstance(other, AsyncEOSDevice):\n return False\n return self._session.host == other._session.host and self._session.port == other._session.port\n\n async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output from EOS using aio-eapi.\n\n Supports outformat `json` and `text` as output structure.\n Gain privileged access using the `enable_password` attribute\n of the `AntaDevice` instance if populated.\n\n Args:\n command: the command to collect\n \"\"\"\n try:\n if self._enable_password is not None:\n enable_cmd = {\n \"cmd\": \"enable\",\n \"input\": str(self._enable_password),\n }\n else:\n enable_cmd = {\"cmd\": \"enable\"}\n response = await self._session.cli(\n commands=[enable_cmd, command.command],\n ofmt=command.ofmt,\n version=command.version,\n )\n # remove first dict related to enable command\n # only applicable to json output\n if command.ofmt in [\"json\", \"text\"]:\n # selecting only our command output\n response = response[1]\n command.output = response\n logger.debug(f\"{self.name}: {command}\")\n\n except EapiCommandError as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Command '{command.command}' failed on {self.name}: {e.errmsg}\")\n logger.debug(command)\n except (HTTPError, ConnectError) as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Cannot connect to device {self.name}: {exc_to_str(e)}\")\n except Exception as e: # pylint: disable=broad-exception-caught\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.critical(f\"Exception raised while collecting command '{command.command}' on device {self.name} - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n logger.debug(command)\n\n async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AsyncEOSDevice instance.\n\n This coroutine must update the following attributes of AsyncEOSDevice:\n - is_online: When a device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n logger.debug(f\"Refreshing device {self.name}\")\n self.is_online = await self._session.check_connection()\n if self.is_online:\n try:\n response = await self._session.cli(command=\"show version\")\n except EapiCommandError as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {e.errmsg}\")\n except (HTTPError, ConnectError) as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {type(e).__name__}{'' if not str(e) else f' ({str(e)})'}\")\n else:\n if self.HW_MODEL_KEY in response:\n self.hw_model = response[self.HW_MODEL_KEY]\n else:\n logger.warning(f\"Cannot get hardware information from device {self.name}: cannot parse 'show version'\")\n else:\n logger.warning(f\"Could not connect to device {self.name}: cannot open eAPI port\")\n self.established = bool(self.is_online and self.hw_model)\n\n async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device using asyncssh.scp().\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n async with asyncssh.connect(\n host=self._ssh_opts.host,\n port=self._ssh_opts.port,\n tunnel=self._ssh_opts.tunnel,\n family=self._ssh_opts.family,\n local_addr=self._ssh_opts.local_addr,\n options=self._ssh_opts,\n ) as conn:\n src: Union[List[Tuple[SSHClientConnection, Path]], List[Path]]\n dst: Union[Tuple[SSHClientConnection, Path], Path]\n if direction == \"from\":\n src = [(conn, file) for file in sources]\n dst = destination\n for file in sources:\n logger.info(f\"Copying '{file}' from device {self.name} to '{destination}' locally\")\n elif direction == \"to\":\n src = sources\n dst = (conn, destination)\n for file in sources:\n logger.info(f\"Copying '{file}' to device {self.name} to '{destination}' remotely\")\n else:\n logger.critical(f\"'direction' argument to copy() fonction is invalid: {direction}\")\n return\n await asyncssh.scp(src, dst)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__eq__","title":"__eq__(other)
","text":"Two AsyncEOSDevice objects are equal if the hostname and the port are the same. This covers the use case of port forwarding when the host is localhost and the devices have different ports.
Source code in anta/device.py
def __eq__(self, other: object) -> bool:\n\"\"\"\n Two AsyncEOSDevice objects are equal if the hostname and the port are the same.\n This covers the use case of port forwarding when the host is localhost and the devices have different ports.\n \"\"\"\n if not isinstance(other, AsyncEOSDevice):\n return False\n return self._session.host == other._session.host and self._session.port == other._session.port\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__init__","title":"__init__(host, username, password, name=None, enable_password=None, port=None, ssh_port=22, tags=None, timeout=None, insecure=False, proto='https')
","text":"Constructor of AsyncEOSDevice
Parameters:
Name Type Description Default host
str
Device FQDN or IP
required username
str
Username to connect to eAPI and SSH
required password
str
Password to connect to eAPI and SSH
required name
Optional[str]
Device name
None
enable_password
Optional[str]
Password used to gain privileged access on EOS
None
proto
Literal['http', 'https']
eAPI protocol. Value can be \u2018http\u2019 or \u2018https\u2019
'https'
port
Optional[int]
eAPI port. Defaults to 80 is proto is \u2018http\u2019 or 443 if proto is \u2018https\u2019.
None
ssh_port
Optional[int]
SSH port
22
insecure
bool
Disable SSH Host Key validation
False
tags
Optional[List[str]]
List of tags for this device
None
timeout
Optional[float]
Timeout value in seconds for outgoing connections. Default to 10 secs.
None
Source code in anta/device.py
def __init__( # pylint: disable=R0913\n self,\n host: str,\n username: str,\n password: str,\n name: Optional[str] = None,\n enable_password: Optional[str] = None,\n port: Optional[int] = None,\n ssh_port: Optional[int] = 22,\n tags: Optional[List[str]] = None,\n timeout: Optional[float] = None,\n insecure: bool = False,\n proto: Literal[\"http\", \"https\"] = \"https\",\n) -> None:\n\"\"\"\n Constructor of AsyncEOSDevice\n\n Args:\n host: Device FQDN or IP\n username: Username to connect to eAPI and SSH\n password: Password to connect to eAPI and SSH\n name: Device name\n enable_password: Password used to gain privileged access on EOS\n proto: eAPI protocol. Value can be 'http' or 'https'\n port: eAPI port. Defaults to 80 is proto is 'http' or 443 if proto is 'https'.\n ssh_port: SSH port\n insecure: Disable SSH Host Key validation\n tags: List of tags for this device\n timeout: Timeout value in seconds for outgoing connections. Default to 10 secs.\n \"\"\"\n if name is None:\n name = f\"{host}:{port}\"\n super().__init__(name, tags)\n self._enable_password = enable_password\n self._session: Device = Device(host=host, port=port, username=username, password=password, proto=proto, timeout=timeout)\n ssh_params: Dict[str, Any] = {}\n if insecure:\n ssh_params.update({\"known_hosts\": None})\n self._ssh_opts: SSHClientConnectionOptions = SSHClientConnectionOptions(host=host, port=ssh_port, username=username, password=password, **ssh_params)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__rich_repr__","title":"__rich_repr__()
","text":"Implements Rich Repr Protocol https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol
Source code in anta/device.py
def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield from super().__rich_repr__()\n yield \"host\", self._session.host\n yield \"eapi_port\", self._session.port\n yield \"username\", self._ssh_opts.username\n yield \"password\", self._ssh_opts.password\n yield \"enable_password\", self._enable_password\n yield \"insecure\", self._ssh_opts.known_hosts is None\n if __DEBUG__:\n yield \"_session\", vars(self._session)\n yield \"_ssh_opts\", vars(self._ssh_opts)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.collect","title":"collect(command)
async
","text":"Collect device command output from EOS using aio-eapi.
Supports outformat json
and text
as output structure. Gain privileged access using the enable_password
attribute of the AntaDevice
instance if populated.
Parameters:
Name Type Description Default command
AntaTestCommand
the command to collect
required Source code in anta/device.py
async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output from EOS using aio-eapi.\n\n Supports outformat `json` and `text` as output structure.\n Gain privileged access using the `enable_password` attribute\n of the `AntaDevice` instance if populated.\n\n Args:\n command: the command to collect\n \"\"\"\n try:\n if self._enable_password is not None:\n enable_cmd = {\n \"cmd\": \"enable\",\n \"input\": str(self._enable_password),\n }\n else:\n enable_cmd = {\"cmd\": \"enable\"}\n response = await self._session.cli(\n commands=[enable_cmd, command.command],\n ofmt=command.ofmt,\n version=command.version,\n )\n # remove first dict related to enable command\n # only applicable to json output\n if command.ofmt in [\"json\", \"text\"]:\n # selecting only our command output\n response = response[1]\n command.output = response\n logger.debug(f\"{self.name}: {command}\")\n\n except EapiCommandError as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Command '{command.command}' failed on {self.name}: {e.errmsg}\")\n logger.debug(command)\n except (HTTPError, ConnectError) as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Cannot connect to device {self.name}: {exc_to_str(e)}\")\n except Exception as e: # pylint: disable=broad-exception-caught\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.critical(f\"Exception raised while collecting command '{command.command}' on device {self.name} - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n logger.debug(command)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.copy","title":"copy(sources, destination, direction='from')
async
","text":"Copy files to and from the device using asyncssh.scp().
Parameters:
Name Type Description Default sources
List[Path]
List of files to copy to or from the device.
required destination
Path
Local or remote destination when copying the files. Can be a folder.
required direction
Literal['to', 'from']
Defines if this coroutine copies files to or from the device.
'from'
Source code in anta/device.py
async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device using asyncssh.scp().\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n async with asyncssh.connect(\n host=self._ssh_opts.host,\n port=self._ssh_opts.port,\n tunnel=self._ssh_opts.tunnel,\n family=self._ssh_opts.family,\n local_addr=self._ssh_opts.local_addr,\n options=self._ssh_opts,\n ) as conn:\n src: Union[List[Tuple[SSHClientConnection, Path]], List[Path]]\n dst: Union[Tuple[SSHClientConnection, Path], Path]\n if direction == \"from\":\n src = [(conn, file) for file in sources]\n dst = destination\n for file in sources:\n logger.info(f\"Copying '{file}' from device {self.name} to '{destination}' locally\")\n elif direction == \"to\":\n src = sources\n dst = (conn, destination)\n for file in sources:\n logger.info(f\"Copying '{file}' to device {self.name} to '{destination}' remotely\")\n else:\n logger.critical(f\"'direction' argument to copy() fonction is invalid: {direction}\")\n return\n await asyncssh.scp(src, dst)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.refresh","title":"refresh()
async
","text":"Update attributes of an AsyncEOSDevice instance.
This coroutine must update the following attributes of AsyncEOSDevice: - is_online: When a device IP is reachable and a port can be open - established: When a command execution succeeds - hw_model: The hardware model of the device
Source code in anta/device.py
async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AsyncEOSDevice instance.\n\n This coroutine must update the following attributes of AsyncEOSDevice:\n - is_online: When a device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n logger.debug(f\"Refreshing device {self.name}\")\n self.is_online = await self._session.check_connection()\n if self.is_online:\n try:\n response = await self._session.cli(command=\"show version\")\n except EapiCommandError as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {e.errmsg}\")\n except (HTTPError, ConnectError) as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {type(e).__name__}{'' if not str(e) else f' ({str(e)})'}\")\n else:\n if self.HW_MODEL_KEY in response:\n self.hw_model = response[self.HW_MODEL_KEY]\n else:\n logger.warning(f\"Cannot get hardware information from device {self.name}: cannot parse 'show version'\")\n else:\n logger.warning(f\"Could not connect to device {self.name}: cannot open eAPI port\")\n self.established = bool(self.is_online and self.hw_model)\n
"},{"location":"api/inventory/","title":"Inventory module","text":""},{"location":"api/inventory/#anta-inventory-module","title":"ANTA Inventory module","text":" Bases: dict
Inventory abstraction for ANTA framework.
Source code in anta/inventory/__init__.py
class AntaInventory(dict): # type: ignore\n # dict[str, AntaDevice] - not working in python 3.8 hence the ignore\n\"\"\"\n Inventory abstraction for ANTA framework.\n \"\"\"\n\n # Root key of inventory part of the inventory file\n INVENTORY_ROOT_KEY = \"anta_inventory\"\n # Supported Output format\n INVENTORY_OUTPUT_FORMAT = [\"native\", \"json\"]\n\n def __str__(self) -> str:\n\"\"\"Human readable string representing the inventory\"\"\"\n devs = {}\n for dev in self.values():\n if (dev_type := dev.__class__.__name__) not in devs:\n devs[dev_type] = 1\n else:\n devs[dev_type] += 1\n return f\"ANTA Inventory contains {' '.join([f'{n} devices ({t})' for t, n in devs.items()])}\"\n\n @staticmethod\n def parse(\n inventory_file: str, username: str, password: str, enable_password: Optional[str] = None, timeout: Optional[float] = None, insecure: bool = False\n ) -> AntaInventory:\n # pylint: disable=too-many-arguments\n\"\"\"\n Create an AntaInventory object from an inventory file.\n Instantiate AntaDevice objects using the AsyncEOSDevice subclass.\n\n Args:\n inventory_file (str): Path to inventory YAML file where user has described his inputs\n username (str): Username to use to connect to devices\n password (str): Password to use to connect to devices\n timeout (float, optional): timeout in seconds for every API call.\n\n Raises:\n InventoryRootKeyError: Root key of inventory is missing.\n InventoryIncorrectSchema: Inventory file is not following AntaInventory Schema.\n InventoryUnknownFormat: Output format is not supported.\n \"\"\"\n\n inventory = AntaInventory()\n kwargs: Dict[str, Any] = {\"username\": username, \"password\": password, \"enable_password\": enable_password, \"timeout\": timeout, \"insecure\": insecure}\n kwargs = {k: v for k, v in kwargs.items() if v is not None}\n\n with open(inventory_file, \"r\", encoding=\"UTF-8\") as file:\n data = safe_load(file)\n\n # Load data using Pydantic\n try:\n inventory_input = AntaInventoryInput(**data[AntaInventory.INVENTORY_ROOT_KEY])\n except KeyError as exc:\n logger.error(f\"Inventory root key is missing: {AntaInventory.INVENTORY_ROOT_KEY}\")\n raise InventoryRootKeyError(f\"Inventory root key ({AntaInventory.INVENTORY_ROOT_KEY}) is not defined in your inventory\") from exc\n except ValidationError as exc:\n logger.error(\"Inventory data are not compliant with inventory models\")\n raise InventoryIncorrectSchema(f\"Inventory is not following the schema: {str(exc)}\") from exc\n\n # Read data from input\n if inventory_input.hosts is not None:\n for host in inventory_input.hosts:\n device = AsyncEOSDevice(name=host.name, host=str(host.host), port=host.port, tags=host.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.networks is not None:\n for network in inventory_input.networks:\n for host_ip in IPNetwork(str(network.network)):\n device = AsyncEOSDevice(host=str(host_ip), tags=network.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.ranges is not None:\n for range_def in inventory_input.ranges:\n range_increment = IPAddress(str(range_def.start))\n range_stop = IPAddress(str(range_def.end))\n while range_increment <= range_stop:\n device = AsyncEOSDevice(host=str(range_increment), tags=range_def.tags, **kwargs)\n inventory.add_device(device)\n range_increment += 1\n\n return inventory\n\n ###########################################################################\n # Public methods\n ###########################################################################\n\n ###########################################################################\n # GET methods\n ###########################################################################\n\n def get_inventory(self, established_only: bool = False, tags: Optional[List[str]] = None) -> AntaInventory:\n\"\"\"\n Returns a filtered inventory.\n\n Args:\n established_only: Whether or not to include only established devices. Default False.\n tags: List of tags to filter devices.\n\n Returns:\n AntaInventory: An inventory with filtered AntaDevice objects.\n \"\"\"\n\n def _filter_devices(device: AntaDevice) -> bool:\n\"\"\"\n Helper function to select the devices based on the input tags\n and the requirement for an established connection.\n \"\"\"\n if tags is not None and all(tag not in tags for tag in device.tags):\n return False\n return bool(not established_only or device.established)\n\n devices: List[AntaDevice] = list(filter(_filter_devices, self.values()))\n result = AntaInventory()\n for device in devices:\n result.add_device(device)\n return result\n\n ###########################################################################\n # SET methods\n ###########################################################################\n\n def __setitem__(self, key: str, value: AntaDevice) -> None:\n if key != value.name:\n raise RuntimeError(f\"The key must be the device name for device '{value.name}'. Use AntaInventory.add_device().\")\n return super().__setitem__(key, value)\n\n def add_device(self, device: AntaDevice) -> None:\n\"\"\"Add a device to final inventory.\n\n Args:\n device: Device object to be added\n \"\"\"\n self[device.name] = device\n\n ###########################################################################\n # MISC methods\n ###########################################################################\n\n async def connect_inventory(self) -> None:\n\"\"\"Run `refresh()` coroutines for all AntaDevice objects in this inventory.\"\"\"\n logger.debug(\"Refreshing devices...\")\n results = await asyncio.gather(\n *(device.refresh() for device in self.values()),\n return_exceptions=True,\n )\n for r in results:\n if isinstance(r, Exception):\n logger.error(f\"Error when refreshing inventory: {r.__class__.__name__}{'' if not str(r) else f' ({str(r)})'}\")\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.__str__","title":"__str__()
","text":"Human readable string representing the inventory
Source code in anta/inventory/__init__.py
def __str__(self) -> str:\n\"\"\"Human readable string representing the inventory\"\"\"\n devs = {}\n for dev in self.values():\n if (dev_type := dev.__class__.__name__) not in devs:\n devs[dev_type] = 1\n else:\n devs[dev_type] += 1\n return f\"ANTA Inventory contains {' '.join([f'{n} devices ({t})' for t, n in devs.items()])}\"\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.add_device","title":"add_device(device)
","text":"Add a device to final inventory.
Parameters:
Name Type Description Default device
AntaDevice
Device object to be added
required Source code in anta/inventory/__init__.py
def add_device(self, device: AntaDevice) -> None:\n\"\"\"Add a device to final inventory.\n\n Args:\n device: Device object to be added\n \"\"\"\n self[device.name] = device\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.connect_inventory","title":"connect_inventory()
async
","text":"Run refresh()
coroutines for all AntaDevice objects in this inventory.
Source code in anta/inventory/__init__.py
async def connect_inventory(self) -> None:\n\"\"\"Run `refresh()` coroutines for all AntaDevice objects in this inventory.\"\"\"\n logger.debug(\"Refreshing devices...\")\n results = await asyncio.gather(\n *(device.refresh() for device in self.values()),\n return_exceptions=True,\n )\n for r in results:\n if isinstance(r, Exception):\n logger.error(f\"Error when refreshing inventory: {r.__class__.__name__}{'' if not str(r) else f' ({str(r)})'}\")\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.get_inventory","title":"get_inventory(established_only=False, tags=None)
","text":"Returns a filtered inventory.
Parameters:
Name Type Description Default established_only
bool
Whether or not to include only established devices. Default False.
False
tags
Optional[List[str]]
List of tags to filter devices.
None
Returns:
Name Type Description AntaInventory
AntaInventory
An inventory with filtered AntaDevice objects.
Source code in anta/inventory/__init__.py
def get_inventory(self, established_only: bool = False, tags: Optional[List[str]] = None) -> AntaInventory:\n\"\"\"\n Returns a filtered inventory.\n\n Args:\n established_only: Whether or not to include only established devices. Default False.\n tags: List of tags to filter devices.\n\n Returns:\n AntaInventory: An inventory with filtered AntaDevice objects.\n \"\"\"\n\n def _filter_devices(device: AntaDevice) -> bool:\n\"\"\"\n Helper function to select the devices based on the input tags\n and the requirement for an established connection.\n \"\"\"\n if tags is not None and all(tag not in tags for tag in device.tags):\n return False\n return bool(not established_only or device.established)\n\n devices: List[AntaDevice] = list(filter(_filter_devices, self.values()))\n result = AntaInventory()\n for device in devices:\n result.add_device(device)\n return result\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.parse","title":"parse(inventory_file, username, password, enable_password=None, timeout=None, insecure=False)
staticmethod
","text":"Create an AntaInventory object from an inventory file. Instantiate AntaDevice objects using the AsyncEOSDevice subclass.
Parameters:
Name Type Description Default inventory_file
str
Path to inventory YAML file where user has described his inputs
required username
str
Username to use to connect to devices
required password
str
Password to use to connect to devices
required timeout
float
timeout in seconds for every API call.
None
Raises:
Type Description InventoryRootKeyError
Root key of inventory is missing.
InventoryIncorrectSchema
Inventory file is not following AntaInventory Schema.
InventoryUnknownFormat
Output format is not supported.
Source code in anta/inventory/__init__.py
@staticmethod\ndef parse(\n inventory_file: str, username: str, password: str, enable_password: Optional[str] = None, timeout: Optional[float] = None, insecure: bool = False\n) -> AntaInventory:\n # pylint: disable=too-many-arguments\n\"\"\"\n Create an AntaInventory object from an inventory file.\n Instantiate AntaDevice objects using the AsyncEOSDevice subclass.\n\n Args:\n inventory_file (str): Path to inventory YAML file where user has described his inputs\n username (str): Username to use to connect to devices\n password (str): Password to use to connect to devices\n timeout (float, optional): timeout in seconds for every API call.\n\n Raises:\n InventoryRootKeyError: Root key of inventory is missing.\n InventoryIncorrectSchema: Inventory file is not following AntaInventory Schema.\n InventoryUnknownFormat: Output format is not supported.\n \"\"\"\n\n inventory = AntaInventory()\n kwargs: Dict[str, Any] = {\"username\": username, \"password\": password, \"enable_password\": enable_password, \"timeout\": timeout, \"insecure\": insecure}\n kwargs = {k: v for k, v in kwargs.items() if v is not None}\n\n with open(inventory_file, \"r\", encoding=\"UTF-8\") as file:\n data = safe_load(file)\n\n # Load data using Pydantic\n try:\n inventory_input = AntaInventoryInput(**data[AntaInventory.INVENTORY_ROOT_KEY])\n except KeyError as exc:\n logger.error(f\"Inventory root key is missing: {AntaInventory.INVENTORY_ROOT_KEY}\")\n raise InventoryRootKeyError(f\"Inventory root key ({AntaInventory.INVENTORY_ROOT_KEY}) is not defined in your inventory\") from exc\n except ValidationError as exc:\n logger.error(\"Inventory data are not compliant with inventory models\")\n raise InventoryIncorrectSchema(f\"Inventory is not following the schema: {str(exc)}\") from exc\n\n # Read data from input\n if inventory_input.hosts is not None:\n for host in inventory_input.hosts:\n device = AsyncEOSDevice(name=host.name, host=str(host.host), port=host.port, tags=host.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.networks is not None:\n for network in inventory_input.networks:\n for host_ip in IPNetwork(str(network.network)):\n device = AsyncEOSDevice(host=str(host_ip), tags=network.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.ranges is not None:\n for range_def in inventory_input.ranges:\n range_increment = IPAddress(str(range_def.start))\n range_stop = IPAddress(str(range_def.end))\n while range_increment <= range_stop:\n device = AsyncEOSDevice(host=str(range_increment), tags=range_def.tags, **kwargs)\n inventory.add_device(device)\n range_increment += 1\n\n return inventory\n
"},{"location":"api/inventory/#exceptions","title":"Exceptions","text":"Manage Exception in Inventory module.
"},{"location":"api/inventory/#anta.inventory.exceptions.InventoryIncorrectSchema","title":"InventoryIncorrectSchema
","text":" Bases: Exception
Error when user data does not follow ANTA schema.
Source code in anta/inventory/exceptions.py
class InventoryIncorrectSchema(Exception):\n\"\"\"Error when user data does not follow ANTA schema.\"\"\"\n
"},{"location":"api/inventory/#anta.inventory.exceptions.InventoryRootKeyError","title":"InventoryRootKeyError
","text":" Bases: Exception
Error raised when inventory root key is not found.
Source code in anta/inventory/exceptions.py
class InventoryRootKeyError(Exception):\n\"\"\"Error raised when inventory root key is not found.\"\"\"\n
"},{"location":"api/inventory.models.input/","title":"Inventory models","text":""},{"location":"api/inventory.models.input/#data-models-for-antainventory","title":"Data models for anta.inventory","text":" Bases: BaseModel
User\u2019s inventory model.
Attributes:
Name Type Description networks
List[AntaInventoryNetwork], Optional
List of AntaInventoryNetwork objects for networks.
hosts
List[AntaInventoryHost], Optional
List of AntaInventoryHost objects for hosts.
range
List[AntaInventoryRange], Optional
List of AntaInventoryRange objects for ranges.
Source code in anta/inventory/models.py
class AntaInventoryInput(BaseModel):\n\"\"\"\n User's inventory model.\n\n Attributes:\n networks (List[AntaInventoryNetwork],Optional): List of AntaInventoryNetwork objects for networks.\n hosts (List[AntaInventoryHost],Optional): List of AntaInventoryHost objects for hosts.\n range (List[AntaInventoryRange],Optional): List of AntaInventoryRange objects for ranges.\n \"\"\"\n\n networks: Optional[List[AntaInventoryNetwork]]\n hosts: Optional[List[AntaInventoryHost]]\n ranges: Optional[List[AntaInventoryRange]]\n
"},{"location":"api/inventory.models.input/#user-inventory-components","title":"User inventory components","text":" Bases: BaseModel
Host definition for user\u2019s inventory.
Attributes:
Name Type Description host
IPvAnyAddress
IPv4 or IPv6 address of the device
port
int
(Optional) eAPI port to use Default is 443.
name
str
(Optional) Name to display during tests report. Default is hostname:port
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryHost(BaseModel):\n\"\"\"\n Host definition for user's inventory.\n\n Attributes:\n host (IPvAnyAddress): IPv4 or IPv6 address of the device\n port (int): (Optional) eAPI port to use Default is 443.\n name (str): (Optional) Name to display during tests report. Default is hostname:port\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n name: Optional[str]\n host: Union[constr(regex=RFC_1123_REGEX), IPvAnyAddress] # type: ignore\n port: Optional[conint(gt=1, lt=65535)] # type: ignore\n tags: Optional[List[str]]\n
Bases: BaseModel
Network definition for user\u2019s inventory.
Attributes:
Name Type Description network
IPvAnyNetwork
Subnet to use for testing.
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryNetwork(BaseModel):\n\"\"\"\n Network definition for user's inventory.\n\n Attributes:\n network (IPvAnyNetwork): Subnet to use for testing.\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n network: IPvAnyNetwork\n tags: Optional[List[str]]\n
Bases: BaseModel
IP Range definition for user\u2019s inventory.
Attributes:
Name Type Description start
IPvAnyAddress
IPv4 or IPv6 address for the begining of the range.
stop
IPvAnyAddress
IPv4 or IPv6 address for the end of the range.
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryRange(BaseModel):\n\"\"\"\n IP Range definition for user's inventory.\n\n Attributes:\n start (IPvAnyAddress): IPv4 or IPv6 address for the begining of the range.\n stop (IPvAnyAddress): IPv4 or IPv6 address for the end of the range.\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n start: IPvAnyAddress\n end: IPvAnyAddress\n tags: Optional[List[str]]\n
"},{"location":"api/models/","title":"Test models","text":""},{"location":"api/models/#antatest-definition","title":"AntaTest definition","text":" Bases: ABC
Abstract class defining a test for Anta
The goal of this class is to handle the heavy lifting and make writing a test as simple as possible.
TODO - complete doctstring with example
Source code in anta/models.py
class AntaTest(ABC):\n\"\"\"Abstract class defining a test for Anta\n\n The goal of this class is to handle the heavy lifting and make\n writing a test as simple as possible.\n\n TODO - complete doctstring with example\n \"\"\"\n\n # Mandatory class attributes\n # TODO - find a way to tell mypy these are mandatory for child classes - maybe Protocol\n name: ClassVar[str]\n description: ClassVar[str]\n categories: ClassVar[list[str]]\n # Or any child type\n commands: ClassVar[list[AntaTestCommand]]\n # TODO - today we support only one template per Test\n template: ClassVar[AntaTestTemplate]\n\n # Optional class attributes\n test_filters: ClassVar[list[AntaTestFilter]]\n\n def __init__(\n self,\n device: AntaDevice,\n template_params: list[dict[str, Any]] | None = None,\n # TODO document very well the order of eos_data\n eos_data: list[dict[Any, Any] | str] | None = None,\n labels: list[str] | None = None,\n ):\n\"\"\"Class constructor\"\"\"\n self.device = device\n self.result = TestResult(name=device.name, test=self.name, test_category=self.categories, test_description=self.description)\n self.labels = labels or []\n\n # TODO - check optimization for deepcopy\n # Generating instance_commands from list of commands and template\n self.instance_commands = []\n if hasattr(self.__class__, \"commands\") and (cmds := self.__class__.commands) is not None:\n self.instance_commands.extend(deepcopy(cmds))\n if hasattr(self.__class__, \"template\") and (tpl := self.__class__.template) is not None:\n if template_params is None:\n self.result.is_error(\"Command has template but no params were given\")\n return\n self.template_params = template_params\n self.instance_commands.extend(\n AntaTestCommand(\n command=tpl.template.format(**param),\n ofmt=tpl.ofmt,\n version=tpl.version,\n template=tpl,\n template_params=param,\n )\n for param in template_params\n )\n\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n def save_commands_data(self, eos_data: list[dict[Any, Any] | str]) -> None:\n\"\"\"Called at init or at test execution time\"\"\"\n if len(eos_data) != len(self.instance_commands):\n self.result.is_error(\"Test initialization error: Trying to save more data than there are commands for the test\")\n return\n for index, data in enumerate(eos_data or []):\n self.instance_commands[index].output = data\n\n def all_data_collected(self) -> bool:\n\"\"\"returns True if output is populated for every command\"\"\"\n return all(command.output is not None for command in self.instance_commands)\n\n def __init_subclass__(cls) -> None:\n\"\"\"\n Verify that the mandatory class attributes are defined\n \"\"\"\n mandatory_attributes = [\"name\", \"description\", \"categories\"]\n for attr in mandatory_attributes:\n if not hasattr(cls, attr):\n raise NotImplementedError(f\"Class {cls} is missing required class attribute {attr}\")\n # Check that either commands or template exist\n if not (hasattr(cls, \"commands\") or hasattr(cls, \"template\")):\n raise NotImplementedError(f\"Class {cls} is missing required either commands or template attribute\")\n\n async def collect(self) -> None:\n\"\"\"\n Method used to collect outputs of all commands of this test class from the device of this test instance.\n \"\"\"\n logger.debug(f\"Test {self.name} on device {self.device.name}: running command outputs collection\")\n try:\n await self.device.collect_commands(self.instance_commands)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised while collecting commands for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n\n @staticmethod\n def anta_test(function: F) -> Callable[..., Coroutine[Any, Any, TestResult]]:\n\"\"\"\n Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing\n \"\"\"\n\n @wraps(function)\n async def wrapper(\n self: AntaTest,\n eos_data: list[dict[Any, Any] | str] | None = None,\n **kwargs: dict[str, Any],\n ) -> TestResult:\n\"\"\"\n Wraps the test function and implement (in this order):\n 1. Instantiate the command outputs if `eos_data` is provided\n 2. Collect missing command outputs from the device\n 3. Run the test function\n 4. Catches and set the result if the test function raises an exception\n\n Returns:\n TestResult: self.result, populated with the correct exit status\n \"\"\"\n if self.result.result != \"unset\":\n return self.result\n\n # TODO maybe_skip decorators\n\n # Data\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n # No test data is present, try to collect\n if not self.all_data_collected():\n await self.collect()\n if self.result.result != \"unset\":\n return self.result\n\n try:\n if not self.all_data_collected():\n raise ValueError(\"Some command output is missing\")\n logger.debug(f\"Test {self.name} on device {self.device.name}: running test\")\n function(self, **kwargs)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n return self.result\n\n return wrapper\n\n @abstractmethod\n def test(self) -> Coroutine[Any, Any, TestResult]:\n\"\"\"\n This abstract method is the core of the test.\n It MUST set the correct status of self.result with the appropriate error messages\n\n it must be implemented as follow\n\n @AntaTest.anta_test\n def test(self) -> None:\n '''\n assert code\n '''\n \"\"\"\n
"},{"location":"api/models/#anta.models.AntaTest.__init__","title":"__init__(device, template_params=None, eos_data=None, labels=None)
","text":"Class constructor
Source code in anta/models.py
def __init__(\n self,\n device: AntaDevice,\n template_params: list[dict[str, Any]] | None = None,\n # TODO document very well the order of eos_data\n eos_data: list[dict[Any, Any] | str] | None = None,\n labels: list[str] | None = None,\n):\n\"\"\"Class constructor\"\"\"\n self.device = device\n self.result = TestResult(name=device.name, test=self.name, test_category=self.categories, test_description=self.description)\n self.labels = labels or []\n\n # TODO - check optimization for deepcopy\n # Generating instance_commands from list of commands and template\n self.instance_commands = []\n if hasattr(self.__class__, \"commands\") and (cmds := self.__class__.commands) is not None:\n self.instance_commands.extend(deepcopy(cmds))\n if hasattr(self.__class__, \"template\") and (tpl := self.__class__.template) is not None:\n if template_params is None:\n self.result.is_error(\"Command has template but no params were given\")\n return\n self.template_params = template_params\n self.instance_commands.extend(\n AntaTestCommand(\n command=tpl.template.format(**param),\n ofmt=tpl.ofmt,\n version=tpl.version,\n template=tpl,\n template_params=param,\n )\n for param in template_params\n )\n\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n
"},{"location":"api/models/#anta.models.AntaTest.__init_subclass__","title":"__init_subclass__()
","text":"Verify that the mandatory class attributes are defined
Source code in anta/models.py
def __init_subclass__(cls) -> None:\n\"\"\"\n Verify that the mandatory class attributes are defined\n \"\"\"\n mandatory_attributes = [\"name\", \"description\", \"categories\"]\n for attr in mandatory_attributes:\n if not hasattr(cls, attr):\n raise NotImplementedError(f\"Class {cls} is missing required class attribute {attr}\")\n # Check that either commands or template exist\n if not (hasattr(cls, \"commands\") or hasattr(cls, \"template\")):\n raise NotImplementedError(f\"Class {cls} is missing required either commands or template attribute\")\n
"},{"location":"api/models/#anta.models.AntaTest.all_data_collected","title":"all_data_collected()
","text":"returns True if output is populated for every command
Source code in anta/models.py
def all_data_collected(self) -> bool:\n\"\"\"returns True if output is populated for every command\"\"\"\n return all(command.output is not None for command in self.instance_commands)\n
"},{"location":"api/models/#anta.models.AntaTest.anta_test","title":"anta_test(function)
staticmethod
","text":"Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing
Source code in anta/models.py
@staticmethod\ndef anta_test(function: F) -> Callable[..., Coroutine[Any, Any, TestResult]]:\n\"\"\"\n Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing\n \"\"\"\n\n @wraps(function)\n async def wrapper(\n self: AntaTest,\n eos_data: list[dict[Any, Any] | str] | None = None,\n **kwargs: dict[str, Any],\n ) -> TestResult:\n\"\"\"\n Wraps the test function and implement (in this order):\n 1. Instantiate the command outputs if `eos_data` is provided\n 2. Collect missing command outputs from the device\n 3. Run the test function\n 4. Catches and set the result if the test function raises an exception\n\n Returns:\n TestResult: self.result, populated with the correct exit status\n \"\"\"\n if self.result.result != \"unset\":\n return self.result\n\n # TODO maybe_skip decorators\n\n # Data\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n # No test data is present, try to collect\n if not self.all_data_collected():\n await self.collect()\n if self.result.result != \"unset\":\n return self.result\n\n try:\n if not self.all_data_collected():\n raise ValueError(\"Some command output is missing\")\n logger.debug(f\"Test {self.name} on device {self.device.name}: running test\")\n function(self, **kwargs)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n return self.result\n\n return wrapper\n
"},{"location":"api/models/#anta.models.AntaTest.collect","title":"collect()
async
","text":"Method used to collect outputs of all commands of this test class from the device of this test instance.
Source code in anta/models.py
async def collect(self) -> None:\n\"\"\"\n Method used to collect outputs of all commands of this test class from the device of this test instance.\n \"\"\"\n logger.debug(f\"Test {self.name} on device {self.device.name}: running command outputs collection\")\n try:\n await self.device.collect_commands(self.instance_commands)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised while collecting commands for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n
"},{"location":"api/models/#anta.models.AntaTest.save_commands_data","title":"save_commands_data(eos_data)
","text":"Called at init or at test execution time
Source code in anta/models.py
def save_commands_data(self, eos_data: list[dict[Any, Any] | str]) -> None:\n\"\"\"Called at init or at test execution time\"\"\"\n if len(eos_data) != len(self.instance_commands):\n self.result.is_error(\"Test initialization error: Trying to save more data than there are commands for the test\")\n return\n for index, data in enumerate(eos_data or []):\n self.instance_commands[index].output = data\n
"},{"location":"api/models/#anta.models.AntaTest.test","title":"test()
abstractmethod
","text":"This abstract method is the core of the test. It MUST set the correct status of self.result with the appropriate error messages
it must be implemented as follow
@AntaTest.anta_test def test(self) -> None: \u2018\u2019\u2018 assert code \u2018\u2019\u2018
Source code in anta/models.py
@abstractmethod\ndef test(self) -> Coroutine[Any, Any, TestResult]:\n\"\"\"\n This abstract method is the core of the test.\n It MUST set the correct status of self.result with the appropriate error messages\n\n it must be implemented as follow\n\n @AntaTest.anta_test\n def test(self) -> None:\n '''\n assert code\n '''\n \"\"\"\n
"},{"location":"api/models/#antatestcommand-definition","title":"AntaTestCommand definition","text":" Bases: BaseModel
Class to define a test command with its API version
Attributes:
Name Type Description command(str)
Test command
version
Union[int, Literal['latest']]
eAPI version - valid values are integers or the string \u201clatest\u201d - default is \u201clatest\u201d
ofmt(str)
Union[int, Literal['latest']]
eAPI output - json or text - default is json
output
Optional[Union[Dict[str, Any], str]]
collected output either dict for json or str for text
template
Optional(AntaTestTemplate
Template used to generate the command
template_params
Optional(dict
params used in the template to generate the command
Source code in anta/models.py
class AntaTestCommand(BaseModel):\n\"\"\"Class to define a test command with its API version\n\n Attributes:\n command(str): Test command\n version: eAPI version - valid values are integers or the string \"latest\" - default is \"latest\"\n ofmt(str): eAPI output - json or text - default is json\n output: collected output either dict for json or str for text\n template Optional(AntaTestTemplate): Template used to generate the command\n template_params Optional(dict): params used in the template to generate the command\n \"\"\"\n\n command: str\n version: Union[int, Literal[\"latest\"]] = \"latest\"\n ofmt: str = \"json\"\n output: Optional[Union[Dict[str, Any], str]]\n template: Optional[AntaTestTemplate] = None\n template_params: Optional[Dict[str, str]]\n\n @validator(\"template_params\")\n def prevent_none_when_template_is_set(cls: Type[AntaTestTemplate], value: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: # type: ignore\n\"\"\"\n Raises if template is set but no params are given\n \"\"\"\n if hasattr(cls, \"template\") and cls.template is not None:\n assert value is not None\n\n return value\n
"},{"location":"api/models/#anta.models.AntaTestCommand.prevent_none_when_template_is_set","title":"prevent_none_when_template_is_set(value)
","text":"Raises if template is set but no params are given
Source code in anta/models.py
@validator(\"template_params\")\ndef prevent_none_when_template_is_set(cls: Type[AntaTestTemplate], value: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: # type: ignore\n\"\"\"\n Raises if template is set but no params are given\n \"\"\"\n if hasattr(cls, \"template\") and cls.template is not None:\n assert value is not None\n\n return value\n
"},{"location":"api/models/#antatesttemplate-definition","title":"AntaTestTemplate definition","text":" Bases: BaseModel
Class to define a test command with its API version
Attributes:
Name Type Description command(str)
Test command
version
Union[int, Literal['latest']]
eAPI version - valid values are integers or the string \u201clatest\u201d - default is \u201clatest\u201d
ofmt(str)
Union[int, Literal['latest']]
eAPI output - json or text - default is json
output
Union[int, Literal['latest']]
collected output either dict for json or str for text
Source code in anta/models.py
class AntaTestTemplate(BaseModel):\n\"\"\"Class to define a test command with its API version\n\n Attributes:\n command(str): Test command\n version: eAPI version - valid values are integers or the string \"latest\" - default is \"latest\"\n ofmt(str): eAPI output - json or text - default is json\n output: collected output either dict for json or str for text\n \"\"\"\n\n template: str\n version: Union[int, Literal[\"latest\"]] = \"latest\"\n ofmt: str = \"json\"\n
"},{"location":"api/report_manager/","title":"Report Manager module","text":""},{"location":"api/report_manager/#anta-reportmanager-module","title":"ANTA ReportManager module","text":"TableReport Generate a Table based on TestResult.
Source code in anta/reporter/__init__.py
class ReportTable:\n\"\"\"TableReport Generate a Table based on TestResult.\"\"\"\n\n def __init__(self) -> None:\n\"\"\"\n __init__ Class constructor\n \"\"\"\n self.colors = []\n self.colors.append(ColorManager(level=\"success\", color=RICH_COLOR_PALETTE.SUCCESS))\n self.colors.append(ColorManager(level=\"failure\", color=RICH_COLOR_PALETTE.FAILURE))\n self.colors.append(ColorManager(level=\"error\", color=RICH_COLOR_PALETTE.ERROR))\n self.colors.append(ColorManager(level=\"skipped\", color=RICH_COLOR_PALETTE.SKIPPED))\n\n def _split_list_to_txt_list(self, usr_list: List[str], delimiter: Optional[str] = None) -> str:\n\"\"\"\n Split list to multi-lines string\n\n Args:\n usr_list (List[str]): List of string to concatenate\n delimiter (str, optional): A delimiter to use to start string. Defaults to None.\n\n Returns:\n str: Multi-lines string\n \"\"\"\n if delimiter is not None:\n return \"\\n\".join(f\"{delimiter} {line}\" for line in usr_list)\n return \"\\n\".join(f\"{line}\" for line in usr_list)\n\n def _build_headers(self, headers: List[str], table: Table) -> Table:\n\"\"\"\n Create headers for a table.\n\n First key is considered as header and is colored using RICH_COLOR_PALETTE.HEADER\n\n Args:\n headers (List[str]): List of headers\n table (Table): A rich Table instance\n\n Returns:\n Table: A rich Table instance with headers\n \"\"\"\n for idx, header in enumerate(headers):\n if idx == 0:\n table.add_column(header, justify=\"left\", style=RICH_COLOR_PALETTE.HEADER, no_wrap=True)\n else:\n table.add_column(header, justify=\"left\")\n return table\n\n def _color_result(self, status: str, output_type: str = \"Text\") -> Any:\n\"\"\"\n Helper to implement color based on test status.\n\n It gives output for either standard str or Text() colorized with Style()\n\n Args:\n status (str): status value to colorized\n output_type (str, optional): Which format to output code. Defaults to 'Text'.\n\n Returns:\n Any: Can be either str or Text with Style\n \"\"\"\n if len([result for result in self.colors if str(result.level).upper() == status.upper()]) == 1:\n code: ColorManager = [result for result in self.colors if str(result.level).upper() == status.upper()][0]\n return code.style_rich() if output_type == \"Text\" else code.string()\n return None\n\n def report_all(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n testcase: Optional[str] = None,\n title: str = \"All tests results\",\n ) -> Table:\n\"\"\"\n Create a table report with all tests for one or all devices.\n\n Create table with full output: Host / Test / Status / Message\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\"Device IP\", \"Test Name\", \"Test Status\", \"Message(s)\"]\n table = self._build_headers(headers=headers, table=table)\n\n for result in result_manager.get_results(output_format=\"list\"):\n # pylint: disable=R0916\n if (host is None and testcase is None) or (host is not None and str(result.name) == host) or (testcase is not None and testcase == str(result.test)):\n state = self._color_result(status=str(result.result), output_type=\"str\")\n message = self._split_list_to_txt_list(result.messages) if len(result.messages) > 0 else \"\"\n table.add_row(str(result.name), result.test, state, message)\n return table\n\n def report_summary_tests(\n self,\n result_manager: ResultManager,\n testcase: Optional[str] = None,\n title: str = \"Summary per test case\",\n ) -> Table:\n\"\"\"\n Create a table report with result agregated per test.\n\n Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n # sourcery skip: class-extract-method\n table = Table(title=title)\n headers = [\n \"Test Case\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed or error nodes\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for testcase_read in result_manager.get_testcases():\n if testcase is None or str(testcase_read) == testcase:\n results = result_manager.get_result_by_test(testcase_read)\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.name) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n testcase_read,\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n\n def report_summary_hosts(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n title: str = \"Summary per host\",\n ) -> Table:\n\"\"\"\n Create a table report with result agregated per host.\n\n Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\n \"Host IP\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed ortest case\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for host_read in result_manager.get_hosts():\n if host is None or str(host_read) == host:\n results = result_manager.get_result_by_host(host_read)\n logger.debug(\"data to use for computation\")\n logger.debug(f\"{host}: {results}\")\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.test) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n str(host_read),\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.__init__","title":"__init__()
","text":"init Class constructor
Source code in anta/reporter/__init__.py
def __init__(self) -> None:\n\"\"\"\n __init__ Class constructor\n \"\"\"\n self.colors = []\n self.colors.append(ColorManager(level=\"success\", color=RICH_COLOR_PALETTE.SUCCESS))\n self.colors.append(ColorManager(level=\"failure\", color=RICH_COLOR_PALETTE.FAILURE))\n self.colors.append(ColorManager(level=\"error\", color=RICH_COLOR_PALETTE.ERROR))\n self.colors.append(ColorManager(level=\"skipped\", color=RICH_COLOR_PALETTE.SKIPPED))\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_all","title":"report_all(result_manager, host=None, testcase=None, title='All tests results')
","text":"Create a table report with all tests for one or all devices.
Create table with full output: Host / Test / Status / Message
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required host
str
IP Address of a host to search for. Defaults to None.
None
testcase
str
A test name to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'All tests results'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_all(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n testcase: Optional[str] = None,\n title: str = \"All tests results\",\n) -> Table:\n\"\"\"\n Create a table report with all tests for one or all devices.\n\n Create table with full output: Host / Test / Status / Message\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\"Device IP\", \"Test Name\", \"Test Status\", \"Message(s)\"]\n table = self._build_headers(headers=headers, table=table)\n\n for result in result_manager.get_results(output_format=\"list\"):\n # pylint: disable=R0916\n if (host is None and testcase is None) or (host is not None and str(result.name) == host) or (testcase is not None and testcase == str(result.test)):\n state = self._color_result(status=str(result.result), output_type=\"str\")\n message = self._split_list_to_txt_list(result.messages) if len(result.messages) > 0 else \"\"\n table.add_row(str(result.name), result.test, state, message)\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_summary_hosts","title":"report_summary_hosts(result_manager, host=None, title='Summary per host')
","text":"Create a table report with result agregated per host.
Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required host
str
IP Address of a host to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'Summary per host'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_summary_hosts(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n title: str = \"Summary per host\",\n) -> Table:\n\"\"\"\n Create a table report with result agregated per host.\n\n Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\n \"Host IP\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed ortest case\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for host_read in result_manager.get_hosts():\n if host is None or str(host_read) == host:\n results = result_manager.get_result_by_host(host_read)\n logger.debug(\"data to use for computation\")\n logger.debug(f\"{host}: {results}\")\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.test) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n str(host_read),\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_summary_tests","title":"report_summary_tests(result_manager, testcase=None, title='Summary per test case')
","text":"Create a table report with result agregated per test.
Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required testcase
str
A test name to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'Summary per test case'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_summary_tests(\n self,\n result_manager: ResultManager,\n testcase: Optional[str] = None,\n title: str = \"Summary per test case\",\n) -> Table:\n\"\"\"\n Create a table report with result agregated per test.\n\n Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n # sourcery skip: class-extract-method\n table = Table(title=title)\n headers = [\n \"Test Case\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed or error nodes\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for testcase_read in result_manager.get_testcases():\n if testcase is None or str(testcase_read) == testcase:\n results = result_manager.get_result_by_test(testcase_read)\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.name) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n testcase_read,\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager_models/","title":"Report Manager models","text":""},{"location":"api/report_manager_models/#colormanager-entry","title":"ColorManager Entry","text":" Bases: BaseModel
Color management for status report.
Attributes:
Name Type Description level
str
Test result value.
color
str
Associated color.
Source code in anta/reporter/models.py
class ColorManager(BaseModel):\n\"\"\"Color management for status report.\n\n Attributes:\n level (str): Test result value.\n color (str): Associated color.\n \"\"\"\n\n level: str\n color: str\n\n @validator(\"level\", allow_reuse=True)\n def name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined level\n\n Raises:\n ValueError: If level is unsupported\n\n Returns:\n str: level value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n\n def style_rich(self) -> Text:\n\"\"\"\n Build a rich Text syntax with color\n\n Returns:\n Text: object with level string and its associated color.\n \"\"\"\n return Text(self.level, style=self.color)\n\n def string(self) -> str:\n\"\"\"\n Build an str with color code\n\n Returns:\n str: String with level and its associated color\n \"\"\"\n return f\"[{self.color}]{self.level}\"\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.name_must_be_in","title":"name_must_be_in(v)
","text":"Status validator
Validate status is a supported one
Parameters:
Name Type Description Default v
str
User defined level
required Raises:
Type Description ValueError
If level is unsupported
Returns:
Name Type Description str
str
level value
Source code in anta/reporter/models.py
@validator(\"level\", allow_reuse=True)\ndef name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined level\n\n Raises:\n ValueError: If level is unsupported\n\n Returns:\n str: level value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.string","title":"string()
","text":"Build an str with color code
Returns:
Name Type Description str
str
String with level and its associated color
Source code in anta/reporter/models.py
def string(self) -> str:\n\"\"\"\n Build an str with color code\n\n Returns:\n str: String with level and its associated color\n \"\"\"\n return f\"[{self.color}]{self.level}\"\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.style_rich","title":"style_rich()
","text":"Build a rich Text syntax with color
Returns:
Name Type Description Text
Text
object with level string and its associated color.
Source code in anta/reporter/models.py
def style_rich(self) -> Text:\n\"\"\"\n Build a rich Text syntax with color\n\n Returns:\n Text: object with level string and its associated color.\n \"\"\"\n return Text(self.level, style=self.color)\n
"},{"location":"api/result_manager/","title":"Result Manager module","text":""},{"location":"api/result_manager/#anta-resultmanager-module","title":"ANTA ResultManager module","text":"Helper to manage Test Results and generate reports.
Examples:
Create Inventory:
inventory_anta = AntaInventory.parse(\n inventory_file='examples/inventory.yml',\n username='ansible',\n password='ansible',\n timeout=0.5\n)\n
Create Result Manager:
manager = ResultManager()\n
Run tests for all connected devices:
for device in inventory_anta.get_inventory():\n manager.add_test_result(\nVerifyNTP(device=device).test()\n)\nmanager.add_test_result(\nVerifyEOSVersion(device=device).test(version='4.28.3M')\n)\n
Print result in native format:
manager.get_results()\n[\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyNTP',\n result='failure',\n message=\"device is not running NTP correctly\"\n ),\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyEOSVersion',\n result='success',\n message=None\n ),\n]\n
Source code in anta/result_manager/__init__.py
class ResultManager:\n\"\"\"\n Helper to manage Test Results and generate reports.\n\n Examples:\n\n Create Inventory:\n\n inventory_anta = AntaInventory.parse(\n inventory_file='examples/inventory.yml',\n username='ansible',\n password='ansible',\n timeout=0.5\n )\n\n Create Result Manager:\n\n manager = ResultManager()\n\n Run tests for all connected devices:\n\n for device in inventory_anta.get_inventory():\n manager.add_test_result(\n VerifyNTP(device=device).test()\n )\n manager.add_test_result(\n VerifyEOSVersion(device=device).test(version='4.28.3M')\n )\n\n Print result in native format:\n\n manager.get_results()\n [\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyNTP',\n result='failure',\n message=\"device is not running NTP correctly\"\n ),\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyEOSVersion',\n result='success',\n message=None\n ),\n ]\n \"\"\"\n\n def __init__(self) -> None:\n\"\"\"\n Class constructor.\n\n The status of the class is initialized to \"unset\"\n\n Then when adding a test with a status that is NOT 'error' the following\n table shows the updated status:\n\n | Current Status | Added test Status | Updated Status |\n | -------------- | ------------------------------- | -------------- |\n | unset | Any | Any |\n | skipped | unset, skipped | skipped |\n | skipped | success | success |\n | skipped | failure | failure |\n | success | unset, skipped, success | success |\n | success | failure | failure |\n | failure | unset, skipped success, failure | failure |\n\n If the status of the added test is error, the status is untouched and the\n error_status is set to True.\n \"\"\"\n logger.debug(\"Instantiate result-manager\")\n self._result_entries = ListResult()\n # Initialize status\n self.status = \"unset\"\n self.error_status = False\n\n def __len__(self) -> int:\n\"\"\"\n Implement __len__ method to count number of results.\n \"\"\"\n return len(self._result_entries)\n\n def __update_status(self, test_status: str) -> None:\n\"\"\"\n Update ResultManager status based on the table above.\n \"\"\"\n if test_status not in RESULT_OPTIONS:\n raise ValueError(\"{test_status} is not a valid result option\")\n if test_status == \"error\":\n if not self.error_status:\n logger.info(\"A test has returned an 'error' status\")\n self.error_status = True\n return\n\n if self.status == \"unset\":\n self.status = test_status\n elif self.status == \"skipped\" and test_status in {\"success\", \"failure\"}:\n self.status = test_status\n elif self.status == \"success\" and test_status == \"failure\":\n self.status = \"failure\"\n\n def add_test_result(self, entry: TestResult) -> None:\n\"\"\"Add a result to the list\n\n Args:\n entry (TestResult): TestResult data to add to the report\n \"\"\"\n logger.debug(entry)\n self._result_entries.append(entry)\n self.__update_status(entry.result)\n\n def add_test_results(self, entries: List[TestResult]) -> None:\n\"\"\"Add a list of results to the list\n\n Args:\n entries (List[TestResult]): list of TestResult data to add to the report\n \"\"\"\n for e in entries:\n self.add_test_result(e)\n\n def get_status(self, ignore_error: bool = False) -> str:\n\"\"\"\n Returns the current status including error_status if ignore_error is False\n \"\"\"\n return \"error\" if self.error_status and not ignore_error else self.status\n\n def get_results(self, output_format: str = \"native\") -> Any:\n\"\"\"\n Expose list of all test results in different format\n\n Support multiple format:\n - native: ListResults format\n - list: a list of TestResult\n - json: a native JSON format\n\n Args:\n output_format (str, optional): format selector. Can be either native/list/json. Defaults to 'native'.\n\n Returns:\n any: List of results.\n \"\"\"\n if output_format == \"list\":\n return list(self._result_entries)\n\n if output_format == \"json\":\n return json.dumps(pydantic_to_dict(self._result_entries), indent=4)\n\n # Default return for native format.\n return self._result_entries\n\n def get_result_by_test(self, test_name: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given test.\n\n Args:\n test_name (str): Test name to use to filter results\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n list[TestResult]: List of results related to the test.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.test) == test_name]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if result.test == test_name:\n result_manager_filtered.append(result)\n return result_manager_filtered\n\n def get_result_by_host(self, host_ip: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given host.\n\n Args:\n host_ip (str): IP Address of the host to use to filter results.\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n Any: List of results related to the host.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.name) == host_ip]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if str(result.name) == host_ip:\n result_manager_filtered.append(result)\n return result_manager_filtered\n\n def get_testcases(self) -> List[str]:\n\"\"\"\n Get list of name of all test cases in current manager.\n\n Returns:\n List[str]: List of names for all tests.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.test) not in result_list:\n result_list.append(str(testcase.test))\n return result_list\n\n def get_hosts(self) -> List[str]:\n\"\"\"\n Get list of IP addresses in current manager.\n\n Returns:\n List[str]: List of IP addresses.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.name) not in result_list:\n result_list.append(str(testcase.name))\n return result_list\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__init__","title":"__init__()
","text":"Class constructor.
The status of the class is initialized to \u201cunset\u201d
Then when adding a test with a status that is NOT \u2018error\u2019 the following table shows the updated status:
Current Status Added test Status Updated Status unset Any Any skipped unset, skipped skipped skipped success success skipped failure failure success unset, skipped, success success success failure failure failure unset, skipped success, failure failure If the status of the added test is error, the status is untouched and the error_status is set to True.
Source code in anta/result_manager/__init__.py
def __init__(self) -> None:\n\"\"\"\n Class constructor.\n\n The status of the class is initialized to \"unset\"\n\n Then when adding a test with a status that is NOT 'error' the following\n table shows the updated status:\n\n | Current Status | Added test Status | Updated Status |\n | -------------- | ------------------------------- | -------------- |\n | unset | Any | Any |\n | skipped | unset, skipped | skipped |\n | skipped | success | success |\n | skipped | failure | failure |\n | success | unset, skipped, success | success |\n | success | failure | failure |\n | failure | unset, skipped success, failure | failure |\n\n If the status of the added test is error, the status is untouched and the\n error_status is set to True.\n \"\"\"\n logger.debug(\"Instantiate result-manager\")\n self._result_entries = ListResult()\n # Initialize status\n self.status = \"unset\"\n self.error_status = False\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__len__","title":"__len__()
","text":"Implement len method to count number of results.
Source code in anta/result_manager/__init__.py
def __len__(self) -> int:\n\"\"\"\n Implement __len__ method to count number of results.\n \"\"\"\n return len(self._result_entries)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__update_status","title":"__update_status(test_status)
","text":"Update ResultManager status based on the table above.
Source code in anta/result_manager/__init__.py
def __update_status(self, test_status: str) -> None:\n\"\"\"\n Update ResultManager status based on the table above.\n \"\"\"\n if test_status not in RESULT_OPTIONS:\n raise ValueError(\"{test_status} is not a valid result option\")\n if test_status == \"error\":\n if not self.error_status:\n logger.info(\"A test has returned an 'error' status\")\n self.error_status = True\n return\n\n if self.status == \"unset\":\n self.status = test_status\n elif self.status == \"skipped\" and test_status in {\"success\", \"failure\"}:\n self.status = test_status\n elif self.status == \"success\" and test_status == \"failure\":\n self.status = \"failure\"\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.add_test_result","title":"add_test_result(entry)
","text":"Add a result to the list
Parameters:
Name Type Description Default entry
TestResult
TestResult data to add to the report
required Source code in anta/result_manager/__init__.py
def add_test_result(self, entry: TestResult) -> None:\n\"\"\"Add a result to the list\n\n Args:\n entry (TestResult): TestResult data to add to the report\n \"\"\"\n logger.debug(entry)\n self._result_entries.append(entry)\n self.__update_status(entry.result)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.add_test_results","title":"add_test_results(entries)
","text":"Add a list of results to the list
Parameters:
Name Type Description Default entries
List[TestResult]
list of TestResult data to add to the report
required Source code in anta/result_manager/__init__.py
def add_test_results(self, entries: List[TestResult]) -> None:\n\"\"\"Add a list of results to the list\n\n Args:\n entries (List[TestResult]): list of TestResult data to add to the report\n \"\"\"\n for e in entries:\n self.add_test_result(e)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_hosts","title":"get_hosts()
","text":"Get list of IP addresses in current manager.
Returns:
Type Description List[str]
List[str]: List of IP addresses.
Source code in anta/result_manager/__init__.py
def get_hosts(self) -> List[str]:\n\"\"\"\n Get list of IP addresses in current manager.\n\n Returns:\n List[str]: List of IP addresses.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.name) not in result_list:\n result_list.append(str(testcase.name))\n return result_list\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_result_by_host","title":"get_result_by_host(host_ip, output_format='native')
","text":"Get list of test result for a given host.
Parameters:
Name Type Description Default host_ip
str
IP Address of the host to use to filter results.
required output_format
str
format selector. Can be either native/list. Defaults to \u2018native\u2019.
'native'
Returns:
Name Type Description Any
Any
List of results related to the host.
Source code in anta/result_manager/__init__.py
def get_result_by_host(self, host_ip: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given host.\n\n Args:\n host_ip (str): IP Address of the host to use to filter results.\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n Any: List of results related to the host.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.name) == host_ip]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if str(result.name) == host_ip:\n result_manager_filtered.append(result)\n return result_manager_filtered\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_result_by_test","title":"get_result_by_test(test_name, output_format='native')
","text":"Get list of test result for a given test.
Parameters:
Name Type Description Default test_name
str
Test name to use to filter results
required output_format
str
format selector. Can be either native/list. Defaults to \u2018native\u2019.
'native'
Returns:
Type Description Any
list[TestResult]: List of results related to the test.
Source code in anta/result_manager/__init__.py
def get_result_by_test(self, test_name: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given test.\n\n Args:\n test_name (str): Test name to use to filter results\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n list[TestResult]: List of results related to the test.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.test) == test_name]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if result.test == test_name:\n result_manager_filtered.append(result)\n return result_manager_filtered\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_results","title":"get_results(output_format='native')
","text":"Expose list of all test results in different format
Support multiple format - native: ListResults format
- list: a list of TestResult
- json: a native JSON format
Parameters:
Name Type Description Default output_format
str
format selector. Can be either native/list/json. Defaults to \u2018native\u2019.
'native'
Returns:
Name Type Description any
Any
List of results.
Source code in anta/result_manager/__init__.py
def get_results(self, output_format: str = \"native\") -> Any:\n\"\"\"\n Expose list of all test results in different format\n\n Support multiple format:\n - native: ListResults format\n - list: a list of TestResult\n - json: a native JSON format\n\n Args:\n output_format (str, optional): format selector. Can be either native/list/json. Defaults to 'native'.\n\n Returns:\n any: List of results.\n \"\"\"\n if output_format == \"list\":\n return list(self._result_entries)\n\n if output_format == \"json\":\n return json.dumps(pydantic_to_dict(self._result_entries), indent=4)\n\n # Default return for native format.\n return self._result_entries\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_status","title":"get_status(ignore_error=False)
","text":"Returns the current status including error_status if ignore_error is False
Source code in anta/result_manager/__init__.py
def get_status(self, ignore_error: bool = False) -> str:\n\"\"\"\n Returns the current status including error_status if ignore_error is False\n \"\"\"\n return \"error\" if self.error_status and not ignore_error else self.status\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_testcases","title":"get_testcases()
","text":"Get list of name of all test cases in current manager.
Returns:
Type Description List[str]
List[str]: List of names for all tests.
Source code in anta/result_manager/__init__.py
def get_testcases(self) -> List[str]:\n\"\"\"\n Get list of name of all test cases in current manager.\n\n Returns:\n List[str]: List of names for all tests.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.test) not in result_list:\n result_list.append(str(testcase.test))\n return result_list\n
"},{"location":"api/result_manager_models/","title":"Result Manager models","text":""},{"location":"api/result_manager_models/#testresult-entry","title":"TestResult Entry","text":" Bases: BaseModel
Describe the result of a test from a single device.
Attributes:
Name Type Description name
str
Device name where the test has run.
test
str
Test name runs on the device.
test_category
List[str]
List of test categories the test belongs to.
test_description
str
Test description.
results
str
Result of the test. Can be one of [\u201cunset\u201d, \u201csuccess\u201d, \u201cfailure\u201d, \u201cerror\u201d, \u201cskipped\u201d].
message
str
Message to report after the test if any.
Source code in anta/result_manager/models.py
class TestResult(BaseModel):\n\"\"\"\n Describe the result of a test from a single device.\n\n Attributes:\n name (str): Device name where the test has run.\n test (str): Test name runs on the device.\n test_category (List[str]): List of test categories the test belongs to.\n test_description (str): Test description.\n results (str): Result of the test. Can be one of [\"unset\", \"success\", \"failure\", \"error\", \"skipped\"].\n message (str, optional): Message to report after the test if any.\n \"\"\"\n\n name: str\n test: str\n test_category: List[str]\n test_description: str\n result: str = \"unset\"\n messages: List[str] = []\n\n @classmethod\n @validator(\"result\", allow_reuse=True)\n def name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined status\n\n Raises:\n ValueError: If status is unsupported\n\n Returns:\n str: status value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n\n def is_success(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to success\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"success\", message)\n\n def is_failure(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to failure\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"failure\", message)\n\n def is_skipped(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to skipped\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"skipped\", message)\n\n def is_error(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to error\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"error\", message)\n\n def _set_status(self, status: str, message: str = \"\") -> bool:\n\"\"\"\n Set status and insert optional message\n\n Args:\n status (str): status of the test\n message (str): optional message\n\n Returns:\n bool: Always true\n \"\"\"\n self.result = status\n if message != \"\":\n self.messages.append(message)\n return True\n\n def __str__(self) -> str:\n\"\"\"\n Returns a human readable string of this TestResult\n \"\"\"\n return f\"Test {self.test} on device {self.name} has result {self.result}\"\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.__str__","title":"__str__()
","text":"Returns a human readable string of this TestResult
Source code in anta/result_manager/models.py
def __str__(self) -> str:\n\"\"\"\n Returns a human readable string of this TestResult\n \"\"\"\n return f\"Test {self.test} on device {self.name} has result {self.result}\"\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_error","title":"is_error(message='')
","text":"Helper to set status to error
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_error(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to error\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"error\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_failure","title":"is_failure(message='')
","text":"Helper to set status to failure
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_failure(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to failure\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"failure\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_skipped","title":"is_skipped(message='')
","text":"Helper to set status to skipped
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_skipped(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to skipped\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"skipped\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_success","title":"is_success(message='')
","text":"Helper to set status to success
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_success(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to success\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"success\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.name_must_be_in","title":"name_must_be_in(v)
classmethod
","text":"Status validator
Validate status is a supported one
Parameters:
Name Type Description Default v
str
User defined status
required Raises:
Type Description ValueError
If status is unsupported
Returns:
Name Type Description str
str
status value
Source code in anta/result_manager/models.py
@classmethod\n@validator(\"result\", allow_reuse=True)\ndef name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined status\n\n Raises:\n ValueError: If status is unsupported\n\n Returns:\n str: status value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n
"},{"location":"api/result_manager_models/#listresult","title":"ListResult","text":" Bases: BaseModel
List result for all tests on all devices.
Attributes:
Name Type Description __root__
List[TestResult]
A list of TestResult objects.
Source code in anta/result_manager/models.py
class ListResult(BaseModel):\n\"\"\"\n List result for all tests on all devices.\n\n Attributes:\n __root__ (List[TestResult]): A list of TestResult objects.\n \"\"\"\n\n # pylint: disable=R0801\n\n __root__: List[TestResult] = []\n\n def extend(self, values: List[TestResult]) -> None:\n\"\"\"Add support for extend method.\"\"\"\n self.__root__.extend(values)\n\n def append(self, value: TestResult) -> None:\n\"\"\"Add support for append method.\"\"\"\n self.__root__.append(value)\n\n def __iter__(self) -> Iterator[TestResult]: # type: ignore\n\"\"\"Use custom iter method.\"\"\"\n # TODO - mypy is not happy because we overwrite BaseModel.__iter__\n # return type and are breaking Liskov Substitution Principle.\n return iter(self.__root__)\n\n def __getitem__(self, item: int) -> TestResult:\n\"\"\"Use custom getitem method.\"\"\"\n return self.__root__[item]\n\n def __len__(self) -> int:\n\"\"\"Support for length of __root__\"\"\"\n return len(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__getitem__","title":"__getitem__(item)
","text":"Use custom getitem method.
Source code in anta/result_manager/models.py
def __getitem__(self, item: int) -> TestResult:\n\"\"\"Use custom getitem method.\"\"\"\n return self.__root__[item]\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__iter__","title":"__iter__()
","text":"Use custom iter method.
Source code in anta/result_manager/models.py
def __iter__(self) -> Iterator[TestResult]: # type: ignore\n\"\"\"Use custom iter method.\"\"\"\n # TODO - mypy is not happy because we overwrite BaseModel.__iter__\n # return type and are breaking Liskov Substitution Principle.\n return iter(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__len__","title":"__len__()
","text":"Support for length of root
Source code in anta/result_manager/models.py
def __len__(self) -> int:\n\"\"\"Support for length of __root__\"\"\"\n return len(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.append","title":"append(value)
","text":"Add support for append method.
Source code in anta/result_manager/models.py
def append(self, value: TestResult) -> None:\n\"\"\"Add support for append method.\"\"\"\n self.__root__.append(value)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.extend","title":"extend(values)
","text":"Add support for extend method.
Source code in anta/result_manager/models.py
def extend(self, values: List[TestResult]) -> None:\n\"\"\"Add support for extend method.\"\"\"\n self.__root__.extend(values)\n
"},{"location":"api/tests.aaa/","title":"AAA","text":""},{"location":"api/tests.aaa/#anta-catalog-for-aaa-tests","title":"ANTA catalog for AAA tests","text":"Test functions related to the EOS various AAA settings
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctConsoleMethods","title":"VerifyAcctConsoleMethods
","text":" Bases: AntaTest
Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).
Expected Results - success: The test will pass if the provided AAA accounting console method list is matching in the configured accounting types.
- failure: The test will fail if the provided AAA accounting console method list is NOT matching in the configured accounting types.
- skipped: The test will be skipped if the AAA accounting console method list or accounting type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAcctConsoleMethods(AntaTest):\n\"\"\"\n Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA accounting console method list is matching in the configured accounting types.\n * failure: The test will fail if the provided AAA accounting console method list is NOT matching in the configured accounting types.\n * skipped: The test will be skipped if the AAA accounting console method list or accounting type list are not provided.\n \"\"\"\n\n name = \"VerifyAcctConsoleMethods\"\n description = \"Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods accounting\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctConsoleMethods validation.\n\n Args:\n methods: List of AAA accounting console methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"consoleAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"consoleMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA console accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting console methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctConsoleMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAcctConsoleMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA accounting console methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of accounting types to verify. List elements must be: commands, exec, system, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctConsoleMethods validation.\n\n Args:\n methods: List of AAA accounting console methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"consoleAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"consoleMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA console accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting console methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctDefaultMethods","title":"VerifyAcctDefaultMethods
","text":" Bases: AntaTest
Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).
Expected Results - success: The test will pass if the provided AAA accounting default method list is matching in the configured accounting types.
- failure: The test will fail if the provided AAA accounting default method list is NOT matching in the configured accounting types.
- skipped: The test will be skipped if the AAA accounting default method list or accounting type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAcctDefaultMethods(AntaTest):\n\"\"\"\n Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA accounting default method list is matching in the configured accounting types.\n * failure: The test will fail if the provided AAA accounting default method list is NOT matching in the configured accounting types.\n * skipped: The test will be skipped if the AAA accounting default method list or accounting type list are not provided.\n \"\"\"\n\n name = \"VerifyAcctDefaultMethods\"\n description = \"Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods accounting\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctDefaultMethods validation.\n\n Args:\n methods: List of AAA accounting default methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"defaultAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"defaultMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA default accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting default methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctDefaultMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAcctDefaultMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA accounting default methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of accounting types to verify. List elements must be: commands, exec, system, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctDefaultMethods validation.\n\n Args:\n methods: List of AAA accounting default methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"defaultAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"defaultMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA default accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting default methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthenMethods","title":"VerifyAuthenMethods
","text":" Bases: AntaTest
Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).
Expected Results - success: The test will pass if the provided AAA authentication method list is matching in the configured authentication types.
- failure: The test will fail if the provided AAA authentication method list is NOT matching in the configured authentication types.
- skipped: The test will be skipped if the AAA authentication method list or authentication type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAuthenMethods(AntaTest):\n\"\"\"\n Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA authentication method list is matching in the configured authentication types.\n * failure: The test will fail if the provided AAA authentication method list is NOT matching in the configured authentication types.\n * skipped: The test will be skipped if the AAA authentication method list or authentication type list are not provided.\n \"\"\"\n\n name = \"VerifyAuthenMethods\"\n description = \"Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods authentication\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthenMethods validation.\n\n Args:\n methods: List of AAA authentication methods. Methods should be in the right order.\n auth_types: List of authentication types to verify. List elements must be: login, enable, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"login\", \"enable\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthenMethods\"\n\n if auth_type_key == \"loginAuthenMethods\":\n if not command_output[auth_type_key].get(\"login\"):\n self.result.is_failure(\"AAA authentication methods are not configured for login console\")\n return\n\n if command_output[auth_type_key][\"login\"][\"methods\"] != methods_with_group:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for login console\")\n return\n\n if command_output[auth_type_key][\"default\"][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthenMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAuthenMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA authentication methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of authentication types to verify. List elements must be: login, enable, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthenMethods validation.\n\n Args:\n methods: List of AAA authentication methods. Methods should be in the right order.\n auth_types: List of authentication types to verify. List elements must be: login, enable, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"login\", \"enable\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthenMethods\"\n\n if auth_type_key == \"loginAuthenMethods\":\n if not command_output[auth_type_key].get(\"login\"):\n self.result.is_failure(\"AAA authentication methods are not configured for login console\")\n return\n\n if command_output[auth_type_key][\"login\"][\"methods\"] != methods_with_group:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for login console\")\n return\n\n if command_output[auth_type_key][\"default\"][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthzMethods","title":"VerifyAuthzMethods
","text":" Bases: AntaTest
Verifies the AAA authorization method lists for different authorization types (commands, exec).
Expected Results - success: The test will pass if the provided AAA authorization method list is matching in the configured authorization types.
- failure: The test will fail if the provided AAA authorization method list is NOT matching in the configured authorization types.
- skipped: The test will be skipped if the AAA authentication method list or authorization type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAuthzMethods(AntaTest):\n\"\"\"\n Verifies the AAA authorization method lists for different authorization types (commands, exec).\n\n Expected Results:\n * success: The test will pass if the provided AAA authorization method list is matching in the configured authorization types.\n * failure: The test will fail if the provided AAA authorization method list is NOT matching in the configured authorization types.\n * skipped: The test will be skipped if the AAA authentication method list or authorization type list are not provided.\n \"\"\"\n\n name = \"VerifyAuthzMethods\"\n description = \"Verifies the AAA authorization method lists for different authorization types (commands, exec).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods authorization\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthzMethods validation.\n\n Args:\n methods: List of AAA authorization methods. Methods should be in the right order.\n auth_types: List of authorization types to verify. List elements must be: commands, exec.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n _check_auth_type(auth_types, [\"commands\", \"exec\"])\n\n methods_with_group = _check_group_methods(methods)\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthzMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if command_output[auth_type_key][method_key][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authorization methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthzMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAuthzMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA authorization methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of authorization types to verify. List elements must be: commands, exec.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthzMethods validation.\n\n Args:\n methods: List of AAA authorization methods. Methods should be in the right order.\n auth_types: List of authorization types to verify. List elements must be: commands, exec.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n _check_auth_type(auth_types, [\"commands\", \"exec\"])\n\n methods_with_group = _check_group_methods(methods)\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthzMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if command_output[auth_type_key][method_key][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authorization methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServerGroups","title":"VerifyTacacsServerGroups
","text":" Bases: AntaTest
Verifies if the provided TACACS server group(s) are configured.
Expected Results - success: The test will pass if the provided TACACS server group(s) are configured.
- failure: The test will fail if one or all the provided TACACS server group(s) are NOT configured.
- skipped: The test will be skipped if TACACS server group(s) are not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsServerGroups(AntaTest):\n\"\"\"\n Verifies if the provided TACACS server group(s) are configured.\n\n Expected Results:\n * success: The test will pass if the provided TACACS server group(s) are configured.\n * failure: The test will fail if one or all the provided TACACS server group(s) are NOT configured.\n * skipped: The test will be skipped if TACACS server group(s) are not provided.\n \"\"\"\n\n name = \"VerifyTacacsServerGroups\"\n description = \"Verifies if the provided TACACS server group(s) are configured.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, groups: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTacacsServerGroups validation.\n\n Args:\n groups: List of TACACS server group.\n \"\"\"\n if not groups:\n self.result.is_skipped(f\"{self.__class__.name} did not run because groups were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_groups = command_output[\"groups\"]\n\n if not tacacs_groups:\n self.result.is_failure(\"No TACACS server group(s) are configured\")\n return\n\n not_configured = [group for group in groups if group not in tacacs_groups]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS server group(s) {not_configured} are not configured\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServerGroups.test","title":"test(groups=None)
","text":"Run VerifyTacacsServerGroups validation.
Parameters:
Name Type Description Default groups
Optional[List[str]]
List of TACACS server group.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, groups: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTacacsServerGroups validation.\n\n Args:\n groups: List of TACACS server group.\n \"\"\"\n if not groups:\n self.result.is_skipped(f\"{self.__class__.name} did not run because groups were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_groups = command_output[\"groups\"]\n\n if not tacacs_groups:\n self.result.is_failure(\"No TACACS server group(s) are configured\")\n return\n\n not_configured = [group for group in groups if group not in tacacs_groups]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS server group(s) {not_configured} are not configured\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServers","title":"VerifyTacacsServers
","text":" Bases: AntaTest
Verifies TACACS servers are configured for a specified VRF.
Expected Results - success: The test will pass if the provided TACACS servers are configured in the specified VRF.
- failure: The test will fail if the provided TACACS servers are NOT configured in the specified VRF.
- skipped: The test will be skipped if TACACS servers or VRF are not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsServers(AntaTest):\n\"\"\"\n Verifies TACACS servers are configured for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided TACACS servers are configured in the specified VRF.\n * failure: The test will fail if the provided TACACS servers are NOT configured in the specified VRF.\n * skipped: The test will be skipped if TACACS servers or VRF are not provided.\n \"\"\"\n\n name = \"VerifyTacacsServers\"\n description = \"Verifies TACACS servers are configured for a specified VRF.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, servers: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsServers validation.\n\n Args:\n servers: List of TACACS servers IP addresses.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not servers or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because servers or vrf were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_servers = command_output[\"tacacsServers\"]\n\n if not tacacs_servers:\n self.result.is_failure(\"No TACACS servers are configured\")\n return\n\n not_configured = [\n server\n for server in servers\n if not any(server == tacacs_server[\"serverInfo\"][\"hostname\"] and vrf == tacacs_server[\"serverInfo\"][\"vrf\"] for tacacs_server in tacacs_servers)\n ]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServers.test","title":"test(servers=None, vrf='default')
","text":"Run VerifyTacacsServers validation.
Parameters:
Name Type Description Default servers
Optional[List[str]]
List of TACACS servers IP addresses.
None
vrf
str
The name of the VRF to transport TACACS messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, servers: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsServers validation.\n\n Args:\n servers: List of TACACS servers IP addresses.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not servers or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because servers or vrf were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_servers = command_output[\"tacacsServers\"]\n\n if not tacacs_servers:\n self.result.is_failure(\"No TACACS servers are configured\")\n return\n\n not_configured = [\n server\n for server in servers\n if not any(server == tacacs_server[\"serverInfo\"][\"hostname\"] and vrf == tacacs_server[\"serverInfo\"][\"vrf\"] for tacacs_server in tacacs_servers)\n ]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsSourceIntf","title":"VerifyTacacsSourceIntf
","text":" Bases: AntaTest
Verifies TACACS source-interface for a specified VRF.
Expected Results - success: The test will pass if the provided TACACS source-interface is configured in the specified VRF.
- failure: The test will fail if the provided TACACS source-interface is NOT configured in the specified VRF.
- skipped: The test will be skipped if source-interface or VRF is not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsSourceIntf(AntaTest):\n\"\"\"\n Verifies TACACS source-interface for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided TACACS source-interface is configured in the specified VRF.\n * failure: The test will fail if the provided TACACS source-interface is NOT configured in the specified VRF.\n * skipped: The test will be skipped if source-interface or VRF is not provided.\n \"\"\"\n\n name = \"VerifyTacacsSourceIntf\"\n description = \"Verifies TACACS source-interface for a specified VRF.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsSourceIntf validation.\n\n Args:\n intf: Source-interface to use as source IP of TACACS messages.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"srcIntf\"][vrf] == intf:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Wrong source-interface configured in VRF {vrf}\")\n\n except KeyError:\n self.result.is_failure(f\"Source-interface {intf} is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsSourceIntf.test","title":"test(intf=None, vrf='default')
","text":"Run VerifyTacacsSourceIntf validation.
Parameters:
Name Type Description Default intf
Optional[str]
Source-interface to use as source IP of TACACS messages.
None
vrf
str
The name of the VRF to transport TACACS messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsSourceIntf validation.\n\n Args:\n intf: Source-interface to use as source IP of TACACS messages.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"srcIntf\"][vrf] == intf:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Wrong source-interface configured in VRF {vrf}\")\n\n except KeyError:\n self.result.is_failure(f\"Source-interface {intf} is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.configuration/","title":"Configuration","text":""},{"location":"api/tests.configuration/#anta-catalog-for-configuration-tests","title":"ANTA catalog for configuration tests","text":"Test functions related to the device configuration
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyRunningConfigDiffs","title":"VerifyRunningConfigDiffs
","text":" Bases: AntaTest
Verifies there is no difference between the running-config and the startup-config.
Source code in anta/tests/configuration.py
class VerifyRunningConfigDiffs(AntaTest):\n\"\"\"\n Verifies there is no difference between the running-config and the startup-config.\n \"\"\"\n\n name = \"VerifyRunningConfigDiffs\"\n description = \"\"\n categories = [\"configuration\"]\n commands = [AntaTestCommand(command=\"show running-config diffs\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyRunningConfigDiffs validation\"\"\"\n logger.debug(f\"self.instance_commands is {self.instance_commands}\")\n command_output = self.instance_commands[0].output\n logger.debug(f\"command_output is {command_output}\")\n if command_output is None or command_output == \"\":\n self.result.is_success()\n else:\n self.result.is_failure()\n self.result.is_failure(str(command_output))\n logger.debug(f\"result is {self.result}\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyRunningConfigDiffs.test","title":"test()
","text":"Run VerifyRunningConfigDiffs validation
Source code in anta/tests/configuration.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyRunningConfigDiffs validation\"\"\"\n logger.debug(f\"self.instance_commands is {self.instance_commands}\")\n command_output = self.instance_commands[0].output\n logger.debug(f\"command_output is {command_output}\")\n if command_output is None or command_output == \"\":\n self.result.is_success()\n else:\n self.result.is_failure()\n self.result.is_failure(str(command_output))\n logger.debug(f\"result is {self.result}\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyZeroTouch","title":"VerifyZeroTouch
","text":" Bases: AntaTest
Verifies ZeroTouch is disabled.
Source code in anta/tests/configuration.py
class VerifyZeroTouch(AntaTest):\n\"\"\"\n Verifies ZeroTouch is disabled.\n \"\"\"\n\n name = \"VerifyZeroTouch\"\n description = \"Verifies ZeroTouch is disabled.\"\n categories = [\"configuration\"]\n commands = [AntaTestCommand(command=\"show zerotouch\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyZeroTouch validation\"\"\"\n\n command_output = self.instance_commands[0].output\n\n assert isinstance(command_output, dict)\n if command_output[\"mode\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"ZTP is NOT disabled\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyZeroTouch.test","title":"test()
","text":"Run VerifyZeroTouch validation
Source code in anta/tests/configuration.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyZeroTouch validation\"\"\"\n\n command_output = self.instance_commands[0].output\n\n assert isinstance(command_output, dict)\n if command_output[\"mode\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"ZTP is NOT disabled\")\n
"},{"location":"api/tests.connectivity/","title":"Connectivity","text":""},{"location":"api/tests.connectivity/#anta-catalog-for-connectivity-tests","title":"ANTA catalog for connectivity tests","text":"Test functions related to various connectivity checks
"},{"location":"api/tests.connectivity/#anta.tests.connectivity.VerifyReachability","title":"VerifyReachability
","text":" Bases: AntaTest
Test network reachability to one or many destination IP(s).
Expected Results - success: The test will pass if all destination IP(s) are reachable.
- failure: The test will fail if one or many destination IP(s) are unreachable.
- error: The test will give an error if the destination IP(s) or the source interface/IP(s) are not provided as template_params.
Source code in anta/tests/connectivity.py
class VerifyReachability(AntaTest):\n\"\"\"\n Test network reachability to one or many destination IP(s).\n\n Expected Results:\n * success: The test will pass if all destination IP(s) are reachable.\n * failure: The test will fail if one or many destination IP(s) are unreachable.\n * error: The test will give an error if the destination IP(s) or the source interface/IP(s) are not provided as template_params.\n \"\"\"\n\n name = \"VerifyReachability\"\n description = \"Test the network reachability to one or many destination IP(s).\"\n categories = [\"connectivity\"]\n template = AntaTestTemplate(template=\"ping {dst} source {src} repeat 2\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyReachability validation.\n \"\"\"\n\n failures = []\n\n for index, command in enumerate(self.instance_commands):\n src, dst = (cast(Dict[str, str], command.template_params)[\"src\"], cast(Dict[str, str], command.template_params)[\"dst\"])\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n if \"2 received\" not in command_output[\"messages\"][0]:\n failures.append((src, dst))\n\n if not failures:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"Connectivity test failed for the following source-destination pairs: {failures}\")\n
"},{"location":"api/tests.connectivity/#anta.tests.connectivity.VerifyReachability.test","title":"test()
","text":"Run VerifyReachability validation.
Source code in anta/tests/connectivity.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyReachability validation.\n \"\"\"\n\n failures = []\n\n for index, command in enumerate(self.instance_commands):\n src, dst = (cast(Dict[str, str], command.template_params)[\"src\"], cast(Dict[str, str], command.template_params)[\"dst\"])\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n if \"2 received\" not in command_output[\"messages\"][0]:\n failures.append((src, dst))\n\n if not failures:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"Connectivity test failed for the following source-destination pairs: {failures}\")\n
"},{"location":"api/tests.field_notices/","title":"Field Notices","text":""},{"location":"api/tests.field_notices/#anta-catalog-for-field-notices-tests","title":"ANTA catalog for Field Notices tests","text":"Test functions to flag field notices
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice44Resolution","title":"VerifyFieldNotice44Resolution
","text":" Bases: AntaTest
Verifies the device is using an Aboot version that fix the bug discussed in the field notice 44 (Aboot manages system settings prior to EOS initialization).
https://www.arista.com/en/support/advisories-notices/field-notice/8756-field-notice-44
Source code in anta/tests/field_notices.py
class VerifyFieldNotice44Resolution(AntaTest):\n\"\"\"\n Verifies the device is using an Aboot version that fix the bug discussed\n in the field notice 44 (Aboot manages system settings prior to EOS initialization).\n\n https://www.arista.com/en/support/advisories-notices/field-notice/8756-field-notice-44\n \"\"\"\n\n name = \"VerifyFieldNotice44Resolution\"\n description = (\n \"Verifies the device is using an Aboot version that fix the bug discussed in the field notice 44 (Aboot manages system settings prior to EOS initialization)\"\n )\n categories = [\"field notices\", \"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n # TODO maybe implement ONLY ON PLATFORMS instead\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice44Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\n \"DCS-7010T-48\",\n \"DCS-7010T-48-DC\",\n \"DCS-7050TX-48\",\n \"DCS-7050TX-64\",\n \"DCS-7050TX-72\",\n \"DCS-7050TX-72Q\",\n \"DCS-7050TX-96\",\n \"DCS-7050TX2-128\",\n \"DCS-7050SX-64\",\n \"DCS-7050SX-72\",\n \"DCS-7050SX-72Q\",\n \"DCS-7050SX2-72Q\",\n \"DCS-7050SX-96\",\n \"DCS-7050SX2-128\",\n \"DCS-7050QX-32S\",\n \"DCS-7050QX2-32S\",\n \"DCS-7050SX3-48YC12\",\n \"DCS-7050CX3-32S\",\n \"DCS-7060CX-32S\",\n \"DCS-7060CX2-32S\",\n \"DCS-7060SX2-48YC6\",\n \"DCS-7160-48YC6\",\n \"DCS-7160-48TC6\",\n \"DCS-7160-32CQ\",\n \"DCS-7280SE-64\",\n \"DCS-7280SE-68\",\n \"DCS-7280SE-72\",\n \"DCS-7150SC-24-CLD\",\n \"DCS-7150SC-64-CLD\",\n \"DCS-7020TR-48\",\n \"DCS-7020TRA-48\",\n \"DCS-7020SR-24C2\",\n \"DCS-7020SRG-24C2\",\n \"DCS-7280TR-48C6\",\n \"DCS-7280TRA-48C6\",\n \"DCS-7280SR-48C6\",\n \"DCS-7280SRA-48C6\",\n \"DCS-7280SRAM-48C6\",\n \"DCS-7280SR2K-48C6-M\",\n \"DCS-7280SR2-48YC6\",\n \"DCS-7280SR2A-48YC6\",\n \"DCS-7280SRM-40CX2\",\n \"DCS-7280QR-C36\",\n \"DCS-7280QRA-C36S\",\n ]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n\n model = cast(str, command_output[\"modelName\"])\n # TODO this list could be a regex\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"device is not impacted by FN044\")\n return\n\n for component in command_output[\"details\"][\"components\"]:\n if component[\"name\"] == \"Aboot\":\n aboot_version = component[\"version\"].split(\"-\")[2]\n self.result.is_success()\n if aboot_version.startswith(\"4.0.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"4.1.\") and int(aboot_version.split(\".\")[2]) < 1:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.0.\") and int(aboot_version.split(\".\")[2]) < 9:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.1.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice44Resolution.test","title":"test()
","text":"Run VerifyFieldNotice44Resolution validation
Source code in anta/tests/field_notices.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice44Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\n \"DCS-7010T-48\",\n \"DCS-7010T-48-DC\",\n \"DCS-7050TX-48\",\n \"DCS-7050TX-64\",\n \"DCS-7050TX-72\",\n \"DCS-7050TX-72Q\",\n \"DCS-7050TX-96\",\n \"DCS-7050TX2-128\",\n \"DCS-7050SX-64\",\n \"DCS-7050SX-72\",\n \"DCS-7050SX-72Q\",\n \"DCS-7050SX2-72Q\",\n \"DCS-7050SX-96\",\n \"DCS-7050SX2-128\",\n \"DCS-7050QX-32S\",\n \"DCS-7050QX2-32S\",\n \"DCS-7050SX3-48YC12\",\n \"DCS-7050CX3-32S\",\n \"DCS-7060CX-32S\",\n \"DCS-7060CX2-32S\",\n \"DCS-7060SX2-48YC6\",\n \"DCS-7160-48YC6\",\n \"DCS-7160-48TC6\",\n \"DCS-7160-32CQ\",\n \"DCS-7280SE-64\",\n \"DCS-7280SE-68\",\n \"DCS-7280SE-72\",\n \"DCS-7150SC-24-CLD\",\n \"DCS-7150SC-64-CLD\",\n \"DCS-7020TR-48\",\n \"DCS-7020TRA-48\",\n \"DCS-7020SR-24C2\",\n \"DCS-7020SRG-24C2\",\n \"DCS-7280TR-48C6\",\n \"DCS-7280TRA-48C6\",\n \"DCS-7280SR-48C6\",\n \"DCS-7280SRA-48C6\",\n \"DCS-7280SRAM-48C6\",\n \"DCS-7280SR2K-48C6-M\",\n \"DCS-7280SR2-48YC6\",\n \"DCS-7280SR2A-48YC6\",\n \"DCS-7280SRM-40CX2\",\n \"DCS-7280QR-C36\",\n \"DCS-7280QRA-C36S\",\n ]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n\n model = cast(str, command_output[\"modelName\"])\n # TODO this list could be a regex\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"device is not impacted by FN044\")\n return\n\n for component in command_output[\"details\"][\"components\"]:\n if component[\"name\"] == \"Aboot\":\n aboot_version = component[\"version\"].split(\"-\")[2]\n self.result.is_success()\n if aboot_version.startswith(\"4.0.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"4.1.\") and int(aboot_version.split(\".\")[2]) < 1:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.0.\") and int(aboot_version.split(\".\")[2]) < 9:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.1.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice72Resolution","title":"VerifyFieldNotice72Resolution
","text":" Bases: AntaTest
Checks if the device is potentially exposed to Field Notice 72, and if the issue has been mitigated.
https://www.arista.com/en/support/advisories-notices/field-notice/17410-field-notice-0072
Source code in anta/tests/field_notices.py
class VerifyFieldNotice72Resolution(AntaTest):\n\"\"\"\n Checks if the device is potentially exposed to Field Notice 72, and if the issue has been mitigated.\n\n https://www.arista.com/en/support/advisories-notices/field-notice/17410-field-notice-0072\n \"\"\"\n\n name = \"VerifyFieldNotice72Resolution\"\n description = \"Verifies if the device has exposeure to FN72, and if the issue has been mitigated\"\n categories = [\"field notices\", \"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n # TODO maybe implement ONLY ON PLATFORMS instead\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice72Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\"DCS-7280SR3-48YC8\", \"DCS-7280SR3K-48YC8\"]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n model = cast(str, command_output[\"modelName\"])\n\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"Platform is not impacted by FN072\")\n return\n\n serial = command_output[\"serialNumber\"]\n number = int(serial[3:7])\n\n if \"JPE\" not in serial and \"JAS\" not in serial:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JPE\" in serial and number >= 2131:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JPE\" in serial and number >= 2134:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n # Because each of the if checks above will return if taken, we only run the long\n # check if we get this far\n for entry in command_output[\"details\"][\"components\"]:\n if entry[\"name\"] == \"FixedSystemvrm1\":\n if int(entry[\"version\"]) < 7:\n self.result.is_failure(\"Device is exposed to FN72\")\n else:\n self.result.is_success(\"FN72 is mitigated\")\n return\n # We should never hit this point\n self.result.is_error(\"Error in running test - FixedSystemvrm1 not found\")\n return\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice72Resolution.test","title":"test()
","text":"Run VerifyFieldNotice72Resolution validation
Source code in anta/tests/field_notices.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice72Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\"DCS-7280SR3-48YC8\", \"DCS-7280SR3K-48YC8\"]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n model = cast(str, command_output[\"modelName\"])\n\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"Platform is not impacted by FN072\")\n return\n\n serial = command_output[\"serialNumber\"]\n number = int(serial[3:7])\n\n if \"JPE\" not in serial and \"JAS\" not in serial:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JPE\" in serial and number >= 2131:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JPE\" in serial and number >= 2134:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n # Because each of the if checks above will return if taken, we only run the long\n # check if we get this far\n for entry in command_output[\"details\"][\"components\"]:\n if entry[\"name\"] == \"FixedSystemvrm1\":\n if int(entry[\"version\"]) < 7:\n self.result.is_failure(\"Device is exposed to FN72\")\n else:\n self.result.is_success(\"FN72 is mitigated\")\n return\n # We should never hit this point\n self.result.is_error(\"Error in running test - FixedSystemvrm1 not found\")\n return\n
"},{"location":"api/tests.hardware/","title":"Hardware","text":""},{"location":"api/tests.hardware/#anta-catalog-for-hardware-tests","title":"ANTA catalog for hardware tests","text":"Test functions related to the hardware or environement
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyAdverseDrops","title":"VerifyAdverseDrops
","text":" Bases: AntaTest
Verifies there is no adverse drops on DCS7280E and DCS7500E.
Source code in anta/tests/hardware.py
class VerifyAdverseDrops(AntaTest):\n\"\"\"\n Verifies there is no adverse drops on DCS7280E and DCS7500E.\n \"\"\"\n\n name = \"VerifyAdverseDrops\"\n description = \"Verifies there is no adverse drops on DCS7280E and DCS7500E\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show hardware counter drop\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyAdverseDrops validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_adverse_drop = command_output[\"totalAdverseDrops\"] if \"totalAdverseDrops\" in command_output.keys() else \"\"\n if total_adverse_drop == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device TotalAdverseDrops counter is {total_adverse_drop}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyAdverseDrops.test","title":"test()
","text":"Run VerifyAdverseDrops validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyAdverseDrops validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_adverse_drop = command_output[\"totalAdverseDrops\"] if \"totalAdverseDrops\" in command_output.keys() else \"\"\n if total_adverse_drop == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device TotalAdverseDrops counter is {total_adverse_drop}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentCooling","title":"VerifyEnvironmentCooling
","text":" Bases: AntaTest
Verifies the fans status is in the accepted states list.
Default accepted states list is [\u2018ok\u2019]
Source code in anta/tests/hardware.py
class VerifyEnvironmentCooling(AntaTest):\n\"\"\"\n Verifies the fans status is in the accepted states list.\n\n Default accepted states list is ['ok']\n \"\"\"\n\n name = \"VerifyEnvironmentCooling\"\n description = \"Verifies the fans status is OK for fans\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment cooling\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentCooling validation\n\n Args:\n accepted_states: Accepted states list for fan status\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n self.result.is_success()\n # First go through power supplies fans\n for power_supply in command_output.get(\"powerSupplySlots\", []):\n for fan in power_supply.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on PowerSupply {power_supply['label']} has state '{state}'.\")\n # Then go through Fan Trays\n for fan_tray in command_output.get(\"fanTraySlots\", []):\n for fan in fan_tray.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on Fan Tray {fan_tray['label']} has state '{state}'.\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentCooling.test","title":"test(accepted_states=None)
","text":"Run VerifyEnvironmentCooling validation
Parameters:
Name Type Description Default accepted_states
Optional[List[str]]
Accepted states list for fan status
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentCooling validation\n\n Args:\n accepted_states: Accepted states list for fan status\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n self.result.is_success()\n # First go through power supplies fans\n for power_supply in command_output.get(\"powerSupplySlots\", []):\n for fan in power_supply.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on PowerSupply {power_supply['label']} has state '{state}'.\")\n # Then go through Fan Trays\n for fan_tray in command_output.get(\"fanTraySlots\", []):\n for fan in fan_tray.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on Fan Tray {fan_tray['label']} has state '{state}'.\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentPower","title":"VerifyEnvironmentPower
","text":" Bases: AntaTest
Verifies the power supplies status is in the accepted states list
The default accepted states list is [\u2018ok\u2019]
Source code in anta/tests/hardware.py
class VerifyEnvironmentPower(AntaTest):\n\"\"\"\n Verifies the power supplies status is in the accepted states list\n\n The default accepted states list is ['ok']\n \"\"\"\n\n name = \"VerifyEnvironmentPower\"\n description = \"Verifies the power supplies status is OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment power\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentPower validation\n\n Args:\n accepted_states: Accepted states list for power supplies\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n power_supplies = command_output[\"powerSupplies\"] if \"powerSupplies\" in command_output.keys() else \"{}\"\n wrong_power_supplies = {\n powersupply: {\"state\": value[\"state\"]} for powersupply, value in dict(power_supplies).items() if value[\"state\"] not in accepted_states\n }\n if not wrong_power_supplies:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following power supplies states are not in the accepted_states list {accepted_states}\")\n self.result.messages.append(str(wrong_power_supplies))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentPower.test","title":"test(accepted_states=None)
","text":"Run VerifyEnvironmentPower validation
Parameters:
Name Type Description Default accepted_states
Optional[List[str]]
Accepted states list for power supplies
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentPower validation\n\n Args:\n accepted_states: Accepted states list for power supplies\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n power_supplies = command_output[\"powerSupplies\"] if \"powerSupplies\" in command_output.keys() else \"{}\"\n wrong_power_supplies = {\n powersupply: {\"state\": value[\"state\"]} for powersupply, value in dict(power_supplies).items() if value[\"state\"] not in accepted_states\n }\n if not wrong_power_supplies:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following power supplies states are not in the accepted_states list {accepted_states}\")\n self.result.messages.append(str(wrong_power_supplies))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentSystemCooling","title":"VerifyEnvironmentSystemCooling
","text":" Bases: AntaTest
Verifies the System Cooling is ok.
Source code in anta/tests/hardware.py
class VerifyEnvironmentSystemCooling(AntaTest):\n\"\"\"\n Verifies the System Cooling is ok.\n \"\"\"\n\n name = \"VerifyEnvironmentSystemCooling\"\n description = \"Verifies the fans status is OK for fans\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment cooling\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyEnvironmentCooling validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sys_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n\n self.result.is_success()\n if sys_status != \"coolingOk\":\n self.result.is_failure(f\"Device System cooling is not OK: {sys_status}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentSystemCooling.test","title":"test()
","text":"Run VerifyEnvironmentCooling validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyEnvironmentCooling validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sys_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n\n self.result.is_success()\n if sys_status != \"coolingOk\":\n self.result.is_failure(f\"Device System cooling is not OK: {sys_status}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTemperature","title":"VerifyTemperature
","text":" Bases: AntaTest
Verifies device temparture is currently OK (temperatureOK).
Source code in anta/tests/hardware.py
class VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK (temperatureOK).\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK (temperatureOK)\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTemperature.test","title":"test()
","text":"Run VerifyTemperature validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversManufacturers","title":"VerifyTransceiversManufacturers
","text":" Bases: AntaTest
Verifies Manufacturers of all Transceivers.
Source code in anta/tests/hardware.py
class VerifyTransceiversManufacturers(AntaTest):\n\"\"\"\n Verifies Manufacturers of all Transceivers.\n \"\"\"\n\n name = \"VerifyTransceiversManufacturers\"\n description = \"\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show inventory\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTransceiversManufacturers validation\n\n Args:\n manufacturers: List of allowed transceivers manufacturers.\n \"\"\"\n if not manufacturers:\n self.result.is_skipped(f\"{self.__class__.name} was not run as no manufacturers were given\")\n else:\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n wrong_manufacturers = {interface: value[\"mfgName\"] for interface, value in command_output[\"xcvrSlots\"].items() if value[\"mfgName\"] not in manufacturers}\n if not wrong_manufacturers:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following interfaces have transceivers from unauthorized manufacturers\")\n self.result.messages.append(str(wrong_manufacturers))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversManufacturers.test","title":"test(manufacturers=None)
","text":"Run VerifyTransceiversManufacturers validation
Parameters:
Name Type Description Default manufacturers
Optional[List[str]]
List of allowed transceivers manufacturers.
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, manufacturers: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTransceiversManufacturers validation\n\n Args:\n manufacturers: List of allowed transceivers manufacturers.\n \"\"\"\n if not manufacturers:\n self.result.is_skipped(f\"{self.__class__.name} was not run as no manufacturers were given\")\n else:\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n wrong_manufacturers = {interface: value[\"mfgName\"] for interface, value in command_output[\"xcvrSlots\"].items() if value[\"mfgName\"] not in manufacturers}\n if not wrong_manufacturers:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following interfaces have transceivers from unauthorized manufacturers\")\n self.result.messages.append(str(wrong_manufacturers))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversTemperature","title":"VerifyTransceiversTemperature
","text":" Bases: AntaTest
Verifies Transceivers temperature is currently OK.
Source code in anta/tests/hardware.py
class VerifyTransceiversTemperature(AntaTest):\n\"\"\"\n Verifies Transceivers temperature is currently OK.\n \"\"\"\n\n name = \"VerifyTransceiversTemperature\"\n description = \"Verifies Transceivers temperature is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature transceiver\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTransceiversTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sensors = command_output[\"tempSensors\"] if \"tempSensors\" in command_output.keys() else \"\"\n wrong_sensors = {\n sensor[\"name\"]: {\n \"hwStatus\": sensor[\"hwStatus\"],\n \"alertCount\": sensor[\"alertCount\"],\n }\n for sensor in sensors\n if sensor[\"hwStatus\"] != \"ok\" or sensor[\"alertCount\"] != 0\n }\n if not wrong_sensors:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following sensors do not have the correct temperature or had alarms in the past:\")\n self.result.messages.append(str(wrong_sensors))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversTemperature.test","title":"test()
","text":"Run VerifyTransceiversTemperature validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyTransceiversTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sensors = command_output[\"tempSensors\"] if \"tempSensors\" in command_output.keys() else \"\"\n wrong_sensors = {\n sensor[\"name\"]: {\n \"hwStatus\": sensor[\"hwStatus\"],\n \"alertCount\": sensor[\"alertCount\"],\n }\n for sensor in sensors\n if sensor[\"hwStatus\"] != \"ok\" or sensor[\"alertCount\"] != 0\n }\n if not wrong_sensors:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following sensors do not have the correct temperature or had alarms in the past:\")\n self.result.messages.append(str(wrong_sensors))\n
"},{"location":"api/tests.interfaces/","title":"Interfaces","text":""},{"location":"api/tests.interfaces/#anta-catalog-for-interfaces-tests","title":"ANTA catalog for interfaces tests","text":"Test functions related to the device interfaces
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIPProxyARP","title":"VerifyIPProxyARP
","text":" Bases: AntaTest
Verifies if Proxy-ARP is enabled for the provided list of interface(s).
Expected Results - success: The test will pass if Proxy-ARP is enabled on the specified interface(s).
- failure: The test will fail if Proxy-ARP is disabled on the specified interface(s).
- error: The test will give an error if a list of interface(s) is not provided as template_params.
Source code in anta/tests/interfaces.py
class VerifyIPProxyARP(AntaTest):\n\"\"\"\n Verifies if Proxy-ARP is enabled for the provided list of interface(s).\n\n Expected Results:\n * success: The test will pass if Proxy-ARP is enabled on the specified interface(s).\n * failure: The test will fail if Proxy-ARP is disabled on the specified interface(s).\n * error: The test will give an error if a list of interface(s) is not provided as template_params.\n\n \"\"\"\n\n name = \"VerifyIPProxyARP\"\n description = \"Verifies if Proxy-ARP is enabled for the provided list of interface(s).\"\n categories = [\"interfaces\"]\n template = AntaTestTemplate(template=\"show ip interface {intf}\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyIPProxyARP validation.\n \"\"\"\n\n disabled_intf = []\n\n for index, command in enumerate(self.instance_commands):\n intf = cast(Dict[str, str], command.template_params).get(\"intf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not command_output[\"interfaces\"][intf][\"proxyArp\"]:\n disabled_intf.append(intf)\n\n if disabled_intf:\n self.result.is_failure(f\"The following interface(s) have Proxy-ARP disabled: {disabled_intf}\")\n\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIPProxyARP.test","title":"test()
","text":"Run VerifyIPProxyARP validation.
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyIPProxyARP validation.\n \"\"\"\n\n disabled_intf = []\n\n for index, command in enumerate(self.instance_commands):\n intf = cast(Dict[str, str], command.template_params).get(\"intf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not command_output[\"interfaces\"][intf][\"proxyArp\"]:\n disabled_intf.append(intf)\n\n if disabled_intf:\n self.result.is_failure(f\"The following interface(s) have Proxy-ARP disabled: {disabled_intf}\")\n\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIllegalLACP","title":"VerifyIllegalLACP
","text":" Bases: AntaTest
Verifies there is no illegal LACP packets received.
Source code in anta/tests/interfaces.py
class VerifyIllegalLACP(AntaTest):\n\"\"\"\n Verifies there is no illegal LACP packets received.\n \"\"\"\n\n name = \"VerifyIllegalLACP\"\n description = \"Verifies there is no illegal LACP packets received.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show lacp counters all-ports\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyIllegalLACP validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_illegal_lacp: List[Dict[str, Dict[str, int]]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n po_with_illegal_lacp.extend(\n {portchannel: interface} for interface, interface_dict in portchannel_dict[\"interfaces\"].items() if interface_dict[\"illegalRxCount\"] != 0\n )\n\n if not po_with_illegal_lacp:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following port-channels have recieved illegal lacp packets on the \" f\"following ports: {po_with_illegal_lacp}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIllegalLACP.test","title":"test()
","text":"Run VerifyIllegalLACP validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyIllegalLACP validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_illegal_lacp: List[Dict[str, Dict[str, int]]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n po_with_illegal_lacp.extend(\n {portchannel: interface} for interface, interface_dict in portchannel_dict[\"interfaces\"].items() if interface_dict[\"illegalRxCount\"] != 0\n )\n\n if not po_with_illegal_lacp:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following port-channels have recieved illegal lacp packets on the \" f\"following ports: {po_with_illegal_lacp}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceDiscards","title":"VerifyInterfaceDiscards
","text":" Bases: AntaTest
Verifies interfaces packet discard counters are equal to zero.
Source code in anta/tests/interfaces.py
class VerifyInterfaceDiscards(AntaTest):\n\"\"\"\n Verifies interfaces packet discard counters are equal to zero.\n \"\"\"\n\n name = \"VerifyInterfaceDiscards\"\n description = \"Verifies interfaces packet discard counters are equal to zero.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces counters discards\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceDiscards validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n\n for interface, outer_v in command_output[\"interfaces\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 discard counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceDiscards.test","title":"test()
","text":"Run VerifyInterfaceDiscards validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceDiscards validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n\n for interface, outer_v in command_output[\"interfaces\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 discard counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrDisabled","title":"VerifyInterfaceErrDisabled
","text":" Bases: AntaTest
Verifies there is no interface in error disable state.
Source code in anta/tests/interfaces.py
class VerifyInterfaceErrDisabled(AntaTest):\n\"\"\"\n Verifies there is no interface in error disable state.\n \"\"\"\n\n name = \"VerifyInterfaceErrDisabled\"\n description = \"Verifies there is no interface in error disable state.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces status\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceErrDisabled validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n errdisabled_interfaces = [interface for interface, value in command_output[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n\n if errdisabled_interfaces:\n self.result.is_failure(f\"The following interfaces are in error disabled state: {errdisabled_interfaces}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrDisabled.test","title":"test()
","text":"Run VerifyInterfaceErrDisabled validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceErrDisabled validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n errdisabled_interfaces = [interface for interface, value in command_output[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n\n if errdisabled_interfaces:\n self.result.is_failure(f\"The following interfaces are in error disabled state: {errdisabled_interfaces}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrors","title":"VerifyInterfaceErrors
","text":" Bases: AntaTest
Verifies interfaces error counters are equal to zero.
Source code in anta/tests/interfaces.py
class VerifyInterfaceErrors(AntaTest):\n\"\"\"\n Verifies interfaces error counters are equal to zero.\n \"\"\"\n\n name = \"VerifyInterfaceErrors\"\n description = \"Verifies interfaces error counters are equal to zero.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces counters errors\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n for interface, outer_v in command_output[\"interfaceErrorCounters\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 error counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrors.test","title":"test()
","text":"Run VerifyInterfaceUtilization validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n for interface, outer_v in command_output[\"interfaceErrorCounters\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 error counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceUtilization","title":"VerifyInterfaceUtilization
","text":" Bases: AntaTest
Verifies interfaces utilization is below 75%.
Source code in anta/tests/interfaces.py
class VerifyInterfaceUtilization(AntaTest):\n\"\"\"\n Verifies interfaces utilization is below 75%.\n \"\"\"\n\n name = \"VerifyInterfaceUtilization\"\n description = \"Verifies interfaces utilization is below 75%.\"\n categories = [\"interfaces\"]\n # TODO - move from text to json if possible\n commands = [AntaTestCommand(command=\"show interfaces counters rates\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n wrong_interfaces = {}\n for line in command_output.split(\"\\n\")[1:]:\n if len(line) > 0:\n if line.split()[-5] == \"-\" or line.split()[-2] == \"-\":\n pass\n elif float(line.split()[-5].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-5]\n elif float(line.split()[-2].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-2]\n\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have a usage > 75%: {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceUtilization.test","title":"test()
","text":"Run VerifyInterfaceUtilization validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n wrong_interfaces = {}\n for line in command_output.split(\"\\n\")[1:]:\n if len(line) > 0:\n if line.split()[-5] == \"-\" or line.split()[-2] == \"-\":\n pass\n elif float(line.split()[-5].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-5]\n elif float(line.split()[-2].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-2]\n\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have a usage > 75%: {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfacesStatus","title":"VerifyInterfacesStatus
","text":" Bases: AntaTest
Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.
Source code in anta/tests/interfaces.py
class VerifyInterfacesStatus(AntaTest):\n\"\"\"\n Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.\n \"\"\"\n\n name = \"VerifyInterfacesStatus\"\n description = \"Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces description\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyInterfacesStatus validation\n\n Args:\n minimum: Expected minimum number of Ethernet interfaces up/up.\n \"\"\"\n\n if minimum is None or minimum < 0:\n self.result.is_skipped(f\"VerifyInterfacesStatus was not run as an invalid minimum value was given {minimum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n count_up_up = 0\n other_ethernet_interfaces = []\n\n for interface in command_output[\"interfaceDescriptions\"]:\n interface_dict = command_output[\"interfaceDescriptions\"][interface]\n if \"Ethernet\" in interface:\n if re.match(r\"connected|up\", interface_dict[\"lineProtocolStatus\"]) and re.match(r\"connected|up\", interface_dict[\"interfaceStatus\"]):\n count_up_up += 1\n else:\n other_ethernet_interfaces.append(interface)\n\n if count_up_up >= minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Only {count_up_up}, less than {minimum} Ethernet interfaces are UP/UP\")\n self.result.messages.append(f\"The following Ethernet interfaces are not UP/UP: {other_ethernet_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfacesStatus.test","title":"test(minimum=None)
","text":"Run VerifyInterfacesStatus validation
Parameters:
Name Type Description Default minimum
Optional[int]
Expected minimum number of Ethernet interfaces up/up.
None
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyInterfacesStatus validation\n\n Args:\n minimum: Expected minimum number of Ethernet interfaces up/up.\n \"\"\"\n\n if minimum is None or minimum < 0:\n self.result.is_skipped(f\"VerifyInterfacesStatus was not run as an invalid minimum value was given {minimum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n count_up_up = 0\n other_ethernet_interfaces = []\n\n for interface in command_output[\"interfaceDescriptions\"]:\n interface_dict = command_output[\"interfaceDescriptions\"][interface]\n if \"Ethernet\" in interface:\n if re.match(r\"connected|up\", interface_dict[\"lineProtocolStatus\"]) and re.match(r\"connected|up\", interface_dict[\"interfaceStatus\"]):\n count_up_up += 1\n else:\n other_ethernet_interfaces.append(interface)\n\n if count_up_up >= minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Only {count_up_up}, less than {minimum} Ethernet interfaces are UP/UP\")\n self.result.messages.append(f\"The following Ethernet interfaces are not UP/UP: {other_ethernet_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyL3MTU","title":"VerifyL3MTU
","text":" Bases: AntaTest
Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.
Expected Results - success: The test will pass if all layer 3 interfaces have the proper MTU configured.
- failure: The test will fail if one or many layer 3 interfaces have the wrong MTU configured.
- skipped: The test will be skipped if the MTU value is not provided.
Limitations - Only Ethernet, Port-Channel, Vlan interfaces are supported.
- Other interface types, like Management, Loopback, Vxlan, Tunnel are currently not supported.
https://www.arista.com/en/support/toi/eos-4-23-1f/14388-global-knob-to-set-mtu-for-all-layer-3-interfaces
Source code in anta/tests/interfaces.py
class VerifyL3MTU(AntaTest):\n\"\"\"\n Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.\n\n Expected Results:\n * success: The test will pass if all layer 3 interfaces have the proper MTU configured.\n * failure: The test will fail if one or many layer 3 interfaces have the wrong MTU configured.\n * skipped: The test will be skipped if the MTU value is not provided.\n\n Limitations:\n * Only Ethernet, Port-Channel, Vlan interfaces are supported.\n * Other interface types, like Management, Loopback, Vxlan, Tunnel are currently not supported.\n\n https://www.arista.com/en/support/toi/eos-4-23-1f/14388-global-knob-to-set-mtu-for-all-layer-3-interfaces\n\n \"\"\"\n\n name = \"VerifyL3MTU\"\n description = \"Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces\")]\n\n NOT_SUPPORTED_INTERFACES: List[str] = [\"Management\", \"Loopback\", \"Vxlan\", \"Tunnel\"]\n\n @AntaTest.anta_test\n def test(self, mtu: int = 1500) -> None:\n\"\"\"\n Run VerifyL3MTU validation\n\n Args:\n mtu: Layer 3 MTU to verify. Defaults to 1500.\n\n \"\"\"\n\n if not mtu:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mtu was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_l3mtu_intf = []\n\n for interface, values in command_output[\"interfaces\"].items():\n if re.sub(r\"\\d+$\", \"\", interface) not in self.NOT_SUPPORTED_INTERFACES:\n if values[\"forwardingModel\"] == \"routed\" and values[\"mtu\"] != mtu:\n wrong_l3mtu_intf.append(interface)\n\n if not wrong_l3mtu_intf:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"The following interface(s) have the wrong MTU configured: {wrong_l3mtu_intf}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyL3MTU.test","title":"test(mtu=1500)
","text":"Run VerifyL3MTU validation
Parameters:
Name Type Description Default mtu
int
Layer 3 MTU to verify. Defaults to 1500.
1500
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, mtu: int = 1500) -> None:\n\"\"\"\n Run VerifyL3MTU validation\n\n Args:\n mtu: Layer 3 MTU to verify. Defaults to 1500.\n\n \"\"\"\n\n if not mtu:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mtu was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_l3mtu_intf = []\n\n for interface, values in command_output[\"interfaces\"].items():\n if re.sub(r\"\\d+$\", \"\", interface) not in self.NOT_SUPPORTED_INTERFACES:\n if values[\"forwardingModel\"] == \"routed\" and values[\"mtu\"] != mtu:\n wrong_l3mtu_intf.append(interface)\n\n if not wrong_l3mtu_intf:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"The following interface(s) have the wrong MTU configured: {wrong_l3mtu_intf}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyLoopbackCount","title":"VerifyLoopbackCount
","text":" Bases: AntaTest
Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.
Source code in anta/tests/interfaces.py
class VerifyLoopbackCount(AntaTest):\n\"\"\"\n Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.\n \"\"\"\n\n name = \"VerifyLoopbackCount\"\n description = \"Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show ip interface brief\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyLoopbackCount validation\n\n Args:\n number: Number of loopback interfaces expected to be present.\n \"\"\"\n\n if number is None:\n self.result.is_skipped(\"VerifyLoopbackCount was not run as no number value was given.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n loopback_count = 0\n down_loopback_interfaces = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Loopback\" in interface:\n loopback_count += 1\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_loopback_interfaces.append(interface)\n\n if loopback_count == number and len(down_loopback_interfaces) == 0:\n self.result.is_success()\n else:\n self.result.is_failure()\n if loopback_count != number:\n self.result.is_failure(f\"Found {loopback_count} Loopbacks when expecting {number}\")\n elif len(down_loopback_interfaces) != 0:\n self.result.is_failure(f\"The following Loopbacks are not up: {down_loopback_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyLoopbackCount.test","title":"test(number=None)
","text":"Run VerifyLoopbackCount validation
Parameters:
Name Type Description Default number
Optional[int]
Number of loopback interfaces expected to be present.
None
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyLoopbackCount validation\n\n Args:\n number: Number of loopback interfaces expected to be present.\n \"\"\"\n\n if number is None:\n self.result.is_skipped(\"VerifyLoopbackCount was not run as no number value was given.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n loopback_count = 0\n down_loopback_interfaces = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Loopback\" in interface:\n loopback_count += 1\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_loopback_interfaces.append(interface)\n\n if loopback_count == number and len(down_loopback_interfaces) == 0:\n self.result.is_success()\n else:\n self.result.is_failure()\n if loopback_count != number:\n self.result.is_failure(f\"Found {loopback_count} Loopbacks when expecting {number}\")\n elif len(down_loopback_interfaces) != 0:\n self.result.is_failure(f\"The following Loopbacks are not up: {down_loopback_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyPortChannels","title":"VerifyPortChannels
","text":" Bases: AntaTest
Verifies there is no inactive port in port channels.
Source code in anta/tests/interfaces.py
class VerifyPortChannels(AntaTest):\n\"\"\"\n Verifies there is no inactive port in port channels.\n \"\"\"\n\n name = \"VerifyPortChannels\"\n description = \"Verifies there is no inactive port in port channels.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show port-channel\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyPortChannels validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_invactive_ports: List[Dict[str, str]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n if len(portchannel_dict[\"inactivePorts\"]) != 0:\n po_with_invactive_ports.extend({portchannel: portchannel_dict[\"inactivePorts\"]})\n\n if not po_with_invactive_ports:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following port-channels have inactive port(s): {po_with_invactive_ports}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyPortChannels.test","title":"test()
","text":"Run VerifyPortChannels validation
Source code in anta/tests/interfaces.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyPortChannels validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_invactive_ports: List[Dict[str, str]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n if len(portchannel_dict[\"inactivePorts\"]) != 0:\n po_with_invactive_ports.extend({portchannel: portchannel_dict[\"inactivePorts\"]})\n\n if not po_with_invactive_ports:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following port-channels have inactive port(s): {po_with_invactive_ports}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifySVI","title":"VerifySVI
","text":" Bases: AntaTest
Verifies there is no interface vlan down.
Source code in anta/tests/interfaces.py
class VerifySVI(AntaTest):\n\"\"\"\n Verifies there is no interface vlan down.\n \"\"\"\n\n name = \"VerifySVI\"\n description = \"Verifies there is no interface vlan down.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show ip interface brief\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifySVI validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n down_svis = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Vlan\" in interface:\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_svis.append(interface)\n\n if len(down_svis) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following SVIs are not up: {down_svis}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifySVI.test","title":"test()
","text":"Run VerifySVI validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifySVI validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n down_svis = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Vlan\" in interface:\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_svis.append(interface)\n\n if len(down_svis) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following SVIs are not up: {down_svis}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyStormControlDrops","title":"VerifyStormControlDrops
","text":" Bases: AntaTest
Verifies the device did not drop packets due its to storm-control configuration.
Source code in anta/tests/interfaces.py
class VerifyStormControlDrops(AntaTest):\n\"\"\"\n Verifies the device did not drop packets due its to storm-control configuration.\n \"\"\"\n\n name = \"VerifyStormControlDrops\"\n description = \"Verifies the device did not drop packets due its to storm-control configuration.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show storm-control\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyStormControlDrops validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n storm_controlled_interfaces: Dict[str, Dict[str, Any]] = {}\n for interface, interface_dict in command_output[\"interfaces\"].items():\n for traffic_type, traffic_type_dict in interface_dict[\"trafficTypes\"].items():\n if \"drop\" in traffic_type_dict and traffic_type_dict[\"drop\"] != 0:\n storm_controlled_interface_dict = storm_controlled_interfaces.setdefault(interface, {})\n storm_controlled_interface_dict.update({traffic_type: traffic_type_dict[\"drop\"]})\n\n if not storm_controlled_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have none 0 storm-control drop counters {storm_controlled_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyStormControlDrops.test","title":"test()
","text":"Run VerifyStormControlDrops validation
Source code in anta/tests/interfaces.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyStormControlDrops validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n storm_controlled_interfaces: Dict[str, Dict[str, Any]] = {}\n for interface, interface_dict in command_output[\"interfaces\"].items():\n for traffic_type, traffic_type_dict in interface_dict[\"trafficTypes\"].items():\n if \"drop\" in traffic_type_dict and traffic_type_dict[\"drop\"] != 0:\n storm_controlled_interface_dict = storm_controlled_interfaces.setdefault(interface, {})\n storm_controlled_interface_dict.update({traffic_type: traffic_type_dict[\"drop\"]})\n\n if not storm_controlled_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have none 0 storm-control drop counters {storm_controlled_interfaces}\")\n
"},{"location":"api/tests.logging/","title":"Logging","text":""},{"location":"api/tests.logging/#anta-catalog-for-logging-tests","title":"ANTA catalog for logging tests","text":"Test functions related to the EOS various logging settings
NOTE: \u2018show logging\u2019 does not support json output yet
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingAccounting","title":"VerifyLoggingAccounting
","text":" Bases: AntaTest
Verifies if AAA accounting logs are generated.
Expected Results - success: The test will pass if AAA accounting logs are generated.
- failure: The test will fail if AAA accounting logs are NOT generated.
Source code in anta/tests/logging.py
class VerifyLoggingAccounting(AntaTest):\n\"\"\"\n Verifies if AAA accounting logs are generated.\n\n Expected Results:\n * success: The test will pass if AAA accounting logs are generated.\n * failure: The test will fail if AAA accounting logs are NOT generated.\n \"\"\"\n\n name = \"VerifyLoggingAccounting\"\n description = \"Verifies if AAA accounting logs are generated.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show aaa accounting logs | tail\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingAccountingvalidation.\n \"\"\"\n pattern = r\"cmd=show aaa accounting logs\"\n output = cast(str, self.instance_commands[0].output)\n\n if re.search(pattern, output):\n self.result.is_success()\n else:\n self.result.is_failure(\"AAA accounting logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingAccounting.test","title":"test()
","text":"Run VerifyLoggingAccountingvalidation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingAccountingvalidation.\n \"\"\"\n pattern = r\"cmd=show aaa accounting logs\"\n output = cast(str, self.instance_commands[0].output)\n\n if re.search(pattern, output):\n self.result.is_success()\n else:\n self.result.is_failure(\"AAA accounting logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHostname","title":"VerifyLoggingHostname
","text":" Bases: AntaTest
Verifies if logs are generated with the device FQDN.
Expected Results - success: The test will pass if logs are generated with the device FQDN.
- failure: The test will fail if logs are NOT generated with the device FQDN.
Source code in anta/tests/logging.py
class VerifyLoggingHostname(AntaTest):\n\"\"\"\n Verifies if logs are generated with the device FQDN.\n\n Expected Results:\n * success: The test will pass if logs are generated with the device FQDN.\n * failure: The test will fail if logs are NOT generated with the device FQDN.\n \"\"\"\n\n name = \"VerifyLoggingHostname\"\n description = \"Verifies if logs are generated with the device FQDN.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"show hostname\"),\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingHostname validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingHostname validation.\n \"\"\"\n output_hostname = cast(Dict[str, Any], self.instance_commands[0].output)\n output_logging = cast(str, self.instance_commands[2].output)\n fqdn = output_hostname[\"fqdn\"]\n lines = output_logging.strip().split(\"\\n\")[::-1]\n\n log_pattern = r\"ANTA VerifyLoggingHostname validation\"\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if fqdn in last_line_with_pattern:\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the device FQDN\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHostname.test","title":"test()
","text":"Run VerifyLoggingHostname validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingHostname validation.\n \"\"\"\n output_hostname = cast(Dict[str, Any], self.instance_commands[0].output)\n output_logging = cast(str, self.instance_commands[2].output)\n fqdn = output_hostname[\"fqdn\"]\n lines = output_logging.strip().split(\"\\n\")[::-1]\n\n log_pattern = r\"ANTA VerifyLoggingHostname validation\"\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if fqdn in last_line_with_pattern:\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the device FQDN\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHosts","title":"VerifyLoggingHosts
","text":" Bases: AntaTest
Verifies logging hosts (syslog servers) for a specified VRF.
Expected Results - success: The test will pass if the provided syslog servers are configured in the specified VRF.
- failure: The test will fail if the provided syslog servers are NOT configured in the specified VRF.
- skipped: The test will be skipped if syslog servers or VRF are not provided.
Source code in anta/tests/logging.py
class VerifyLoggingHosts(AntaTest):\n\"\"\"\n Verifies logging hosts (syslog servers) for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided syslog servers are configured in the specified VRF.\n * failure: The test will fail if the provided syslog servers are NOT configured in the specified VRF.\n * skipped: The test will be skipped if syslog servers or VRF are not provided.\n \"\"\"\n\n name = \"VerifyLoggingHosts\"\n description = \"Verifies logging hosts (syslog servers) for a specified VRF.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show logging\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self, hosts: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingHosts validation.\n\n Args:\n hosts: List of hosts (syslog servers) IP addresses.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not hosts or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because hosts or vrf were not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n not_configured = []\n\n for host in hosts:\n pattern = rf\"Logging to '{host}'.*VRF {vrf}\"\n if not re.search(pattern, _get_logging_states(output)):\n not_configured.append(host)\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Syslog servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHosts.test","title":"test(hosts=None, vrf='default')
","text":"Run VerifyLoggingHosts validation.
Parameters:
Name Type Description Default hosts
Optional[List[str]]
List of hosts (syslog servers) IP addresses.
None
vrf
str
The name of the VRF to transport log messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self, hosts: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingHosts validation.\n\n Args:\n hosts: List of hosts (syslog servers) IP addresses.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not hosts or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because hosts or vrf were not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n not_configured = []\n\n for host in hosts:\n pattern = rf\"Logging to '{host}'.*VRF {vrf}\"\n if not re.search(pattern, _get_logging_states(output)):\n not_configured.append(host)\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Syslog servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingLogsGeneration","title":"VerifyLoggingLogsGeneration
","text":" Bases: AntaTest
Verifies if logs are generated.
Expected Results - success: The test will pass if logs are generated.
- failure: The test will fail if logs are NOT generated.
Source code in anta/tests/logging.py
class VerifyLoggingLogsGeneration(AntaTest):\n\"\"\"\n Verifies if logs are generated.\n\n Expected Results:\n * success: The test will pass if logs are generated.\n * failure: The test will fail if logs are NOT generated.\n \"\"\"\n\n name = \"VerifyLoggingLogsGeneration\"\n description = \"Verifies if logs are generated.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingLogsGeneration validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingLogs validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingLogsGeneration validation\"\n\n output = cast(str, self.instance_commands[1].output)\n lines = output.strip().split(\"\\n\")[::-1]\n\n for line in lines:\n if re.search(log_pattern, line):\n self.result.is_success()\n return\n\n self.result.is_failure(\"Logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingLogsGeneration.test","title":"test()
","text":"Run VerifyLoggingLogs validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingLogs validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingLogsGeneration validation\"\n\n output = cast(str, self.instance_commands[1].output)\n lines = output.strip().split(\"\\n\")[::-1]\n\n for line in lines:\n if re.search(log_pattern, line):\n self.result.is_success()\n return\n\n self.result.is_failure(\"Logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingPersistent","title":"VerifyLoggingPersistent
","text":" Bases: AntaTest
Verifies if logging persistent is enabled and logs are saved in flash.
Expected Results - success: The test will pass if logging persistent is enabled and logs are in flash.
- failure: The test will fail if logging persistent is disabled or no logs are saved in flash.
Source code in anta/tests/logging.py
class VerifyLoggingPersistent(AntaTest):\n\"\"\"\n Verifies if logging persistent is enabled and logs are saved in flash.\n\n Expected Results:\n * success: The test will pass if logging persistent is enabled and logs are in flash.\n * failure: The test will fail if logging persistent is disabled or no logs are saved in flash.\n \"\"\"\n\n name = \"VerifyLoggingPersistent\"\n description = \"Verifies if logging persistent is enabled and logs are saved in flash.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"show logging\", ofmt=\"text\"),\n AntaTestCommand(command=\"dir flash:/persist/messages\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingPersistent validation.\n \"\"\"\n self.result.is_success()\n\n log_output = cast(str, self.instance_commands[0].output)\n dir_flash_output = cast(str, self.instance_commands[1].output)\n\n if \"Persistent logging: disabled\" in _get_logging_states(log_output):\n self.result.is_failure(\"Persistent logging is disabled\")\n return\n\n pattern = r\"-rw-\\s+(\\d+)\"\n persist_logs = re.search(pattern, dir_flash_output)\n\n if not persist_logs or int(persist_logs.group(1)) == 0:\n self.result.is_failure(\"No persistent logs are saved in flash\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingPersistent.test","title":"test()
","text":"Run VerifyLoggingPersistent validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingPersistent validation.\n \"\"\"\n self.result.is_success()\n\n log_output = cast(str, self.instance_commands[0].output)\n dir_flash_output = cast(str, self.instance_commands[1].output)\n\n if \"Persistent logging: disabled\" in _get_logging_states(log_output):\n self.result.is_failure(\"Persistent logging is disabled\")\n return\n\n pattern = r\"-rw-\\s+(\\d+)\"\n persist_logs = re.search(pattern, dir_flash_output)\n\n if not persist_logs or int(persist_logs.group(1)) == 0:\n self.result.is_failure(\"No persistent logs are saved in flash\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingSourceIntf","title":"VerifyLoggingSourceIntf
","text":" Bases: AntaTest
Verifies logging source-interface for a specified VRF.
Expected Results - success: The test will pass if the provided logging source-interface is configured in the specified VRF.
- failure: The test will fail if the provided logging source-interface is NOT configured in the specified VRF.
- skipped: The test will be skipped if source-interface or VRF is not provided.
Source code in anta/tests/logging.py
class VerifyLoggingSourceIntf(AntaTest):\n\"\"\"\n Verifies logging source-interface for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided logging source-interface is configured in the specified VRF.\n * failure: The test will fail if the provided logging source-interface is NOT configured in the specified VRF.\n * skipped: The test will be skipped if source-interface or VRF is not provided.\n \"\"\"\n\n name = \"VerifyLoggingSourceInt\"\n description = \"Verifies logging source-interface for a specified VRF.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show logging\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingSrcDst validation.\n\n Args:\n intf: Source-interface to use as source IP of log messages.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n pattern = rf\"Logging source-interface '{intf}'.*VRF {vrf}\"\n\n if re.search(pattern, _get_logging_states(output)):\n self.result.is_success()\n else:\n self.result.is_failure(f\"Source-interface '{intf}' is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingSourceIntf.test","title":"test(intf=None, vrf='default')
","text":"Run VerifyLoggingSrcDst validation.
Parameters:
Name Type Description Default intf
Optional[str]
Source-interface to use as source IP of log messages.
None
vrf
str
The name of the VRF to transport log messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingSrcDst validation.\n\n Args:\n intf: Source-interface to use as source IP of log messages.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n pattern = rf\"Logging source-interface '{intf}'.*VRF {vrf}\"\n\n if re.search(pattern, _get_logging_states(output)):\n self.result.is_success()\n else:\n self.result.is_failure(f\"Source-interface '{intf}' is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingTimestamp","title":"VerifyLoggingTimestamp
","text":" Bases: AntaTest
Verifies if logs are generated with the approprate timestamp.
Expected Results - success: The test will pass if logs are generated with the appropriated timestamp.
- failure: The test will fail if logs are NOT generated with the appropriated timestamp.
Source code in anta/tests/logging.py
class VerifyLoggingTimestamp(AntaTest):\n\"\"\"\n Verifies if logs are generated with the approprate timestamp.\n\n Expected Results:\n * success: The test will pass if logs are generated with the appropriated timestamp.\n * failure: The test will fail if logs are NOT generated with the appropriated timestamp.\n \"\"\"\n\n name = \"VerifyLoggingTimestamp\"\n description = \"Verifies if logs are generated with the appropriate timestamp.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingTimestamp validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingTimestamp validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingTimestamp validation\"\n timestamp_pattern = r\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}-\\d{2}:\\d{2}\"\n\n output = cast(str, self.instance_commands[1].output)\n\n lines = output.strip().split(\"\\n\")[::-1]\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if re.search(timestamp_pattern, last_line_with_pattern):\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the appropriate timestamp format\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingTimestamp.test","title":"test()
","text":"Run VerifyLoggingTimestamp validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingTimestamp validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingTimestamp validation\"\n timestamp_pattern = r\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}-\\d{2}:\\d{2}\"\n\n output = cast(str, self.instance_commands[1].output)\n\n lines = output.strip().split(\"\\n\")[::-1]\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if re.search(timestamp_pattern, last_line_with_pattern):\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the appropriate timestamp format\")\n
"},{"location":"api/tests/","title":"Overview","text":""},{"location":"api/tests/#anta-tests-landing-page","title":"ANTA Tests landing page","text":"This section describes all the available tests provided by ANTA package.
- AAA
- Configuration
- Connectivity
- Field Notice
- Hardware
- Interfaces
- Logging
- MLAG
- Multicast
- Profiles
- Routing Generic
- Routing BGP
- Routing OSPF
- Security
- SNMP
- Software
- STP
- System
- VxLAN
All these tests can be imported in a catalog to be used by the anta cli or in your own framework
"},{"location":"api/tests.mlag/","title":"MLAG","text":""},{"location":"api/tests.mlag/#anta-catalog-for-mlag-tests","title":"ANTA catalog for mlag tests","text":"Test functions related to Multi-Chassis LAG
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagConfigSanity","title":"VerifyMlagConfigSanity
","text":" Bases: AntaTest
Verifies there are no MLAG config-sanity inconsistencies.
Source code in anta/tests/mlag.py
class VerifyMlagConfigSanity(AntaTest):\n\"\"\"\n Verifies there are no MLAG config-sanity inconsistencies.\n \"\"\"\n\n name = \"VerifyMlagConfigSanity\"\n description = \"Verifies there are no MLAG config-sanity inconsistencies.\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag config-sanity\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"mlagActive\" not in command_output.keys():\n self.result.is_error(\"Incorrect JSON response - mlagActive state not found\")\n elif command_output[\"mlagActive\"] is False:\n self.result.is_skipped(\"MLAG is disabled\")\n elif len(command_output[\"globalConfiguration\"]) > 0 or len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure()\n if len(command_output[\"globalConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Global inconsistancies: \" f\"{command_output['globalConfiguration']}\")\n if len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Interface inconsistancies: \" f\"{command_output['interfaceConfiguration']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagConfigSanity.test","title":"test()
","text":"Run VerifyMlagConfigSanity validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"mlagActive\" not in command_output.keys():\n self.result.is_error(\"Incorrect JSON response - mlagActive state not found\")\n elif command_output[\"mlagActive\"] is False:\n self.result.is_skipped(\"MLAG is disabled\")\n elif len(command_output[\"globalConfiguration\"]) > 0 or len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure()\n if len(command_output[\"globalConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Global inconsistancies: \" f\"{command_output['globalConfiguration']}\")\n if len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Interface inconsistancies: \" f\"{command_output['interfaceConfiguration']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagInterfaces","title":"VerifyMlagInterfaces
","text":" Bases: AntaTest
Verifies there are no inactive or active-partial MLAG interfaces.
Source code in anta/tests/mlag.py
class VerifyMlagInterfaces(AntaTest):\n\"\"\"\n Verifies there are no inactive or active-partial MLAG interfaces.\n \"\"\"\n\n name = \"VerifyMlagInterfaces\"\n description = \"Verifies there are no inactive or active-partial MLAG interfaces.\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagInterfaces validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif command_output[\"mlagPorts\"][\"Inactive\"] != 0 or command_output[\"mlagPorts\"][\"Active-partial\"] != 0:\n self.result.is_failure(f\"MLAG status is not OK: {command_output['mlagPorts']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagInterfaces.test","title":"test()
","text":"Run VerifyMlagInterfaces validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagInterfaces validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif command_output[\"mlagPorts\"][\"Inactive\"] != 0 or command_output[\"mlagPorts\"][\"Active-partial\"] != 0:\n self.result.is_failure(f\"MLAG status is not OK: {command_output['mlagPorts']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagStatus","title":"VerifyMlagStatus
","text":" Bases: AntaTest
Verifies if MLAG us running, and if the status is good state is active, negotiation status is connected, local int is up, peer link is up.
Source code in anta/tests/mlag.py
class VerifyMlagStatus(AntaTest):\n\"\"\"\n Verifies if MLAG us running, and if the status is good\n state is active, negotiation status is connected, local int is up, peer link is up.\n \"\"\"\n\n name = \"VerifyMlagStatus\"\n description = \"Verifies MLAG status\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagStatus validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif (\n command_output[\"state\"] != \"active\"\n or command_output[\"negStatus\"] != \"connected\"\n or command_output[\"localIntfStatus\"] != \"up\"\n or command_output[\"peerLinkStatus\"] != \"up\"\n ):\n self.result.is_failure(f\"MLAG status is not OK: {command_output}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagStatus.test","title":"test()
","text":"Run VerifyMlagStatus validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagStatus validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif (\n command_output[\"state\"] != \"active\"\n or command_output[\"negStatus\"] != \"connected\"\n or command_output[\"localIntfStatus\"] != \"up\"\n or command_output[\"peerLinkStatus\"] != \"up\"\n ):\n self.result.is_failure(f\"MLAG status is not OK: {command_output}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.multicast/","title":"Multicast","text":""},{"location":"api/tests.multicast/#anta-catalog-for-multicast-tests","title":"ANTA catalog for multicast tests","text":"Test functions related to multicast
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingGlobal","title":"VerifyIGMPSnoopingGlobal
","text":" Bases: AntaTest
Verifies the IGMP snooping global configuration.
Parameters:
Name Type Description Default configuration
str
Expected global IGMP snooping configuration (enabled or disabled).
required Source code in anta/tests/multicast.py
class VerifyIGMPSnoopingGlobal(AntaTest):\n\"\"\"\n Verifies the IGMP snooping global configuration.\n\n Args:\n configuration (str): Expected global IGMP snooping configuration (enabled or disabled).\n \"\"\"\n\n name = \"VerifyIGMPSnoopingGlobal\"\n description = \"Verifies the IGMP snooping global configuration.\"\n categories = [\"multicast\", \"igmp\"]\n commands = [AntaTestCommand(command=\"show ip igmp snooping\")]\n\n @AntaTest.anta_test\n def test(self, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingGlobal validation\n\n Args:\n configuration: Expected global IGMP configuration (enabled or disabled).\n \"\"\"\n\n if not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingGlobal was not run as no configuration was given\")\n return\n\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingGlobal was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n if (igmp_state := command_output[\"igmpSnoopingState\"]) != configuration:\n self.result.is_failure(f\"IGMP state is not valid: {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingGlobal.test","title":"test(configuration=None)
","text":"Run VerifyIGMPSnoopingGlobal validation
Parameters:
Name Type Description Default configuration
Optional[str]
Expected global IGMP configuration (enabled or disabled).
None
Source code in anta/tests/multicast.py
@AntaTest.anta_test\ndef test(self, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingGlobal validation\n\n Args:\n configuration: Expected global IGMP configuration (enabled or disabled).\n \"\"\"\n\n if not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingGlobal was not run as no configuration was given\")\n return\n\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingGlobal was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n if (igmp_state := command_output[\"igmpSnoopingState\"]) != configuration:\n self.result.is_failure(f\"IGMP state is not valid: {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingVlans","title":"VerifyIGMPSnoopingVlans
","text":" Bases: AntaTest
Verifies the IGMP snooping configuration for some VLANs.
Parameters:
Name Type Description Default vlans
List[str]
A list of VLANs
required configuration
str
Expected IGMP snooping configuration (enabled or disabled) for these VLANs.
required Source code in anta/tests/multicast.py
class VerifyIGMPSnoopingVlans(AntaTest):\n\"\"\"\n Verifies the IGMP snooping configuration for some VLANs.\n\n Args:\n vlans (List[str]): A list of VLANs\n configuration (str): Expected IGMP snooping configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n name = \"VerifyIGMPSnoopingVlans\"\n description = \"Verifies the IGMP snooping configuration for some VLANs.\"\n categories = [\"multicast\", \"igmp\"]\n commands = [AntaTestCommand(command=\"show ip igmp snooping\")]\n\n @AntaTest.anta_test\n def test(self, vlans: Optional[List[str]] = None, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingVlans validation\n\n Args:\n vlans: List of VLANs.\n configuration: Expected IGMP configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n if not vlans or not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingVlans was not run as no vlans or configuration was given\")\n return\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingVlans was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n for vlan in vlans:\n if vlan not in command_output[\"vlans\"]:\n self.result.is_failure(f\"Supplied vlan {vlan} is not present on the device.\")\n continue\n\n igmp_state = command_output[\"vlans\"][str(vlan)][\"igmpSnoopingState\"]\n if igmp_state != configuration:\n self.result.is_failure(f\"IGMP state for vlan {vlan} is {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingVlans.test","title":"test(vlans=None, configuration=None)
","text":"Run VerifyIGMPSnoopingVlans validation
Parameters:
Name Type Description Default vlans
Optional[List[str]]
List of VLANs.
None
configuration
Optional[str]
Expected IGMP configuration (enabled or disabled) for these VLANs.
None
Source code in anta/tests/multicast.py
@AntaTest.anta_test\ndef test(self, vlans: Optional[List[str]] = None, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingVlans validation\n\n Args:\n vlans: List of VLANs.\n configuration: Expected IGMP configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n if not vlans or not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingVlans was not run as no vlans or configuration was given\")\n return\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingVlans was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n for vlan in vlans:\n if vlan not in command_output[\"vlans\"]:\n self.result.is_failure(f\"Supplied vlan {vlan} is not present on the device.\")\n continue\n\n igmp_state = command_output[\"vlans\"][str(vlan)][\"igmpSnoopingState\"]\n if igmp_state != configuration:\n self.result.is_failure(f\"IGMP state for vlan {vlan} is {igmp_state}\")\n
"},{"location":"api/tests.profiles/","title":"Profiles","text":""},{"location":"api/tests.profiles/#anta-catalog-for-profiles-tests","title":"ANTA catalog for profiles tests","text":"Test functions related to ASIC profiles
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyTcamProfile","title":"VerifyTcamProfile
","text":" Bases: AntaTest
Verifies the device is using the configured TCAM profile.
Source code in anta/tests/profiles.py
class VerifyTcamProfile(AntaTest):\n\"\"\"\n Verifies the device is using the configured TCAM profile.\n \"\"\"\n\n name = \"VerifyTcamProfile\"\n description = \"Verify that the assigned TCAM profile is actually running on the device\"\n categories = [\"profiles\"]\n commands = [AntaTestCommand(command=\"show hardware tcam profile\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyTcamProfile validation\n\n Args:\n profile: Expected TCAM profile.\n \"\"\"\n if not profile:\n self.result.is_skipped(\"VerifyTcamProfile was not run as no profile was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"pmfProfiles\"][\"FixedSystem\"][\"status\"] == command_output[\"pmfProfiles\"][\"FixedSystem\"][\"config\"] == profile:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Incorrect profile running on device: {command_output['pmfProfiles']['FixedSystem']['status']}\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyTcamProfile.test","title":"test(profile=None)
","text":"Run VerifyTcamProfile validation
Parameters:
Name Type Description Default profile
Optional[str]
Expected TCAM profile.
None
Source code in anta/tests/profiles.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyTcamProfile validation\n\n Args:\n profile: Expected TCAM profile.\n \"\"\"\n if not profile:\n self.result.is_skipped(\"VerifyTcamProfile was not run as no profile was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"pmfProfiles\"][\"FixedSystem\"][\"status\"] == command_output[\"pmfProfiles\"][\"FixedSystem\"][\"config\"] == profile:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Incorrect profile running on device: {command_output['pmfProfiles']['FixedSystem']['status']}\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyUnifiedForwardingTableMode","title":"VerifyUnifiedForwardingTableMode
","text":" Bases: AntaTest
Verifies the device is using the expected Unified Forwarding Table mode.
Source code in anta/tests/profiles.py
class VerifyUnifiedForwardingTableMode(AntaTest):\n\"\"\"\n Verifies the device is using the expected Unified Forwarding Table mode.\n \"\"\"\n\n name = \"VerifyUnifiedForwardingTableMode\"\n description = \"\"\n categories = [\"profiles\"]\n commands = [AntaTestCommand(command=\"show platform trident forwarding-table partition\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, mode: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyUnifiedForwardingTableMode validation\n\n Args:\n mode: Expected UFT mode.\n \"\"\"\n if not mode:\n self.result.is_skipped(\"VerifyUnifiedForwardingTableMode was not run as no mode was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"uftMode\"] == mode:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device is not running correct UFT mode (expected: {mode} / running: {command_output['uftMode']})\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyUnifiedForwardingTableMode.test","title":"test(mode=None)
","text":"Run VerifyUnifiedForwardingTableMode validation
Parameters:
Name Type Description Default mode
Optional[str]
Expected UFT mode.
None
Source code in anta/tests/profiles.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, mode: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyUnifiedForwardingTableMode validation\n\n Args:\n mode: Expected UFT mode.\n \"\"\"\n if not mode:\n self.result.is_skipped(\"VerifyUnifiedForwardingTableMode was not run as no mode was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"uftMode\"] == mode:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device is not running correct UFT mode (expected: {mode} / running: {command_output['uftMode']})\")\n
"},{"location":"api/tests.routing.bgp/","title":"BGP","text":""},{"location":"api/tests.routing.bgp/#anta-catalog-for-routing-bgp-tests","title":"ANTA catalog for routing-bgp tests","text":"BGP test functions
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNCount","title":"VerifyBGPEVPNCount
","text":" Bases: AntaTest
Verifies all EVPN BGP sessions are established (default VRF) and the actual number of BGP EVPN neighbors is the one we expect (default VRF).
- self.result = \u201cskipped\u201d if the
number
parameter is missing - self.result = \u201csuccess\u201d if all EVPN BGP sessions are Established and if the actual number of BGP EVPN neighbors is the one we expect.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPEVPNCount(AntaTest):\n\"\"\"\n Verifies all EVPN BGP sessions are established (default VRF)\n and the actual number of BGP EVPN neighbors is the one we expect (default VRF).\n\n * self.result = \"skipped\" if the `number` parameter is missing\n * self.result = \"success\" if all EVPN BGP sessions are Established and if the actual\n number of BGP EVPN neighbors is the one we expect.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPEVPNCount\"\n description = \"Verifies all EVPN BGP sessions are established (default VRF) and the actual number of BGP EVPN neighbors is the one we expect (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp evpn summary\")]\n\n @check_bgp_family_enable(\"evpn\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPEVPNCount validation\n\n Args:\n number: The expected number of BGP EVPN neighbors in the default VRF.\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPEVPNCount could not run because number was not supplied.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP EVPN peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNCount.test","title":"test(number=None)
","text":"Run VerifyBGPEVPNCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP EVPN neighbors in the default VRF.
None
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"evpn\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPEVPNCount validation\n\n Args:\n number: The expected number of BGP EVPN neighbors in the default VRF.\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPEVPNCount could not run because number was not supplied.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP EVPN peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNState","title":"VerifyBGPEVPNState
","text":" Bases: AntaTest
Verifies all EVPN BGP sessions are established (default VRF).
- self.result = \u201cskipped\u201d if no BGP EVPN peers are returned by the device
- self.result = \u201csuccess\u201d if all EVPN BGP sessions are established.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPEVPNState(AntaTest):\n\"\"\"\n Verifies all EVPN BGP sessions are established (default VRF).\n\n * self.result = \"skipped\" if no BGP EVPN peers are returned by the device\n * self.result = \"success\" if all EVPN BGP sessions are established.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPEVPNState\"\n description = \"Verifies all EVPN BGP sessions are established (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp evpn summary\")]\n\n @check_bgp_family_enable(\"evpn\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPEVPNState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNState.test","title":"test()
","text":"Run VerifyBGPEVPNState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"evpn\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPEVPNState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastCount","title":"VerifyBGPIPv4UnicastCount
","text":" Bases: AntaTest
Verifies all IPv4 unicast BGP sessions are established and all BGP messages queues for these sessions are empty and the actual number of BGP IPv4 unicast neighbors is the one we expect.
- self.result = \u201cskipped\u201d if the
number
or vrf
parameter is missing - self.result = \u201csuccess\u201d if all IPv4 unicast BGP sessions are established and if all BGP messages queues for these sessions are empty and if the actual number of BGP IPv4 unicast neighbors is equal to `number.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv4UnicastCount(AntaTest):\n\"\"\"\n Verifies all IPv4 unicast BGP sessions are established\n and all BGP messages queues for these sessions are empty\n and the actual number of BGP IPv4 unicast neighbors is the one we expect.\n\n * self.result = \"skipped\" if the `number` or `vrf` parameter is missing\n * self.result = \"success\" if all IPv4 unicast BGP sessions are established\n and if all BGP messages queues for these sessions are empty\n and if the actual number of BGP IPv4 unicast neighbors is equal to `number.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastCount\"\n description = (\n \"Verifies all IPv4 unicast BGP sessions are established and all their BGP messages queues are empty and \"\n \" the actual number of BGP IPv4 unicast neighbors is the one we expect.\"\n )\n categories = [\"routing\", \"bgp\"]\n template = AntaTestTemplate(template=\"show bgp ipv4 unicast summary vrf {vrf}\")\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPIPv4UnicastCount validation\n\n Args:\n number: The expected number of BGP IPv4 unicast neighbors.\n vrf: VRF to verify (template parameter)\n \"\"\"\n\n if not number:\n self.result.is_skipped(\"VerifyBGPIPv4UnicastCount could not run because number was not supplied\")\n return\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vrf = cast(Dict[str, str], command.template_params).get(\"vrf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n peers = command_output[\"vrfs\"][vrf][\"peers\"]\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP peer in vrf {vrf} and got {len(peers)}\")\n if state_issue:\n self.result.is_failure(f\"The following IPv4 peers are not established: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastCount.test","title":"test(number=None)
","text":"Run VerifyBGPIPv4UnicastCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP IPv4 unicast neighbors.
None
vrf
VRF to verify (template parameter)
required Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv4\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPIPv4UnicastCount validation\n\n Args:\n number: The expected number of BGP IPv4 unicast neighbors.\n vrf: VRF to verify (template parameter)\n \"\"\"\n\n if not number:\n self.result.is_skipped(\"VerifyBGPIPv4UnicastCount could not run because number was not supplied\")\n return\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vrf = cast(Dict[str, str], command.template_params).get(\"vrf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n peers = command_output[\"vrfs\"][vrf][\"peers\"]\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP peer in vrf {vrf} and got {len(peers)}\")\n if state_issue:\n self.result.is_failure(f\"The following IPv4 peers are not established: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastState","title":"VerifyBGPIPv4UnicastState
","text":" Bases: AntaTest
Verifies all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cskipped\u201d if no BGP vrf are returned by the device
- self.result = \u201csuccess\u201d if all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv4UnicastState(AntaTest):\n\"\"\"\n Verifies all IPv4 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n\n * self.result = \"skipped\" if no BGP vrf are returned by the device\n * self.result = \"success\" if all IPv4 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastState\"\n description = \"Verifies all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp ipv4 unicast summary vrf all\")]\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPIPv4UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastState.test","title":"test()
","text":"Run VerifyBGPIPv4UnicastState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv4\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPIPv4UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv6UnicastState","title":"VerifyBGPIPv6UnicastState
","text":" Bases: AntaTest
Verifies all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cskipped\u201d if no BGP vrf are returned by the device
- self.result = \u201csuccess\u201d if all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv6UnicastState(AntaTest):\n\"\"\"\n Verifies all IPv6 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n\n * self.result = \"skipped\" if no BGP vrf are returned by the device\n * self.result = \"success\" if all IPv6 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv6UnicastState\"\n description = \"Verifies all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp ipv6 unicast summary vrf all\")]\n\n @check_bgp_family_enable(\"ipv6\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPIPv6UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv6UnicastState.test","title":"test()
","text":"Run VerifyBGPIPv6UnicastState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv6\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPIPv6UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCCount","title":"VerifyBGPRTCCount
","text":" Bases: AntaTest
Verifies all RTC BGP sessions are established (default VRF) and the actual number of BGP RTC neighbors is the one we expect (default VRF).
- self.result = \u201cskipped\u201d if the
number
parameter is missing - self.result = \u201csuccess\u201d if all RTC BGP sessions are Established and if the actual number of BGP RTC neighbors is the one we expect.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPRTCCount(AntaTest):\n\"\"\"\n Verifies all RTC BGP sessions are established (default VRF)\n and the actual number of BGP RTC neighbors is the one we expect (default VRF).\n\n * self.result = \"skipped\" if the `number` parameter is missing\n * self.result = \"success\" if all RTC BGP sessions are Established and if the actual\n number of BGP RTC neighbors is the one we expect.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPRTCCount\"\n description = \"Verifies all RTC BGP sessions are established (default VRF) and the actual number of BGP RTC neighbors is the one we expect (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp rt-membership summary\")]\n\n @check_bgp_family_enable(\"rtc\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPRTCCount validation\n\n Args:\n number: The expected number of BGP RTC neighbors (default VRF).\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPRTCCount could not run because number was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP RTC peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCCount.test","title":"test(number=None)
","text":"Run VerifyBGPRTCCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP RTC neighbors (default VRF).
None
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"rtc\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPRTCCount validation\n\n Args:\n number: The expected number of BGP RTC neighbors (default VRF).\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPRTCCount could not run because number was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP RTC peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCState","title":"VerifyBGPRTCState
","text":" Bases: AntaTest
Verifies all RTC BGP sessions are established (default VRF).
- self.result = \u201cskipped\u201d if no BGP RTC peers are returned by the device
- self.result = \u201csuccess\u201d if all RTC BGP sessions are established.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPRTCState(AntaTest):\n\"\"\"\n Verifies all RTC BGP sessions are established (default VRF).\n\n * self.result = \"skipped\" if no BGP RTC peers are returned by the device\n * self.result = \"success\" if all RTC BGP sessions are established.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPRTCState\"\n description = \"Verifies all RTC BGP sessions are established (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp rt-membership summary\")]\n\n @check_bgp_family_enable(\"rtc\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPRTCState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCState.test","title":"test()
","text":"Run VerifyBGPRTCState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"rtc\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPRTCState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.generic/","title":"Generic","text":""},{"location":"api/tests.routing.generic/#anta-catalog-for-routing-generic-tests","title":"ANTA catalog for routing-generic tests","text":"Generic routing test functions
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyBFD","title":"VerifyBFD
","text":" Bases: AntaTest
Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).
Source code in anta/tests/routing/generic.py
class VerifyBFD(AntaTest):\n\"\"\"\n Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).\n \"\"\"\n\n name = \"VerifyBFD\"\n description = \"Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).\"\n categories = [\"routing\", \"generic\"]\n # revision 1 as later revision introduce additional nesting for type\n commands = [AntaTestCommand(command=\"show bfd peers\", version=1)]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBFD validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n self.result.is_success()\n\n for _, vrf_data in command_output[\"vrfs\"].items():\n for _, neighbor_data in vrf_data[\"ipv4Neighbors\"].items():\n for peer, peer_data in neighbor_data[\"peerStats\"].items():\n if (peer_status := peer_data[\"status\"]) != \"up\":\n failure_message = f\"bfd state for peer '{peer}' is {peer_status} (expected up).\"\n if (peer_l3intf := peer_data.get(\"l3intf\")) is not None and peer_l3intf != \"\":\n failure_message += f\" Interface: {peer_l3intf}.\"\n self.result.is_failure(failure_message)\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyBFD.test","title":"test()
","text":"Run VerifyBFD validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBFD validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n self.result.is_success()\n\n for _, vrf_data in command_output[\"vrfs\"].items():\n for _, neighbor_data in vrf_data[\"ipv4Neighbors\"].items():\n for peer, peer_data in neighbor_data[\"peerStats\"].items():\n if (peer_status := peer_data[\"status\"]) != \"up\":\n failure_message = f\"bfd state for peer '{peer}' is {peer_status} (expected up).\"\n if (peer_l3intf := peer_data.get(\"l3intf\")) is not None and peer_l3intf != \"\":\n failure_message += f\" Interface: {peer_l3intf}.\"\n self.result.is_failure(failure_message)\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingProtocolModel","title":"VerifyRoutingProtocolModel
","text":" Bases: AntaTest
Verifies the configured routing protocol model is the one we expect. And if there is no mismatch between the configured and operating routing protocol model.
model(str): Expected routing protocol model (multi-agent or ribd). Default is multi-agent\n
Source code in anta/tests/routing/generic.py
class VerifyRoutingProtocolModel(AntaTest):\n\"\"\"\n Verifies the configured routing protocol model is the one we expect.\n And if there is no mismatch between the configured and operating routing protocol model.\n\n model(str): Expected routing protocol model (multi-agent or ribd). Default is multi-agent\n \"\"\"\n\n name = \"VerifyRoutingProtocolModel\"\n description = (\n \"Verifies the configured routing protocol model is the expected one and if there is no mismatch between the configured and operating routing protocol model.\"\n )\n categories = [\"routing\", \"generic\"]\n # \"revision\": 3\n commands = [AntaTestCommand(command=\"show ip route summary\")]\n\n @AntaTest.anta_test\n def test(self, model: Optional[str] = \"multi-agent\") -> None:\n\"\"\"Run VerifyRoutingProtocolModel validation\"\"\"\n\n if not model:\n self.result.is_skipped(\"VerifyRoutingProtocolModel was not run as no model was given\")\n return\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n configured_model = command_output[\"protoModelStatus\"][\"configuredProtoModel\"]\n operating_model = command_output[\"protoModelStatus\"][\"operatingProtoModel\"]\n if configured_model == operating_model == model:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {model}\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingProtocolModel.test","title":"test(model='multi-agent')
","text":"Run VerifyRoutingProtocolModel validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self, model: Optional[str] = \"multi-agent\") -> None:\n\"\"\"Run VerifyRoutingProtocolModel validation\"\"\"\n\n if not model:\n self.result.is_skipped(\"VerifyRoutingProtocolModel was not run as no model was given\")\n return\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n configured_model = command_output[\"protoModelStatus\"][\"configuredProtoModel\"]\n operating_model = command_output[\"protoModelStatus\"][\"operatingProtoModel\"]\n if configured_model == operating_model == model:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {model}\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingTableSize","title":"VerifyRoutingTableSize
","text":" Bases: AntaTest
Verifies the size of the IP routing table (default VRF). Should be between the two provided thresholds.
Parameters:
Name Type Description Default minimum(int)
Expected minimum routing table (default VRF) size.
required maximum(int)
Expected maximum routing table (default VRF) size.
required Source code in anta/tests/routing/generic.py
class VerifyRoutingTableSize(AntaTest):\n\"\"\"\n Verifies the size of the IP routing table (default VRF).\n Should be between the two provided thresholds.\n\n Args:\n minimum(int): Expected minimum routing table (default VRF) size.\n maximum(int): Expected maximum routing table (default VRF) size.\n \"\"\"\n\n name = \"VerifyRoutingTableSize\"\n description = \"Verifies the size of the IP routing table (default VRF). Should be between the two provided thresholds.\"\n categories = [\"routing\", \"generic\"]\n # \"revision\": 3\n commands = [AntaTestCommand(command=\"show ip route summary\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None, maximum: Optional[int] = None) -> None:\n\"\"\"Run VerifyRoutingTableSize validation\"\"\"\n\n if not minimum or not maximum:\n self.result.is_skipped(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} was not provided\")\n return\n if not isinstance(minimum, int) or not isinstance(maximum, int):\n self.result.is_error(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} is not a valid value (integer)\")\n return\n if maximum < minimum:\n self.result.is_error(f\"VerifyRoutingTableSize was not run as minimum {minimum} is greate than maximum {maximum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_routes = int(command_output[\"vrfs\"][\"default\"][\"totalRoutes\"])\n if minimum <= total_routes <= maximum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing-table has {total_routes} routes and not between min ({minimum}) and maximum ({maximum})\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingTableSize.test","title":"test(minimum=None, maximum=None)
","text":"Run VerifyRoutingTableSize validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None, maximum: Optional[int] = None) -> None:\n\"\"\"Run VerifyRoutingTableSize validation\"\"\"\n\n if not minimum or not maximum:\n self.result.is_skipped(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} was not provided\")\n return\n if not isinstance(minimum, int) or not isinstance(maximum, int):\n self.result.is_error(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} is not a valid value (integer)\")\n return\n if maximum < minimum:\n self.result.is_error(f\"VerifyRoutingTableSize was not run as minimum {minimum} is greate than maximum {maximum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_routes = int(command_output[\"vrfs\"][\"default\"][\"totalRoutes\"])\n if minimum <= total_routes <= maximum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing-table has {total_routes} routes and not between min ({minimum}) and maximum ({maximum})\")\n
"},{"location":"api/tests.routing.ospf/","title":"OSPF","text":""},{"location":"api/tests.routing.ospf/#anta-catalog-for-routing-ospf-tests","title":"ANTA catalog for routing-ospf tests","text":"OSPF test functions
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborCount","title":"VerifyOSPFNeighborCount
","text":" Bases: AntaTest
Verifies the number of OSPF neighbors in FULL state is the one we expect.
Parameters:
Name Type Description Default number
int
The expected number of OSPF neighbors in FULL state.
required Source code in anta/tests/routing/ospf.py
class VerifyOSPFNeighborCount(AntaTest):\n\"\"\"\n Verifies the number of OSPF neighbors in FULL state is the one we expect.\n\n Args:\n number (int): The expected number of OSPF neighbors in FULL state.\n \"\"\"\n\n name = \"VerifyOSPFNeighborCount\"\n description = \"Verifies the number of OSPF neighbors in FULL state is the one we expect.\"\n categories = [\"routing\", \"ospf\"]\n commands = [AntaTestCommand(command=\"show ip ospf neighbor\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"Run VerifyOSPFNeighborCount validation\"\"\"\n if not (isinstance(number, int) and number >= 0):\n self.result.is_skipped(f\"VerifyOSPFNeighborCount was not run as the number given '{number}' is not a valid value.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if (neighbor_count := _count_ospf_neighbor(command_output)) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n if neighbor_count != number:\n self.result.is_failure(f\"device has {neighbor_count} neighbors (expected {number})\")\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n print(not_full_neighbors)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborCount.test","title":"test(number=None)
","text":"Run VerifyOSPFNeighborCount validation
Source code in anta/tests/routing/ospf.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"Run VerifyOSPFNeighborCount validation\"\"\"\n if not (isinstance(number, int) and number >= 0):\n self.result.is_skipped(f\"VerifyOSPFNeighborCount was not run as the number given '{number}' is not a valid value.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if (neighbor_count := _count_ospf_neighbor(command_output)) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n if neighbor_count != number:\n self.result.is_failure(f\"device has {neighbor_count} neighbors (expected {number})\")\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n print(not_full_neighbors)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborState","title":"VerifyOSPFNeighborState
","text":" Bases: AntaTest
Verifies all OSPF neighbors are in FULL state.
Source code in anta/tests/routing/ospf.py
class VerifyOSPFNeighborState(AntaTest):\n\"\"\"\n Verifies all OSPF neighbors are in FULL state.\n \"\"\"\n\n name = \"VerifyOSPFNeighborState\"\n description = \"Verifies all OSPF neighbors are in FULL state.\"\n categories = [\"routing\", \"ospf\"]\n commands = [AntaTestCommand(command=\"show ip ospf neighbor\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyOSPFNeighborState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if _count_ospf_neighbor(command_output) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborState.test","title":"test()
","text":"Run VerifyOSPFNeighborState validation
Source code in anta/tests/routing/ospf.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyOSPFNeighborState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if _count_ospf_neighbor(command_output) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.security/","title":"Security","text":""},{"location":"api/tests.security/#anta-catalog-for-security-tests","title":"ANTA catalog for security tests","text":"Test functions related to the EOS various security settings
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpStatus","title":"VerifyAPIHttpStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyAPIHttpStatus(AntaTest):\n\"\"\"\n Verifies if eAPI HTTP server is disabled globally.\n\n Expected Results:\n * success: The test will pass if eAPI HTTP server is disabled globally.\n * failure: The test will fail if eAPI HTTP server is NOT disabled globally.\n \"\"\"\n\n name = \"VerifyAPIHttpStatus\"\n description = \"Verifies if eAPI HTTP server is disabled globally.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyAPIHTTPStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and not command_output[\"httpServer\"][\"running\"]:\n self.result.is_success()\n else:\n self.result.is_failure(\"eAPI HTTP server is enabled globally\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpStatus.test","title":"test()
","text":"Run VerifyAPIHTTPStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyAPIHTTPStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and not command_output[\"httpServer\"][\"running\"]:\n self.result.is_success()\n else:\n self.result.is_failure(\"eAPI HTTP server is enabled globally\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpsSSL","title":"VerifyAPIHttpsSSL
","text":" Bases: AntaTest
Verifies if eAPI HTTPS server SSL profile is configured and valid.
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.
- skipped: The test will be skipped if the SSL profile is not provided.
Source code in anta/tests/security.py
class VerifyAPIHttpsSSL(AntaTest):\n\"\"\"\n Verifies if eAPI HTTPS server SSL profile is configured and valid.\n\n Expected results:\n * success: The test will pass if the eAPI HTTPS server SSL profile is configured and valid.\n * failure: The test will fail if the eAPI HTTPS server SSL profile is NOT configured, misconfigured or invalid.\n * skipped: The test will be skipped if the SSL profile is not provided.\n \"\"\"\n\n name = \"VerifyAPIHttpsSSL\"\n description = \"Verifies if eAPI HTTPS server SSL profile is configured and valid.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands\")]\n\n @AntaTest.anta_test\n def test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyAPIHttpsSSL validation.\n\n Args:\n profile: SSL profile to verify.\n \"\"\"\n if not profile:\n self.result.is_skipped(f\"{self.__class__.name} did not run because profile was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"sslProfile\"][\"name\"] == profile and command_output[\"sslProfile\"][\"state\"] == \"valid\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is misconfigured or invalid\")\n\n except KeyError:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is not configured\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpsSSL.test","title":"test(profile=None)
","text":"Run VerifyAPIHttpsSSL validation.
Parameters:
Name Type Description Default profile
Optional[str]
SSL profile to verify.
None
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyAPIHttpsSSL validation.\n\n Args:\n profile: SSL profile to verify.\n \"\"\"\n if not profile:\n self.result.is_skipped(f\"{self.__class__.name} did not run because profile was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"sslProfile\"][\"name\"] == profile and command_output[\"sslProfile\"][\"state\"] == \"valid\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is misconfigured or invalid\")\n\n except KeyError:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is not configured\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv4Acl","title":"VerifyAPIIPv4Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifyAPIIPv4Acl(AntaTest):\n\"\"\"\n Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if eAPI has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if eAPI has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifyAPIIPv4Acl\"\n description = \"Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands ip access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifyAPIIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for eAPI. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv6Acl","title":"VerifyAPIIPv6Acl
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyAPIIPv6Acl(AntaTest):\n\"\"\"\n Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if eAPI has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if eAPI has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifyAPIIPv6Acl\"\n description = \"Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifyAPIIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for eAPI. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv4Acl","title":"VerifySSHIPv4Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifySSHIPv4Acl(AntaTest):\n\"\"\"\n Verifies if the SSHD agent has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SSHD agent has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if the SSHD agent has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySSHIPv4Acl\"\n description = \"Verifies if the SSHD agent has IPv4 ACL(s) configured.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh ip access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySSHIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SSHD agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv6Acl","title":"VerifySSHIPv6Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifySSHIPv6Acl(AntaTest):\n\"\"\"\n Verifies if the SSHD agent has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SSHD agent has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if the SSHD agent has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySSHIPv6Acl\"\n description = \"Verifies if the SSHD agent has IPv6 ACL(s) configured.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySSHIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SSHD agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHStatus","title":"VerifySSHStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifySSHStatus(AntaTest):\n\"\"\"\n Verifies if the SSHD agent is disabled in the default VRF.\n\n Expected Results:\n * success: The test will pass if the SSHD agent is disabled in the default VRF.\n * failure: The test will fail if the SSHD agent is NOT disabled in the default VRF.\n \"\"\"\n\n name = \"VerifySSHStatus\"\n description = \"Verifies if the SSHD agent is disabled in the default VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySSHStatus validation.\n \"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n line = [line for line in command_output.split(\"\\n\") if line.startswith(\"SSHD status\")][0]\n status = line.split(\"is \")[1]\n\n if status == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(line)\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHStatus.test","title":"test()
","text":"Run VerifySSHStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySSHStatus validation.\n \"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n line = [line for line in command_output.split(\"\\n\") if line.startswith(\"SSHD status\")][0]\n status = line.split(\"is \")[1]\n\n if status == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(line)\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyTelnetStatus","title":"VerifyTelnetStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyTelnetStatus(AntaTest):\n\"\"\"\n Verifies if Telnet is disabled in the default VRF.\n\n Expected Results:\n * success: The test will pass if Telnet is disabled in the default VRF.\n * failure: The test will fail if Telnet is NOT disabled in the default VRF.\n \"\"\"\n\n name = \"VerifyTelnetStatus\"\n description = \"Verifies if Telnet is disabled in the default VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management telnet\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyTelnetStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"serverState\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"Telnet status for Default VRF is enabled\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyTelnetStatus.test","title":"test()
","text":"Run VerifyTelnetStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyTelnetStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"serverState\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"Telnet status for Default VRF is enabled\")\n
"},{"location":"api/tests.snmp/","title":"SNMP","text":""},{"location":"api/tests.snmp/#anta-catalog-for-snmp-tests","title":"ANTA catalog for SNMP tests","text":"Test functions related to the EOS various SNMP settings
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv4Acl","title":"VerifySnmpIPv4Acl
","text":" Bases: AntaTest
Verifies if the SNMP agent has the right number IPv4 ACL(s) configured for a specified VRF.
Expected results - success: The test will pass if the SNMP agent has the provided number of IPv4 ACL(s) in the specified VRF.
- failure: The test will fail if the SNMP agent has not the right number of IPv4 ACL(s) in the specified VRF.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/snmp.py
class VerifySnmpIPv4Acl(AntaTest):\n\"\"\"\n Verifies if the SNMP agent has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SNMP agent has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if the SNMP agent has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpIPv4Acl\"\n description = \"Verifies if the SNMP agent has IPv4 ACL(s) configured.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp ipv4 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySnmpIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv6Acl","title":"VerifySnmpIPv6Acl
","text":" Bases: AntaTest
Verifies if the SNMP agent has the right number IPv6 ACL(s) configured for a specified VRF.
Expected results - success: The test will pass if the SNMP agent has the provided number of IPv6 ACL(s) in the specified VRF.
- failure: The test will fail if the SNMP agent 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.
Source code in anta/tests/snmp.py
class VerifySnmpIPv6Acl(AntaTest):\n\"\"\"\n Verifies if the SNMP agent has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SNMP agent has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if the SNMP agent has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpIPv6Acl\"\n description = \"Verifies if the SNMP agent has IPv6 ACL(s) configured.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySnmpIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpStatus","title":"VerifySnmpStatus
","text":" Bases: AntaTest
Verifies whether the SNMP agent is enabled in a specified VRF.
Expected Results - success: The test will pass if the SNMP agent is enabled in the specified VRF.
- failure: The test will fail if the SNMP agent is disabled in the specified VRF.
- skipped: The test will be skipped if the VRF parameter is not provided.
Source code in anta/tests/snmp.py
class VerifySnmpStatus(AntaTest):\n\"\"\"\n Verifies whether the SNMP agent is enabled in a specified VRF.\n\n Expected Results:\n * success: The test will pass if the SNMP agent is enabled in the specified VRF.\n * failure: The test will fail if the SNMP agent is disabled in the specified VRF.\n * skipped: The test will be skipped if the VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpStatus\"\n description = \"Verifies if the SNMP agent is enabled.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp\")]\n\n @AntaTest.anta_test\n def test(self, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpStatus validation.\n\n Args:\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because vrf was not supplied\")\n else:\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and vrf in command_output[\"vrfs\"][\"snmpVrfs\"]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"SNMP agent disabled in vrf {vrf}\")\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpStatus.test","title":"test(vrf='default')
","text":"Run VerifySnmpStatus validation.
Parameters:
Name Type Description Default vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpStatus validation.\n\n Args:\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because vrf was not supplied\")\n else:\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and vrf in command_output[\"vrfs\"][\"snmpVrfs\"]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"SNMP agent disabled in vrf {vrf}\")\n
"},{"location":"api/tests.software/","title":"Software","text":""},{"location":"api/tests.software/#anta-catalog-for-software-tests","title":"ANTA catalog for software tests","text":"Test functions related to the EOS software
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSExtensions","title":"VerifyEOSExtensions
","text":" Bases: AntaTest
Verifies all EOS extensions installed on the device are enabled for boot persistence.
Source code in anta/tests/software.py
class VerifyEOSExtensions(AntaTest):\n\"\"\"\n Verifies all EOS extensions installed on the device are enabled for boot persistence.\n \"\"\"\n\n name = \"VerifyEOSExtensions\"\n description = \"Verifies all EOS extensions installed on the device are enabled for boot persistence.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show extensions\"), AntaTestCommand(command=\"show boot-extensions\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyEOSExtensions validation\"\"\"\n\n boot_extensions = []\n\n show_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n show_boot_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[1].output)\n\n installed_extensions = [\n extension for extension, extension_data in show_extensions_command_output[\"extensions\"].items() if extension_data[\"status\"] == \"installed\"\n ]\n\n for extension in show_boot_extensions_command_output[\"extensions\"]:\n extension = extension.strip(\"\\n\")\n if extension != \"\":\n boot_extensions.append(extension)\n\n installed_extensions.sort()\n boot_extensions.sort()\n if installed_extensions == boot_extensions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Missing EOS extensions: installed {installed_extensions} / configured: {boot_extensions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSExtensions.test","title":"test()
","text":"Run VerifyEOSExtensions validation
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyEOSExtensions validation\"\"\"\n\n boot_extensions = []\n\n show_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n show_boot_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[1].output)\n\n installed_extensions = [\n extension for extension, extension_data in show_extensions_command_output[\"extensions\"].items() if extension_data[\"status\"] == \"installed\"\n ]\n\n for extension in show_boot_extensions_command_output[\"extensions\"]:\n extension = extension.strip(\"\\n\")\n if extension != \"\":\n boot_extensions.append(extension)\n\n installed_extensions.sort()\n boot_extensions.sort()\n if installed_extensions == boot_extensions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Missing EOS extensions: installed {installed_extensions} / configured: {boot_extensions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSVersion","title":"VerifyEOSVersion
","text":" Bases: AntaTest
Verifies the device is running one of the allowed EOS version.
Source code in anta/tests/software.py
class VerifyEOSVersion(AntaTest):\n\"\"\"\n Verifies the device is running one of the allowed EOS version.\n \"\"\"\n\n name = \"VerifyEOSVersion\"\n description = \"Verifies the device is running one of the allowed EOS version.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show version\")]\n\n @AntaTest.anta_test\n def test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEOSVersion validation\n\n Args:\n versions: List of allowed EOS versions.\n \"\"\"\n if not versions:\n self.result.is_skipped(\"VerifyEOSVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if command_output[\"version\"] in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f'device is running version {command_output[\"version\"]} not in expected versions: {versions}')\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSVersion.test","title":"test(versions=None)
","text":"Run VerifyEOSVersion validation
Parameters:
Name Type Description Default versions
Optional[List[str]]
List of allowed EOS versions.
None
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEOSVersion validation\n\n Args:\n versions: List of allowed EOS versions.\n \"\"\"\n if not versions:\n self.result.is_skipped(\"VerifyEOSVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if command_output[\"version\"] in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f'device is running version {command_output[\"version\"]} not in expected versions: {versions}')\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyTerminAttrVersion","title":"VerifyTerminAttrVersion
","text":" Bases: AntaTest
Verifies the device is running one of the allowed TerminAttr version.
Source code in anta/tests/software.py
class VerifyTerminAttrVersion(AntaTest):\n\"\"\"\n Verifies the device is running one of the allowed TerminAttr version.\n \"\"\"\n\n name = \"VerifyTerminAttrVersion\"\n description = \"Verifies the device is running one of the allowed TerminAttr version.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n @AntaTest.anta_test\n def test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTerminAttrVersion validation\n\n Args:\n versions: List of allowed TerminAttr versions.\n \"\"\"\n\n if not versions:\n self.result.is_skipped(\"VerifyTerminAttrVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n command_output_data = command_output[\"details\"][\"packages\"][\"TerminAttr-core\"][\"version\"]\n if command_output_data in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device is running TerminAttr version {command_output_data} and is not in the allowed list: {versions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyTerminAttrVersion.test","title":"test(versions=None)
","text":"Run VerifyTerminAttrVersion validation
Parameters:
Name Type Description Default versions
Optional[List[str]]
List of allowed TerminAttr versions.
None
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTerminAttrVersion validation\n\n Args:\n versions: List of allowed TerminAttr versions.\n \"\"\"\n\n if not versions:\n self.result.is_skipped(\"VerifyTerminAttrVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n command_output_data = command_output[\"details\"][\"packages\"][\"TerminAttr-core\"][\"version\"]\n if command_output_data in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device is running TerminAttr version {command_output_data} and is not in the allowed list: {versions}\")\n
"},{"location":"api/tests.stp/","title":"STP","text":""},{"location":"api/tests.stp/#anta-catalog-for-stp-tests","title":"ANTA catalog for STP tests","text":"Test functions related to various Spanning Tree Protocol (STP) settings
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPBlockedPorts","title":"VerifySTPBlockedPorts
","text":" Bases: AntaTest
Verifies there is no STP blocked ports.
Expected Results - success: The test will pass if there are NO ports blocked by STP.
- failure: The test will fail if there are ports blocked by STP.
Source code in anta/tests/stp.py
class VerifySTPBlockedPorts(AntaTest):\n\"\"\"\n Verifies there is no STP blocked ports.\n\n Expected Results:\n * success: The test will pass if there are NO ports blocked by STP.\n * failure: The test will fail if there are ports blocked by STP.\n \"\"\"\n\n name = \"VerifySTPBlockedPorts\"\n description = \"Verifies there is no STP blocked ports.\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree blockedports\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"spanningTreeInstances\"]):\n self.result.is_success()\n else:\n for key, value in stp_instances.items():\n stp_instances[key] = value.pop(\"spanningTreeBlockedPorts\")\n self.result.is_failure(f\"The following ports are blocked by STP: {stp_instances}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPBlockedPorts.test","title":"test()
","text":"Run VerifySTPBlockedPorts validation
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"spanningTreeInstances\"]):\n self.result.is_success()\n else:\n for key, value in stp_instances.items():\n stp_instances[key] = value.pop(\"spanningTreeBlockedPorts\")\n self.result.is_failure(f\"The following ports are blocked by STP: {stp_instances}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPCounters","title":"VerifySTPCounters
","text":" Bases: AntaTest
Verifies there is no errors in STP BPDU packets.
Expected Results - success: The test will pass if there are NO STP BPDU packet errors under all interfaces participating in STP.
- failure: The test will fail if there are STP BPDU packet errors on one or many interface(s).
Source code in anta/tests/stp.py
class VerifySTPCounters(AntaTest):\n\"\"\"\n Verifies there is no errors in STP BPDU packets.\n\n Expected Results:\n * success: The test will pass if there are NO STP BPDU packet errors under all interfaces participating in STP.\n * failure: The test will fail if there are STP BPDU packet errors on one or many interface(s).\n \"\"\"\n\n name = \"VerifySTPCounters\"\n description = \"Verifies there is no errors in STP BPDU packets.\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree counters\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n interfaces_with_errors = [\n interface for interface, counters in command_output[\"interfaces\"].items() if counters[\"bpduTaggedError\"] or counters[\"bpduOtherError\"] != 0\n ]\n\n if interfaces_with_errors:\n self.result.is_failure(f\"The following interfaces have STP BPDU packet errors: {interfaces_with_errors}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPCounters.test","title":"test()
","text":"Run VerifySTPBlockedPorts validation
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n interfaces_with_errors = [\n interface for interface, counters in command_output[\"interfaces\"].items() if counters[\"bpduTaggedError\"] or counters[\"bpduOtherError\"] != 0\n ]\n\n if interfaces_with_errors:\n self.result.is_failure(f\"The following interfaces have STP BPDU packet errors: {interfaces_with_errors}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPForwardingPorts","title":"VerifySTPForwardingPorts
","text":" Bases: AntaTest
Verifies that all interfaces are in a forwarding state for a provided list of VLAN(s).
Expected Results - success: The test will pass if all interfaces are in a forwarding state for the specified VLAN(s).
- failure: The test will fail if one or many interfaces are NOT in a forwarding state in the specified VLAN(s).
- error: The test will give an error if a list of VLAN(s) is not provided as template_params.
Source code in anta/tests/stp.py
class VerifySTPForwardingPorts(AntaTest):\n\"\"\"\n Verifies that all interfaces are in a forwarding state for a provided list of VLAN(s).\n\n Expected Results:\n * success: The test will pass if all interfaces are in a forwarding state for the specified VLAN(s).\n * failure: The test will fail if one or many interfaces are NOT in a forwarding state in the specified VLAN(s).\n * error: The test will give an error if a list of VLAN(s) is not provided as template_params.\n \"\"\"\n\n name = \"VerifySTPForwardingPorts\"\n description = \"Verifies that all interfaces are forwarding for a provided list of VLAN(s).\"\n categories = [\"stp\"]\n template = AntaTestTemplate(template=\"show spanning-tree topology vlan {vlan} status\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPForwardingPorts validation.\n \"\"\"\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params)[\"vlan\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (topologies := get_value(command_output, \"topologies\")):\n self.result.is_failure(f\"STP instance for VLAN {vlan_id} is not configured\")\n\n else:\n for value in topologies.values():\n if int(vlan_id) in value[\"vlans\"]:\n interfaces_not_forwarding = [interface for interface, state in value[\"interfaces\"].items() if state[\"state\"] != \"forwarding\"]\n\n if interfaces_not_forwarding:\n self.result.is_failure(f\"The following interface(s) are not in a forwarding state for VLAN {vlan_id}: {interfaces_not_forwarding}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPForwardingPorts.test","title":"test()
","text":"Run VerifySTPForwardingPorts validation.
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPForwardingPorts validation.\n \"\"\"\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params)[\"vlan\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (topologies := get_value(command_output, \"topologies\")):\n self.result.is_failure(f\"STP instance for VLAN {vlan_id} is not configured\")\n\n else:\n for value in topologies.values():\n if int(vlan_id) in value[\"vlans\"]:\n interfaces_not_forwarding = [interface for interface, state in value[\"interfaces\"].items() if state[\"state\"] != \"forwarding\"]\n\n if interfaces_not_forwarding:\n self.result.is_failure(f\"The following interface(s) are not in a forwarding state for VLAN {vlan_id}: {interfaces_not_forwarding}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPMode","title":"VerifySTPMode
","text":" Bases: AntaTest
Verifies the configured STP mode for a provided list of VLAN(s).
Expected Results - success: The test will pass if the STP mode is configured properly in the specified VLAN(s).
- failure: The test will fail if the STP mode is NOT configured properly for one or more specified VLAN(s).
- skipped: The test will be skipped if the STP mode is not provided.
- error: The test will give an error if a list of VLAN(s) is not provided as template_params.
Source code in anta/tests/stp.py
class VerifySTPMode(AntaTest):\n\"\"\"\n Verifies the configured STP mode for a provided list of VLAN(s).\n\n Expected Results:\n * success: The test will pass if the STP mode is configured properly in the specified VLAN(s).\n * failure: The test will fail if the STP mode is NOT configured properly for one or more specified VLAN(s).\n * skipped: The test will be skipped if the STP mode is not provided.\n * error: The test will give an error if a list of VLAN(s) is not provided as template_params.\n \"\"\"\n\n name = \"VerifySTPMode\"\n description = \"Verifies the configured STP mode for a provided list of VLAN(s).\"\n categories = [\"stp\"]\n template = AntaTestTemplate(template=\"show spanning-tree vlan {vlan}\")\n\n @staticmethod\n def _check_stp_mode(mode: str) -> None:\n\"\"\"\n Verifies if the provided STP mode is compatible with Arista EOS devices.\n\n Args:\n mode: The STP mode to verify.\n \"\"\"\n stp_modes = [\"mstp\", \"rstp\", \"rapidPvst\"]\n\n if mode not in stp_modes:\n raise ValueError(f\"Wrong STP mode provided. Valid modes are: {stp_modes}\")\n\n @AntaTest.anta_test\n def test(self, mode: str = \"mstp\") -> None:\n\"\"\"\n Run VerifySTPVersion validation.\n\n Args:\n mode: STP mode to verify. Defaults to 'mstp'.\n \"\"\"\n if not mode:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mode was not supplied\")\n return\n\n self._check_stp_mode(mode)\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params).get(\"vlan\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (stp_mode := get_value(command_output, f\"spanningTreeVlanInstances.{vlan_id}.spanningTreeVlanInstance.protocol\")):\n self.result.is_failure(f\"STP mode '{mode}' not configured for VLAN {vlan_id}\")\n\n elif stp_mode != mode:\n self.result.is_failure(f\"Wrong STP mode configured for VLAN {vlan_id}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPMode.test","title":"test(mode='mstp')
","text":"Run VerifySTPVersion validation.
Parameters:
Name Type Description Default mode
str
STP mode to verify. Defaults to \u2018mstp\u2019.
'mstp'
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self, mode: str = \"mstp\") -> None:\n\"\"\"\n Run VerifySTPVersion validation.\n\n Args:\n mode: STP mode to verify. Defaults to 'mstp'.\n \"\"\"\n if not mode:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mode was not supplied\")\n return\n\n self._check_stp_mode(mode)\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params).get(\"vlan\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (stp_mode := get_value(command_output, f\"spanningTreeVlanInstances.{vlan_id}.spanningTreeVlanInstance.protocol\")):\n self.result.is_failure(f\"STP mode '{mode}' not configured for VLAN {vlan_id}\")\n\n elif stp_mode != mode:\n self.result.is_failure(f\"Wrong STP mode configured for VLAN {vlan_id}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPRootPriority","title":"VerifySTPRootPriority
","text":" Bases: AntaTest
Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).
Expected Results - success: The test will pass if the STP root priority is configured properly for the specified VLAN or MST instance ID(s).
- failure: The test will fail if the STP root priority is NOT configured properly for the specified VLAN or MST instance ID(s).
- skipped: The test will be skipped if the STP root priority is not provided.
Source code in anta/tests/stp.py
class VerifySTPRootPriority(AntaTest):\n\"\"\"\n Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).\n\n Expected Results:\n * success: The test will pass if the STP root priority is configured properly for the specified VLAN or MST instance ID(s).\n * failure: The test will fail if the STP root priority is NOT configured properly for the specified VLAN or MST instance ID(s).\n * skipped: The test will be skipped if the STP root priority is not provided.\n \"\"\"\n\n name = \"VerifySTPRootPriority\"\n description = \"Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree root detail\")]\n\n @AntaTest.anta_test\n def test(self, priority: Optional[int] = None, instances: Optional[List[int]] = None) -> None:\n\"\"\"\n Run VerifySTPRootPriority validation.\n\n Args:\n priority: STP root priority to verify.\n instances: List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.\n \"\"\"\n if not priority:\n self.result.is_skipped(f\"{self.__class__.name} did not run because priority was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"instances\"]):\n self.result.is_failure(\"No STP instances configured\")\n return\n\n for instance in stp_instances:\n if instance.startswith(\"MST\"):\n prefix = \"MST\"\n break\n if instance.startswith(\"VL\"):\n prefix = \"VL\"\n break\n\n check_instances = [f\"{prefix}{instance_id}\" for instance_id in instances] if instances else command_output[\"instances\"].keys()\n\n wrong_priority_instances = [instance for instance in check_instances if get_value(command_output, f\"instances.{instance}.rootBridge.priority\") != priority]\n\n if wrong_priority_instances:\n self.result.is_failure(f\"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPRootPriority.test","title":"test(priority=None, instances=None)
","text":"Run VerifySTPRootPriority validation.
Parameters:
Name Type Description Default priority
Optional[int]
STP root priority to verify.
None
instances
Optional[List[int]]
List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.
None
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self, priority: Optional[int] = None, instances: Optional[List[int]] = None) -> None:\n\"\"\"\n Run VerifySTPRootPriority validation.\n\n Args:\n priority: STP root priority to verify.\n instances: List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.\n \"\"\"\n if not priority:\n self.result.is_skipped(f\"{self.__class__.name} did not run because priority was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"instances\"]):\n self.result.is_failure(\"No STP instances configured\")\n return\n\n for instance in stp_instances:\n if instance.startswith(\"MST\"):\n prefix = \"MST\"\n break\n if instance.startswith(\"VL\"):\n prefix = \"VL\"\n break\n\n check_instances = [f\"{prefix}{instance_id}\" for instance_id in instances] if instances else command_output[\"instances\"].keys()\n\n wrong_priority_instances = [instance for instance in check_instances if get_value(command_output, f\"instances.{instance}.rootBridge.priority\") != priority]\n\n if wrong_priority_instances:\n self.result.is_failure(f\"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.system/","title":"System","text":""},{"location":"api/tests.system/#anta-catalog-for-system-tests","title":"ANTA catalog for system tests","text":"Test functions related to system-level features and protocols
"},{"location":"api/tests.system/#anta.tests.system.VerifyAgentLogs","title":"VerifyAgentLogs
","text":" Bases: AntaTest
Verifies there is no agent crash reported on the device.
Source code in anta/tests/system.py
class VerifyAgentLogs(AntaTest):\n\"\"\"\n Verifies there is no agent crash reported on the device.\n \"\"\"\n\n name = \"VerifyAgentLogs\"\n description = \"Verifies there is no agent crash reported on the device.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show agent logs crash\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyAgentLogs validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported some agent crashes: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyAgentLogs.test","title":"test()
","text":"Run VerifyAgentLogs validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyAgentLogs validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported some agent crashes: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCPUUtilization","title":"VerifyCPUUtilization
","text":" Bases: AntaTest
Verifies the CPU utilization is less than 75%.
Source code in anta/tests/system.py
class VerifyCPUUtilization(AntaTest):\n\"\"\"\n Verifies the CPU utilization is less than 75%.\n \"\"\"\n\n name = \"VerifyCPUUtilization\"\n description = \"Verifies the CPU utilization is less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show processes top once\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyCPUUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n command_output_data = command_output[\"cpuInfo\"][\"%Cpu(s)\"][\"idle\"]\n\n if command_output_data > 25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported a high CPU utilization ({100 - command_output_data}%)\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCPUUtilization.test","title":"test()
","text":"Run VerifyCPUUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyCPUUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n command_output_data = command_output[\"cpuInfo\"][\"%Cpu(s)\"][\"idle\"]\n\n if command_output_data > 25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported a high CPU utilization ({100 - command_output_data}%)\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCoredump","title":"VerifyCoredump
","text":" Bases: AntaTest
Verifies there is no core file.
Source code in anta/tests/system.py
class VerifyCoredump(AntaTest):\n\"\"\"\n Verifies there is no core file.\n \"\"\"\n\n name = \"VerifyCoredump\"\n description = \"Verifies there is no core file.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"bash timeout 10 ls /var/core\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyCoredump validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Core-dump(s) have been found: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCoredump.test","title":"test()
","text":"Run VerifyCoredump validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyCoredump validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Core-dump(s) have been found: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyFileSystemUtilization","title":"VerifyFileSystemUtilization
","text":" Bases: AntaTest
Verifies each partition on the disk is used less than 75%.
Source code in anta/tests/system.py
class VerifyFileSystemUtilization(AntaTest):\n\"\"\"\n Verifies each partition on the disk is used less than 75%.\n \"\"\"\n\n name = \"VerifyFileSystemUtilization\"\n description = \"Verifies each partition on the disk is used less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"bash timeout 10 df -h\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyFileSystemUtilization validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n self.result.is_success()\n\n for line in command_output.split(\"\\n\")[1:]:\n if \"loop\" not in line and len(line) > 0 and (percentage := int(line.split()[4].replace(\"%\", \"\"))) > 75:\n self.result.is_failure(f\"mount point {line} is higher than 75% (reported {percentage})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyFileSystemUtilization.test","title":"test()
","text":"Run VerifyFileSystemUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyFileSystemUtilization validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n self.result.is_success()\n\n for line in command_output.split(\"\\n\")[1:]:\n if \"loop\" not in line and len(line) > 0 and (percentage := int(line.split()[4].replace(\"%\", \"\"))) > 75:\n self.result.is_failure(f\"mount point {line} is higher than 75% (reported {percentage})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyMemoryUtilization","title":"VerifyMemoryUtilization
","text":" Bases: AntaTest
Verifies the Memory utilization is less than 75%.
Source code in anta/tests/system.py
class VerifyMemoryUtilization(AntaTest):\n\"\"\"\n Verifies the Memory utilization is less than 75%.\n \"\"\"\n\n name = \"VerifyMemoryUtilization\"\n description = \"Verifies the Memory utilization is less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show version\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyMemoryUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n memory_usage = float(cast(float, command_output[\"memFree\"])) / float(cast(float, command_output[\"memTotal\"]))\n if memory_usage > 0.25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device report a high memory usage: {(1 - memory_usage)*100:.2f}%\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyMemoryUtilization.test","title":"test()
","text":"Run VerifyMemoryUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyMemoryUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n memory_usage = float(cast(float, command_output[\"memFree\"])) / float(cast(float, command_output[\"memTotal\"]))\n if memory_usage > 0.25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device report a high memory usage: {(1 - memory_usage)*100:.2f}%\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyNTP","title":"VerifyNTP
","text":" Bases: AntaTest
Verifies NTP is synchronised.
Source code in anta/tests/system.py
class VerifyNTP(AntaTest):\n\"\"\"\n Verifies NTP is synchronised.\n \"\"\"\n\n name = \"VerifyNTP\"\n description = \"Verifies NTP is synchronised.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show ntp status\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyNTP validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if command_output.split(\"\\n\")[0].split(\" \")[0] == \"synchronised\":\n self.result.is_success()\n else:\n data = command_output.split(\"\\n\")[0]\n self.result.is_failure(f\"not sync with NTP server ({data})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyNTP.test","title":"test()
","text":"Run VerifyNTP validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyNTP validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if command_output.split(\"\\n\")[0].split(\" \")[0] == \"synchronised\":\n self.result.is_success()\n else:\n data = command_output.split(\"\\n\")[0]\n self.result.is_failure(f\"not sync with NTP server ({data})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyReloadCause","title":"VerifyReloadCause
","text":" Bases: AntaTest
Verifies the last reload of the device was requested by a user.
Test considers the following messages as normal and will return success. Failure is for other messages * Reload requested by the user. * Reload requested after FPGA upgrade
Source code in anta/tests/system.py
class VerifyReloadCause(AntaTest):\n\"\"\"\n Verifies the last reload of the device was requested by a user.\n\n Test considers the following messages as normal and will return success. Failure is for other messages\n * Reload requested by the user.\n * Reload requested after FPGA upgrade\n \"\"\"\n\n name = \"VerifyReloadCause\"\n description = \"Verifies the device uptime is higher than a value.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show reload cause\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyReloadCause validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if \"resetCauses\" not in command_output.keys():\n self.result.is_error(\"no reload cause available\")\n return\n\n if len(command_output[\"resetCauses\"]) == 0:\n # No reload causes\n self.result.is_success()\n return\n\n reset_causes = cast(List[Dict[str, Any]], command_output[\"resetCauses\"])\n command_output_data = reset_causes[0].get(\"description\")\n if command_output_data in [\n \"Reload requested by the user.\",\n \"Reload requested after FPGA upgrade\",\n ]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Reload cause is {command_output_data}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyReloadCause.test","title":"test()
","text":"Run VerifyReloadCause validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyReloadCause validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if \"resetCauses\" not in command_output.keys():\n self.result.is_error(\"no reload cause available\")\n return\n\n if len(command_output[\"resetCauses\"]) == 0:\n # No reload causes\n self.result.is_success()\n return\n\n reset_causes = cast(List[Dict[str, Any]], command_output[\"resetCauses\"])\n command_output_data = reset_causes[0].get(\"description\")\n if command_output_data in [\n \"Reload requested by the user.\",\n \"Reload requested after FPGA upgrade\",\n ]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Reload cause is {command_output_data}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifySyslog","title":"VerifySyslog
","text":" Bases: AntaTest
Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.
Source code in anta/tests/system.py
class VerifySyslog(AntaTest):\n\"\"\"\n Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.\n \"\"\"\n\n name = \"VerifySyslog\"\n description = \"Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show logging last 7 days threshold warnings\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySyslog validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(\"Device has some log messages with a severity WARNING or higher\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifySyslog.test","title":"test()
","text":"Run VerifySyslog validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySyslog validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(\"Device has some log messages with a severity WARNING or higher\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyUptime","title":"VerifyUptime
","text":" Bases: AntaTest
Verifies the device uptime is higher than a value.
Source code in anta/tests/system.py
class VerifyUptime(AntaTest):\n\"\"\"\n Verifies the device uptime is higher than a value.\n \"\"\"\n\n name = \"VerifyUptime\"\n description = \"Verifies the device uptime is higher than a value.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show uptime\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyUptime validation\n\n Args:\n minimum: Minimum uptime in seconds.\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (isinstance(minimum, (int, float))) or minimum < 0:\n self.result.is_skipped(\"VerifyUptime was not run as incorrect minimum uptime was given\")\n return\n\n if cast(float, command_output[\"upTime\"]) > minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Uptime is {command_output['upTime']}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyUptime.test","title":"test(minimum=None)
","text":"Run VerifyUptime validation
Parameters:
Name Type Description Default minimum
Optional[int]
Minimum uptime in seconds.
None
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyUptime validation\n\n Args:\n minimum: Minimum uptime in seconds.\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (isinstance(minimum, (int, float))) or minimum < 0:\n self.result.is_skipped(\"VerifyUptime was not run as incorrect minimum uptime was given\")\n return\n\n if cast(float, command_output[\"upTime\"]) > minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Uptime is {command_output['upTime']}\")\n
"},{"location":"api/tests.vxlan/","title":"VxLAN","text":""},{"location":"api/tests.vxlan/#anta-catalog-for-vxlan-tests","title":"ANTA catalog for VxLAN tests","text":"Test functions related to VXLAN
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlan","title":"VerifyVxlan
","text":" Bases: AntaTest
Verifies if Vxlan1 interface is configured, and is up/up
Source code in anta/tests/vxlan.py
class VerifyVxlan(AntaTest):\n\"\"\"\n Verifies if Vxlan1 interface is configured, and is up/up\n \"\"\"\n\n name = \"VerifyVxlan\"\n description = \"Verifies Vxlan1 status\"\n categories = [\"vxlan\"]\n commands = [AntaTestCommand(command=\"show interfaces description\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyVxlan validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"Vxlan1\" not in command_output[\"interfaceDescriptions\"]:\n self.result.is_skipped(\"Vxlan1 interface is not configured\")\n elif (\n command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"lineProtocolStatus\"] == \"up\"\n and command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"interfaceStatus\"] == \"up\"\n ):\n self.result.is_success()\n else:\n self.result.is_failure(\n f\"Vxlan1 interface is {command_output['interfaceDescriptions']['Vxlan1']['lineProtocolStatus']}\"\n f\"/{command_output['interfaceDescriptions']['Vxlan1']['interfaceStatus']}\"\n )\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlan.test","title":"test()
","text":"Run VerifyVxlan validation
Source code in anta/tests/vxlan.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyVxlan validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"Vxlan1\" not in command_output[\"interfaceDescriptions\"]:\n self.result.is_skipped(\"Vxlan1 interface is not configured\")\n elif (\n command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"lineProtocolStatus\"] == \"up\"\n and command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"interfaceStatus\"] == \"up\"\n ):\n self.result.is_success()\n else:\n self.result.is_failure(\n f\"Vxlan1 interface is {command_output['interfaceDescriptions']['Vxlan1']['lineProtocolStatus']}\"\n f\"/{command_output['interfaceDescriptions']['Vxlan1']['interfaceStatus']}\"\n )\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlanConfigSanity","title":"VerifyVxlanConfigSanity
","text":" Bases: AntaTest
Verifies that there are no VXLAN config-sanity issues flagged
Source code in anta/tests/vxlan.py
class VerifyVxlanConfigSanity(AntaTest):\n\"\"\"\n Verifies that there are no VXLAN config-sanity issues flagged\n \"\"\"\n\n name = \"VerifyVxlanConfigSanity\"\n description = \"Verifies VXLAN config-sanity\"\n categories = [\"vxlan\"]\n commands = [AntaTestCommand(command=\"show vxlan config-sanity\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyVxlanConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"categories\" not in command_output or len(command_output[\"categories\"]) == 0:\n self.result.is_skipped(\"VXLAN is not configured on this device\")\n return\n\n failed_categories = {\n category: content\n for category, content in command_output[\"categories\"].items()\n if category in [\"localVtep\", \"mlag\", \"pd\"] and content[\"allCheckPass\"] is not True\n }\n\n if len(failed_categories) > 0:\n self.result.is_failure(f\"Vxlan config sanity check is not passing: {failed_categories}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlanConfigSanity.test","title":"test()
","text":"Run VerifyVxlanConfigSanity validation
Source code in anta/tests/vxlan.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyVxlanConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"categories\" not in command_output or len(command_output[\"categories\"]) == 0:\n self.result.is_skipped(\"VXLAN is not configured on this device\")\n return\n\n failed_categories = {\n category: content\n for category, content in command_output[\"categories\"].items()\n if category in [\"localVtep\", \"mlag\", \"pd\"] and content[\"allCheckPass\"] is not True\n }\n\n if len(failed_categories) > 0:\n self.result.is_failure(f\"Vxlan config sanity check is not passing: {failed_categories}\")\n else:\n self.result.is_success()\n
"},{"location":"cli/debug/","title":"Helpers","text":""},{"location":"cli/debug/#anta-debug-commands","title":"ANTA debug commands","text":"ANTA CLI also provides a set of entrypoints to help building ANTA content. We call it debug and it provides different options:
- Run a command on a device from your inventory and expose a result from AntaTestCommand
- Run a templated command and expose the result
Both are extremly useful to build your test since you have a visual access to the output you have to test. It also helps to extract content to use for unit test as descirbed in our contribution guide.
Use your inventory
Because it is based on ANTA cli, all your commands use an ANTA inventory and require to get a valid one.
"},{"location":"cli/debug/#get-result-of-an-eos-command","title":"Get result of an EOS command","text":"To run a command, you can leverage run-cmd
entrypoint with following options:
$ anta debug run-cmd --help\nUsage: anta debug run-cmd [OPTIONS]\n\nRun arbitrary command to an EOS device and get result using eAPI\n\nOptions:\n -c, --command TEXT Command to run on EOS using eAPI [required]\n--ofmt [text|json] eAPI format to use. can be text or json\n --api-version, --version TEXT Version of the command through eAPI\n -d, --device TEXT Device from inventory to use [required]\n--log-level, --log TEXT Logging level of the command\n--help Show this message and exit.\n
In practice, this command is very simple to use. Here is an example using show interfaces description
with a JSON
format:
anta debug run-cmd -c \"show interfaces description\" --device ptt015\nrun command show interfaces description on ptt015\n{\n'interfaceDescriptions': {\n'Ethernet8': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n 'Ethernet9': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n 'Ethernet12': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n ...\n}\n
"},{"location":"cli/debug/#get-result-of-an-eos-command-using-templates","title":"Get result of an EOS command using templates","text":"This command allows user to provide an f-string
and a list of dictionary to run a command dynamically. Idea is to help building output for test using such approach.
$ anta debug run-template --help\nUsage: anta debug run-template [OPTIONS]\n\nRun arbitrary command to an EOS device and get result using eAPI\n\nOptions:\n -t, --template TEXT Command template to run on EOS using eAPI\n -p, --params TEXT Command parameters to use with template. Must\n be a JSON string for a list of dict\n [required]\n--ofmt [text|json] eAPI format to use. can be text or json\n --api-version, --version TEXT Version of the command through eAPI\n -d, --device TEXT Device from inventory to use [required]\n--log-level, --log TEXT Logging level of the command\n--help Show this message and exit.\n
In practice, this command is very simple to use. Here is an example using show lldp neighbors
with a JSON
format for only 2 interfaces: Ethernet1 and Ethernet2
anta debug run-template \\\n--params \"[{\"ifd\": \"Ethernet1\"}, {\"ifd\":\"Ethernet2\"}]\" \\\n--template \"show lldp neighbors {ifd}\" \\\n--device ptt015\n\nrun dynmic command show lldp neighbors {ifd} with [{\"ifd\": \"Ethernet1\"}, {\"ifd\":\"Ethernet2\"}] on ptt015\nrun_command = show lldp neighbors Ethernet1 ptt015\n{\n\"tablesLastChangeTime\": 1682498936.0082116,\n \"tablesAgeOuts\": 0,\n \"tablesInserts\": 1,\n \"lldpNeighbors\": [],\n \"tablesDeletes\": 0,\n \"tablesDrops\": 0\n}\nrun_command = show lldp neighbors Ethernet2 ptt015\n{\n\"tablesLastChangeTime\": 1682498936.008321,\n \"tablesAgeOuts\": 0,\n \"tablesInserts\": 1,\n \"lldpNeighbors\": [],\n \"tablesDeletes\": 0,\n \"tablesDrops\": 0\n}\n
"},{"location":"cli/exec/","title":"Execute commands","text":""},{"location":"cli/exec/#execute-commands-on-devices","title":"Execute commands on devices","text":"ANTA CLI also provides a set of entrypoints to execute commands remotely on EOS devices.
"},{"location":"cli/exec/#clear-interfaces-counters","title":"Clear interfaces counters","text":"This command clear interfaces counters on EOS devices defined in your inventory
$ anta exec clear-counters --help\nUsage: anta exec clear-counters [OPTIONS]\n\nClear counter statistics on EOS devices\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
"},{"location":"cli/exec/#collect-a-set-of-commands","title":"Collect a set of commands","text":"This command collects all commands you defined in a catalog. It can be either json
or text
.
$ anta exec snapshot --help\nUsage: anta exec snapshot [OPTIONS]\n\nCollect commands output from devices in inventory\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n -c, --commands-list PATH File with list of commands to grab [env\n var: ANTA_EXEC_SNAPSHOT_COMMANDS_LIST]\n-outut, -o, --output-directory PATH\n Path where to save commands output [env\n var: ANTA_EXEC_SNAPSHOT_OUTPUT_DIRECTORY]\n--log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
And structure of your catalog file should be:
json_format:\n- show version\ntext_format:\n- show agent logs crash\n- show bfd peers\n- show bgp evpn\n
"},{"location":"cli/exec/#get-scheduled-tech-support","title":"Get Scheduled tech-support","text":"EOS comes with a feature that generates tech-support archive every 1 hour by default and save this archive under /mnt/flash/schedule/tech-support
leaf1#show schedule summary\nMaximum concurrent jobs 1\nPrepend host name to logfile: Yes\nName At Time Last Interval Timeout Max Max Logfile Location Status\n Time (mins) (mins) Log Logs\n Files Size\n----------------- ------------- ----------- -------------- ------------- ----------- ---------- --------------------------------- ------\ntech-support now 08:37 60 30 100 - flash:schedule/tech-support/ Success\n\n\nleaf1#bash ls /mnt/flash/schedule/tech-support\nleaf1_tech-support_2023-03-09.1337.log.gz leaf1_tech-support_2023-03-10.0837.log.gz leaf1_tech-support_2023-03-11.0337.log.gz ...\n
As it can be useful for an NRFU to save a very complete state report before a go live, ANTA has implemented a CLI that retrieves these files very easily:
\u276f anta exec collect-tech-support --help\n\nUsage: anta exec collect-tech-support [OPTIONS]\n\nCollect scheduled tech-support from eos devices.\n\nOptions:\n -o, --output PATH Path for tests catalog [default: ./tech-\n support]\n-ssh, --ssh-port INTEGER SSH port to use for connection [default:\n 22]\n--insecure / --secure Disable SSH Host Key validation [default:\n secure]\n--latest INTEGER Number of scheduled show-tech to retrieve\n --configure / --not-configure Ensure device has 'aaa authorization exec\n default local' configured (required for SCP)\n[default: not-configure]\n-t, --tags TEXT List of tags using coma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command [default:\n info]\n--help Show this message and exit.\n
When you run this command, it will retrieve tech-support files and download it locally in a folder and a subfolder per device. You can change the default output folder with the --output
option. ANTA download files from the devices using SCP, all SSH Host Key devices must be trusted prior to run the command, otherwise use the --insecure
option. In order to use SCP with EOS, the configuration aaa authorization exec default local
must be present on the devices. By default, ANTA will not configure this automatically, unless --configure
is specified. It is possible to retrieve only the latest tech-support files using the --latest
option.
\u276f anta exec collect-tech-support --insecure\n[15:27:19] INFO Connecting to devices...\nINFO Copying '/mnt/flash/schedule/tech-support/spine1_tech-support_2023-06-09.1315.log.gz' from device spine1 to 'tech-support/spine1' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf3_tech-support_2023-06-09.1315.log.gz' from device leaf3 to 'tech-support/leaf3' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf1_tech-support_2023-06-09.1315.log.gz' from device leaf1 to 'tech-support/leaf1' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf2_tech-support_2023-06-09.1315.log.gz' from device leaf2 to 'tech-support/leaf2' locally\nINFO Copying '/mnt/flash/schedule/tech-support/spine2_tech-support_2023-06-09.1315.log.gz' from device spine2 to 'tech-support/spine2' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf4_tech-support_2023-06-09.1315.log.gz' from device leaf4 to 'tech-support/leaf4' locally\nINFO Collected 1 scheduled tech-support from leaf2\nINFO Collected 1 scheduled tech-support from spine2\nINFO Collected 1 scheduled tech-support from leaf3\nINFO Collected 1 scheduled tech-support from spine1\nINFO Collected 1 scheduled tech-support from leaf1\nINFO Collected 1 scheduled tech-support from leaf4\n
The output folder will look like this:
\u276f tree tech-support/\ntech-support/\n\u251c\u2500\u2500 leaf1\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf1_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf2\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf2_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf3\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf3_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf4\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf4_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 spine1\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 spine1_tech-support_2023-06-09.1315.log.gz\n\u2514\u2500\u2500 spine2\n \u2514\u2500\u2500 spine2_tech-support_2023-06-09.1315.log.gz\n\n6 directories, 6 files\n
"},{"location":"cli/get-inventory-information/","title":"Get Inventory Information","text":""},{"location":"cli/get-inventory-information/#get-inventory-information","title":"Get Inventory Information","text":"ANTA CLI provides a set of entrypoints to get data from your local inventory.
"},{"location":"cli/get-inventory-information/#get-all-configured-tags","title":"Get all configured tags","text":"Since most commands in anta support tags filtering, this command helps you list all available tags configured in your inventory.
"},{"location":"cli/get-inventory-information/#command-overview","title":"Command overview","text":"anta get tags --help\nUsage: anta get tags [OPTIONS]\n\nGet list of configured tags in user inventory.\n\nOptions:\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
"},{"location":"cli/get-inventory-information/#example","title":"Example","text":"Let\u2019s consider the following inventory:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
To get the list of all configured tags in your CLI, run the following command:
$ anta get tags\nTags found:\n[\n\"all\",\n \"fabric\",\n \"leaf\",\n \"spine\"\n]\nNone\n\n* note that tag all has been added by anta\n
Tip
As you can see, the tag all
has been added even if not explicitely configued in your inventory. This tag is the default tag added to all your devices to run commands against your inventory when you do not provide any specific tag.
"},{"location":"cli/get-inventory-information/#list-devices-in-inventory","title":"List devices in inventory","text":""},{"location":"cli/get-inventory-information/#command-overview_1","title":"Command overview","text":"To get a list of all devices available in your inventory with ANTA, use the following command
anta get inventory --help\nUsage: anta get inventory [OPTIONS]\n\nShow inventory loaded in ANTA.\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--connected / --not-connected Display inventory after connection has been\n created\n --help Show this message and exit.\n
It will give you all information loaded in ANTA inventory from your inventory file.
Tip
By default only information not based on device connection is available. If you want to get information based on connection such as hardware model, you should use the --connected
option.
"},{"location":"cli/get-inventory-information/#example_1","title":"Example","text":"Considering the following inventory file:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
You can get ANTA inventory with the command:
$ anta --username ansible --password ansible get inventory --tags spine\nCurrent inventory content is:\n[\n{\n\"name\": \"spine01\",\n \"host\": \"192.168.0.10\",\n \"username\": \"ansible\",\n \"password\": \"ansible\",\n \"port\": \"443\",\n \"enable_password\": \"None\",\n \"session\": \"<aioeapi.device.Device object at 0x7fa98d0a2d30>\",\n \"hw_model\": \"unset\",\n \"tags\": \"['fabric', 'spine', 'all']\",\n \"timeout\": \"10.0\",\n \"established\": \"False\",\n \"is_online\": \"False\"\n},\n {\n\"name\": \"spine02\",\n \"host\": \"192.168.0.11\",\n \"username\": \"ansible\",\n \"password\": \"ansible\",\n \"port\": \"443\",\n \"enable_password\": \"None\",\n \"session\": \"<aioeapi.device.Device object at 0x7fa98d0a2ac0>\",\n \"hw_model\": \"unset\",\n \"tags\": \"['fabric', 'spine', 'all']\",\n \"timeout\": \"10.0\",\n \"established\": \"False\",\n \"is_online\": \"False\"\n}\n]\nNone\n
"},{"location":"cli/inv-from-cvp/","title":"Inventory from CVP","text":""},{"location":"cli/inv-from-cvp/#create-inventory-from-cloudvision","title":"Create inventory from CloudVision","text":"In a large setup, it can be useful to create your inventory based on CloudVision inventory.
$ anta get from-cvp\nUsage: anta get from-cvp [OPTIONS]\n\nBuild ANTA inventory from Cloudvision\n\nOptions:\n -ip, --cvp-ip TEXT CVP IP Address\n -u, --cvp-username TEXT CVP Username\n -p, --cvp-password TEXT CVP Password / token\n -c, --cvp-container TEXT Container where devices are configured\n -d, --inventory-directory PATH Path to save inventory file\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
Output is an inventory with the name of the container added as a tag for the host:
anta_inventory:\nhosts:\n- host: 192.168.0.13\nname: leaf2\ntags:\n- pod1\n- host: 192.168.0.15\nname: leaf4\ntags:\n- pod2\n
Warning
Current implementation only takes devices directly attached to a specific container when using cli with --cvp-container
option.
If you want to build an inventory based on multiple containers, you can use a bash command as shown below and then manually concatenate files to create a single inventory file.
$ for container in pod01 pod02 spines; do anta get from-cvp -ip <cvp-ip> -u cvpadmin -p cvpadmin -c $container -d test-inventory; done\n\n[12:25:35] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:36] INFO Creating inventory folder /home/tom/Projects/arista/network-test-automation/test-inventory\n WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n\n[12:25:37] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:38] WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n\n[12:25:38] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:39] WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n INFO Inventory file has been created in /home/tom/Projects/arista/network-test-automation/test-inventory/inventory-spines.yml\n
"},{"location":"cli/nrfu/","title":"NRFU","text":""},{"location":"cli/nrfu/#execute-nrfu-testing","title":"Execute NRFU testing","text":"All the NRFU testing commands are placed under anta nrfu
and provide different rendering options:
- Table view
- JSON view
- Text view
- Custom template view
anta nrfu\nUsage: anta nrfu [OPTIONS] COMMAND [ARGS]...\n\n Run NRFU against inventory devices\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n json ANTA command to check network state with JSON result\n table ANTA command to check network states with table result\n text ANTA command to check network states with text result\n tpl-report ANTA command to check network state with templated report\n
All of these commands require the following input:
- A path to a catalog of tests to execute (
--catalog
) - A list of tags if they are part of your inventory (
--tags
). List is comma separated
"},{"location":"cli/nrfu/#nrfu-with-text-rendering","title":"NRFU with text rendering","text":"This rendering is a pure text report for every test run on all devices. It comes with some options:
- Search (
--search
) for a regexp pattern in hostname and test name - Option to skip (
--skip-error
) tests in error (not failure) because of a connectivity issue or unsupported command
Example output
$ anta nrfu text --tags pod1 --catalog nrfu/leaf.yml\nleaf2 :: VerifyMlagStatus :: SUCCESS\nleaf2 :: VerifyMlagInterface :: SUCCESS\nleaf2 :: VerifyMlagConfigSanity :: SUCCESS\nleaf2 :: VerifyInterfaceUtilization :: SUCCESS\nleaf2 :: VerifyInterfaceErrors :: SUCCESS\nleaf2 :: VerifyInterfaceDiscards :: SUCCESS\nleaf2 :: VerifyInterfaceErrDisabled :: SUCCESS\nleaf2 :: VerifyInterfaceStatus :: SUCCESS\nleaf2 :: VerifyStormControlDrop :: SKIPPED (VerifyStormControlDrop test is not supported on cEOSLab.)\nleaf2 :: VerifyPortChannel :: SUCCESS\nleaf2 :: VerifyIllegalLacp :: SUCCESS\nleaf2 :: VerifyLoopbackCount :: FAILURE (Found 3 Loopbacks when expecting 2)\nleaf2 :: VerifySvi :: SUCCESS\n[...]\n
"},{"location":"cli/nrfu/#nrfu-with-table-report","title":"NRFU with table report","text":"This rendering prints results in a nice table supporting grep filtering. It comes with its own set of options:
- Search (
--search
) for a pattern in hostname and test name. - Option to group (
--group-by
) and summarize results. You can group by host
or test
.
$ anta check table -t pod1 -c nrfu/cudi.yml\n
You can also group per host or per test to get a summary view in case of large setup
"},{"location":"cli/nrfu/#nrfu-with-json-output","title":"NRFU with JSON output","text":"This command is helpful to generate a JSON and then pass it to another tool for reporting for instance. Only one option is available to save output to a file (--output
)
$ anta check json -t pod1 -c nrfu/leaf.yml\n[\n{\n\"name\": \"leaf01\",\n \"test\": \"VerifyZeroTouch\",\n \"test_category\": [\n\"configuration\"\n],\n \"test_description\": \"Verifies ZeroTouch is disabled.\",\n \"result\": \"success\",\n \"messages\": []\n},\n {\n\"name\": \"leaf01\",\n \"test\": \"VerifyRunningConfigDiffs\",\n \"test_category\": [\n\"configuration\"\n],\n \"test_description\": \"\",\n \"result\": \"success\",\n \"messages\": []\n},\n]\n
"},{"location":"cli/nrfu/#nrfu-with-your-own-report","title":"NRFU with your own report","text":"Because you may want to have a specific report format, ANTA provides a CLI option to build report based on Jinja2 template.
$ anta nrfu tpl-report -c .personal/catalog-class.yml -tpl .personal/test_template.j2\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/catalog-class.yml \u2502\n\u2502 - Template: .personal/test_template.j2 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n* VerifyZeroTouch is SUCCESS for spine01\n* VerifyRunningConfigDiffs is SUCCESS for spine01\n* VerifyInterfaceUtilization is SUCCESS for spine01\n
And the template .personal/test_template.j2
is a pure Jinja2 template:
$ cat .personal/test_template.j2\n{% for d in data %}\n* {{ d.test }} is [green]{{ d.result | upper}}[/green] for {{ d.name }}\n{% endfor %}\n
In this context, Jinja2 template can access to all TestResult
elements with their values as described in this documentation.
An option is available to save the generated report into a text file:
# Run ANTA\n$ anta nrfu tpl-report -c .personal/catalog-class.yml -tpl .personal/test_template.j2 -o .personal/demo.txt\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/catalog-class.yml \u2502\n\u2502 - Template: .personal/test_template.j2 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n* VerifyZeroTouch is SUCCESS for spine01\n* VerifyRunningConfigDiffs is SUCCESS for spine01\n* VerifyInterfaceUtilization is SUCCESS for spine01\n\n# Display saved report\n$ cat .personal/demo.txt\n* VerifyZeroTouch is [green]SUCCESS[/green] for spine01\n* VerifyRunningConfigDiffs is [green]SUCCESS[/green] for spine01\n* VerifyInterfaceUtilization is [green]SUCCESS[/green] for spine01\n
"},{"location":"cli/overview/","title":"Overview","text":""},{"location":"cli/overview/#cli-overview","title":"CLI overview","text":"ANTA comes with a CLI to execute all the supported actions. If you want to build your own tool, you should visit this page where we describe how to use ANTA as a python library
To invoke anta, open a shell window and then enter anta
Usage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --version Show the version and exit.\n --username TEXT Username to connect to EOS [env var:\n ANTA_USERNAME; required]\n--password TEXT Password to connect to EOS [env var:\n ANTA_PASSWORD; required]\n--timeout INTEGER Global connection timeout [env var:\n ANTA_TIMEOUT; default: 5]\n--insecure / --secure Disable SSH Host Key validation [env var:\n ANTA_INSECURE; default: secure]\n--enable-password TEXT Enable password if required to connect [env\n var: ANTA_ENABLE_PASSWORD]\n-i, --inventory FILE Path to the inventory YAML file [env var:\n ANTA_INVENTORY; required]\n--log-level, --log [CRITICAL|ERROR|WARNING|INFO|DEBUG]\nANTA logging level [env var:\n ANTA_LOG_LEVEL; default: INFO]\n--ignore-status Always exit with success [env var:\n ANTA_IGNORE_STATUS]\n--ignore-error Only report failures and not errors [env\n var: ANTA_IGNORE_ERROR]\n--help Show this message and exit.\n\nCommands:\n debug Debug commands for building ANTA\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n
"},{"location":"cli/overview/#anta-parameters","title":"ANTA parameters","text":"Some parameters are globally required and can be passed to anta via cli or via ENV VAR:
$ anta --username tom --password arista123 --inventory inventory.yml <anta cli>\n
Or if you prefer to set ENV VAR:
# Save information for anta cli\n$ export ANTA_USERNAME=tom\n$ export ANTA_PASSWORD=arista123\n$ export ANTA_INVENTORY=inventory.yml\n\n# Run cli\n$ anta <anta cli>\n
"},{"location":"cli/overview/#anta-exitcodes","title":"ANTA ExitCodes","text":"For all subcommands except nrfu, anta
wil return with the exit code 0.
For the nrfu commands, anta
is using the following exit codes:
- Exit code 0 - All tests passed successfully.
- Exit code 1 - Tests were run but at least one test returned a failure.
- Exit code 2 - Tests were run but at least one test returned an error.
- Exit code 3 - Internal error happened while executing tests (not used today).
It is possible to ignore the status when running the nrfu command by using anta --ignore-status nrfu
and in that case the exit code will always be 0.
It is possible to ignore errors when running the nrfu command by using anta --ignore-error nrfu
and in that case the exit code will either be 0 if all tests succeeded or 1 if any test failed.
"},{"location":"cli/overview/#shell-completion","title":"Shell Completion","text":"You can enable shell completion for the anta cli:
ZSHBASH If you use ZSH shell, add the following line in your ~/.zshrc
:
eval \"$(_ANTA_COMPLETE=zsh_source anta)\" > /dev/null\n
With bash, add the following line in your ~/.bashrc
:
eval \"$(_ANTA_COMPLETE=bash_source anta)\" > /dev/null\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#anta-documentation","title":"ANTA Documentation","text":"This website provides generic documentation related to the Arista Network Test Automation framework (ANTA)
"},{"location":"#arista-network-test-automation-anta-framework","title":"Arista Network Test Automation (ANTA) Framework","text":"This repository is a Python package to automate tests on Arista devices.
- The package name is ANTA, which stands for Arista Network Test Automation.
- This package provides a set of tests to validate the state of your network.
- This package can be imported in Python scripts:
- To automate NRFU (Network Ready For Use) test on a preproduction network
- To automate tests on a live network (periodically or on demand)
This repository comes with a cli to run Arista Network Test Automation (ANTA) framework using your preferred shell:
# Install ANTA\npip install anta\n\n# Run ANTA cli\n$ anta\nUsage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --username TEXT Username to connect to EOS [env var: ANTA_USERNAME]\n--password TEXT Password to connect to EOS [env var: ANTA_PASSWORD]\n--timeout INTEGER Connection timeout (default 5) [env var: ANTA_TIMEOUT]\n--enable-password TEXT Enable password if required to connect [env var: ANTA_ENABLE_PASSWORD]\n-i, --inventory PATH Path to your inventory file [env var: ANTA_INVENTORY]\n--timeout INTEGER Connection timeout (default 5) [env var: ANTA_TIMEOUT]\n--help Show this message and exit.\n\nCommands:\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n
"},{"location":"contribution/","title":"Contributions","text":""},{"location":"contribution/#how-to-contribute-to-anta","title":"How to contribute to ANTA","text":"Contribution model is based on a fork-model. Don\u2019t push to arista-netdevops-community/anta directly. Always do a branch in your repository and create a PR.
To help development, open your PR as soon as possible even in draft mode. It helps other to know on what you are working on and avoid duplicate PRs.
"},{"location":"contribution/#install-repository","title":"Install repository","text":"Run these commands to install:
- The package ANTA and its dependencies
- ANTA cli executable.
# Clone repository\ngit clone https://github.com/arista-netdevops-community/anta.git\ncd network-test-automation\n\n# Install module in editable mode\npip install -e .\n
Run these commands to verify:
# Check python installation\n$ pip list\n\n# Check version using cli\n$ anta --version\nanta, version 0.5.0\n
"},{"location":"contribution/#install-development-requirements","title":"Install development requirements","text":"Run pip to install anta and its developement tools.
pip install 'anta[dev]'\n
This command has to be done after you install repository with commands provided in previous section.
Then, tox is configued with few environment to run CI locally:
tox list\ndefault environments:\nclean -> run the test driver with /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/bin/python3.9\npy38 -> run the test driver with py38\npy39 -> run the test driver with py39\npy310 -> run the test driver with py310\nlint -> check the code style\ntype -> check typing\nreport -> run the test driver with /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/bin/python3.9\n\nadditional environments:\n3.8 -> run the test driver with 3.8\n3.9 -> run the test driver with 3.9\n3.10 -> run the test driver with 3.10\n
"},{"location":"contribution/#code-linting","title":"Code linting","text":"tox -e lint\n[...]\nlint: commands[0]> flake8 --max-line-length=165 --config=/dev/null anta\nlint: commands[1]> flake8 --max-line-length=165 --config=/dev/null scripts\nlint: commands[2]> flake8 --max-line-length=165 --config=/dev/null tests\nlint: commands[3]> pylint anta\n\n--------------------------------------------------------------------\nYour code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)\n\nlint: commands[4]> pylint scripts\n\n-------------------------------------------------------------------\nYour code has been rated at 10.00/10 (previous run: 7.15/10, +2.85)\n\n.pkg: _exit> python /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/lib/python3.9/site-packages/\\\npyproject_api/_backend.py True setuptools.build_meta\n lint: OK (28.37=setup[7.03]+cmd[0.38,0.23,0.25,11.07,9.41] seconds)\ncongratulations :) (28.45 seconds)\n
"},{"location":"contribution/#code-typing","title":"Code Typing","text":"tox -e type\n\ntype: commands[0]> mypy --config-file=pyproject.toml anta\nSuccess: no issues found in 38 source files\ntype: commands[1]> mypy --config-file=pyproject.toml scripts\nSuccess: no issues found in 6 source files\n.pkg: _exit> python /home/tom/.pyenv/versions/3.9.9/envs/arista-anta/lib/python3.9/site-packages/\\\npyproject_api/_backend.py True setuptools.build_meta\n type: OK (28.80=setup[24.54]+cmd[3.35,0.90] seconds)\ncongratulations :) (28.89 seconds)\n
"},{"location":"contribution/#unit-tests","title":"Unit tests","text":"To keep high quality code, we require to provide a Pytest for every tests implemented in ANTA.
All submodule should have its own pytest section under tests/units/anta_tests/<submodule-name>
. In this directory, you should have 3 files:
__init__.py
: Just because it is used as a python module data.py
: Where all your parametrize go. So all your test information should be located here test_exc.py
: Pytest file with test definition.
A pytest definition should be similar to this template:
# -*- coding: utf-8 -*-\n\n\"\"\"\nTests for anta.tests.hardware.py\n\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport logging\nfrom typing import Any\nfrom unittest.mock import MagicMock\n\nimport pytest\n\nfrom anta.tests.hardware import VerifyAdverseDrops\nfrom tests.lib.utils import generate_test_ids_list\n\nfrom .data import INPUT_<TEST_NAME>\n\n@pytest.mark.parametrize(\"test_data\", INPUT_<TEST_NAME>, ids=generate_test_ids_list(INPUT_<TEST_NAME>))\ndef test_<TEST_CASE>(mocked_device: MagicMock, test_data: Any) -> None:\n\"\"\"Check <TEST_CASE>.\"\"\"\n\n logging.info(f\"Mocked device is: {mocked_device.host}\")\n logging.info(f\"Mocked HW is: {mocked_device.hw_model}\")\n\n test = <TEST_CASE>(mocked_device, eos_data=test_data[\"eos_data\"])\n asyncio.run(test.test())\n\n logging.debug(f\"test result is: {test.result}\")\n\n assert str(test.result.name) == mocked_device.name\n assert test.result.result == test_data[\"expected_result\"]\n
The mocked_device
object is a fixture defined in Pytest to represent an InventoryDevice and the parametrize test_data
is a list of dictionries with structure:
INPUT_RUNNING_CONFIG: List[Dict[str, Any]] = [\n # Test Case #1\n {\n \"name\": \"failure\",\n \"eos_data\": [\"blah blah\"],\n \"side_effect\": None,\n \"expected_result\": \"failure\",\n \"expected_messages\": [\"blah blah\"]\n },\n # Test Case #2\n {\n ...\n },\n]\n
Where we have:
name
: Name of the test displayed by Pytest eos_data
: a list of data coming from EOS. side_effect
: defined for futur use. expected_result
: Result we expect for this test expected_messages
: Optional messages we expect for the test.
Use Anta CLI to get test data
To complete this block, you can use anta debug
commands to get AntaTestCommand
output to use in your test.
"},{"location":"contribution/#git-pre-commit-hook","title":"Git Pre-commit hook","text":"pip install pre-commit\npre-commit install\n
When running a commit or a pre-commit check:
\u276f echo \"import foobaz\" > test.py && git add test.py\n\u276f pre-commit\npylint...................................................................Failed\n- hook id: pylint\n- exit code: 22\n\n************* Module test\ntest.py:1:0: C0114: Missing module docstring (missing-module-docstring)\ntest.py:1:0: E0401: Unable to import 'foobaz' (import-error)\ntest.py:1:0: W0611: Unused import foobaz (unused-import)\n
"},{"location":"contribution/#test-your-documentation","title":"Test your documentation","text":"Writing documentation is crucial but managing links can be cumbersome. To be sure there is no 404, you can use muffet
with this cli:
muffet -c 2 --color=always http://127.0.0.1:8000 -e fonts.gstatic.com\n
"},{"location":"contribution/#continuous-integration","title":"Continuous Integration","text":"GitHub actions is used to test git pushes and pull requests. The workflows are defined in this directory. We can view the result here
"},{"location":"getting-started/","title":"Getting Started","text":""},{"location":"getting-started/#getting-started","title":"Getting Started","text":"This section shows how to use ANTA with basic configuration. All examples are based on Arista Test Drive (ATD) topology you can access by reaching out to your prefered SE.
"},{"location":"getting-started/#installation","title":"Installation","text":"The easiest way to intall ANTA package is to run Python (>=3.8
) and its pip package to install:
pip install anta\n
For more details about how to install package, please see the requirements and intallation section.
"},{"location":"getting-started/#configure-arista-eos-devices","title":"Configure Arista EOS devices","text":"First, you need to configure your management interface
vrf instance MGMT\n!\ninterface Management0\n description oob_management\n vrf MGMT\n ip address 192.168.0.10/24\n!\n
Then, configure access to eAPI:
!\nmanagement api http-commands\n protocol https port 443\n no shutdown\n vrf MGMT\n no shutdown\n !\n!\n
"},{"location":"getting-started/#create-your-inventory","title":"Create your inventory","text":"First, we need to list devices we want to test. You can create a file manually with this format:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
You can read more details about how to build your inventory here
"},{"location":"getting-started/#test-catalog","title":"Test Catalog","text":"To test your network, it is important to define a test catalog to list all the tests to run against your inventory. Test catalog references python functions into a yaml file. This file can be loaded by anta.loader.py
The structure to follow is like:
<anta_tests_submodule>:\n- <anta_tests_submodule function name>:\n<test function option>:\n<test function option value>\n
You can read more details about how to build your catalog here
Here is an example for basic things:
# Load anta.tests.software\nanta.tests.software:\n- VerifyEosVersion: # Verifies the device is running one of the allowed EOS version.\nversions: # List of allowed EOS versions.\n- 4.25.4M\n- 4.26.1F\n- '4.28.3M-28837868.4283M (engineering build)'\n- VerifyTerminAttrVersion:\nversions:\n- v1.22.1\n\nanta.tests.system:\n- VerifyUptime: # Verifies the device uptime is higher than a value.\nminimum: 1\n- VerifyNtp:\n- VerifySyslog:\n\nanta.tests.mlag:\n- VerifyMlagStatus:\n- VerifyMlagInterface:\n- VerifyMlagConfigSanity:\n\nanta.tests.configuration:\n- VerifyZeroTouch: # Verifies ZeroTouch is disabled.\n- VerifyRunningConfigDiffs:\n
"},{"location":"getting-started/#test-your-network","title":"Test your network","text":"To test EOS devices, this package comes with a generic CLI entrypoint to run tests in your network. It requires an inventory file as well as a test catalog.
This entrypoint has multiple options to manage test coverage and reporting.
# Generic ANTA options\n$ anta\nUsage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --version Show the version and exit.\n --username TEXT Username to connect to EOS [env var: ANTA_USERNAME;\nrequired]\n--password TEXT Password to connect to EOS [env var: ANTA_PASSWORD;\nrequired]\n--timeout INTEGER Connection timeout (default 5) [env var:\n ANTA_TIMEOUT]\n--enable-password TEXT Enable password if required to connect [env var:\n ANTA_ENABLE_PASSWORD]\n-i, --inventory PATH Path to your inventory file [env var:\n ANTA_INVENTORY; required]\n--help Show this message and exit.\n\nCommands:\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n\n\n\n# NRFU part of ANTA\n$ anta nrfu\nUsage: anta nrfu [OPTIONS] COMMAND [ARGS]...\n\n Run NRFU against inventory devices\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n json ANTA command to check network state with JSON result\n table ANTA command to check network states with table result\n text ANTA command to check network states with text result\n
Default output is a table format listing all test results, and it can be changed to a report per test case or per host
"},{"location":"getting-started/#default-report-using-table","title":"Default report using table","text":"anta \\\n--username tom \\\n--password arista123 \\\n--enable-password t \\\n--inventory .personal/inventory_atd.yml \\\nnrfu table --tags leaf --catalog .personal/tests-bases.yml\n\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/tests-bases.yml \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n All tests results\n\u250f\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2533\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n\u2503 Device IP \u2503 Test Name \u2503 Test Status \u2503 Message(s) \u2503\n\u2521\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2547\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2529\n\u2502 leaf01 \u2502 VerifyEosVersion \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyTerminAttrVersion \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyUptime \u2502 success \u2502 \u2502\n\u2502 leaf01 \u2502 VerifyNtp \u2502 failure \u2502 not sync with NTP server (NTP is disabled.) \u2502\n\u2502 leaf01 \u2502 VerifySyslog \u2502 failure \u2502 Device has some log messages with a severity WARNING or higher \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n
"},{"location":"getting-started/#report-in-text-mode","title":"Report in text mode","text":"$ anta \\\n --username tom \\\n --password arista123 \\\n --enable-password t \\\n --inventory .personal/inventory_atd.yml \\\n nrfu text --tags leaf --catalog .personal/tests-bases.yml\n\nleaf01 :: VerifyEosVersion :: SUCCESS\nleaf01 :: VerifyTerminAttrVersion :: SUCCESS\nleaf01 :: VerifyUptime :: SUCCESS\nleaf01 :: VerifyNtp :: FAILURE (not sync with NTP server (NTP is disabled.))\nleaf01 :: VerifySyslog :: FAILURE (Device has some log messages with a severity WARNING or higher)\n...\n
"},{"location":"getting-started/#report-per-host","title":"Report per host","text":"$ anta \\\n--username tom \\\n--password arista123 \\\n--enable-password t \\\n--inventory .personal/inventory_atd.yml \\\nnrfu json --tags leaf --catalog .personal/tests-bases.yml\n\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 JSON results of all tests \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n[\n{\n\"name\": \"leaf01\",\n \"test\": \"VerifyEosVersion\",\n \"result\": \"success\",\n \"messages\": \"[]\"\n},\n {\n\"name\": \"leaf01\",\n \"test\": \"VerifyTerminAttrVersion\",\n \"result\": \"success\",\n \"messages\": \"[]\"\n},\n...\n]\n
You can find more information under the usage section of the website
"},{"location":"requirements-and-installation/","title":"Installation","text":""},{"location":"requirements-and-installation/#anta-requirements","title":"ANTA Requirements","text":""},{"location":"requirements-and-installation/#python-version","title":"Python version","text":"Python 3 (>=3.8
and =<3.10
) is required:
python --version\nPython 3.9.9\n
"},{"location":"requirements-and-installation/#install-anta-package","title":"Install ANTA package","text":"This installation will deploy tests collection, scripts and all their Python requirements.
The ANTA package and the cli require some packages that are not part of the Python standard library. They are indicated in the pyproject.toml file
"},{"location":"requirements-and-installation/#install-from-pypi-server","title":"Install from Pypi server","text":"pip install anta\n
"},{"location":"requirements-and-installation/#install-anta-from-github","title":"Install ANTA from github","text":"pip install git+https://github.com/arista-netdevops-community/anta.git\n
You can even specify the branch, tag or commit:
<anta-repository>@<cool-feature-branch>
<anta-repository>@<cool-tag>
<anta-repository>@<cool-hash>
"},{"location":"requirements-and-installation/#check-installation","title":"Check installation","text":"Run these commands to verify:
# Check ANTA has been installed in your python path\npip list | grep anta\n\n# Check scripts are in your $PATH\n# Path may differ but it means CLI is in your path\nwhich anta\n/home/tom/.pyenv/shims/anta\n\n# Chck ANTA version\nanta --version\nanta, version 0.5.0\n
"},{"location":"requirements-and-installation/#eos-requirements","title":"EOS Requirements","text":"To get ANTA working, your Arista EOS devices must have the following configuration (assuming you connect to the device using Management interface in MGMT VRF):
configure\n!\nvrf instance MGMT\n!\ninterface Management1\n description oob_management\n vrf MGMT\n ip address 10.73.1.105/24\n!\nend\n
Enable eAPI on the MGMT vrf:
configure\n!\nmanagement api http-commands\n protocol https port 443\n no shutdown\n vrf MGMT\n no shutdown\n!\nend\n
Now the swicth accepts on port 443 in the MGMT VRF HTTPS requests containing a list of CLI commands.
Run these EOS commands to verify:
show management http-server\nshow management api http-commands\n
"},{"location":"usage-inventory-catalog/","title":"Inventory & Tests catalog","text":""},{"location":"usage-inventory-catalog/#inventory-catalog-definition","title":"Inventory & Catalog definition","text":"This page describes how to create an inventory and a tests catalog.
"},{"location":"usage-inventory-catalog/#create-an-inventory-file","title":"Create an inventory file","text":"anta
cli needs an inventory file to list all devices to tests. This inventory is a YAML file with the folowing keys:
anta_inventory:\nhosts:\n- host: < ip address value >\nport: < TCP port for eAPI. Default is 443 (Optional)>\nname: < name to display in report. Default is host:port (Optional) >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\nnetworks:\n- network: < network using CIDR notation >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\nranges:\n- start: < first ip address value of the range >\nend: < last ip address value of the range >\ntags: < list of tags to use to filter inventory during tests. Default is ['all']. (Optional) >\n
Your inventory file can be based on any of these 3 keys and shall start with anta_inventory
key. A full description of inventory model is available in API documentation
The next output is an inventory example:
---\nanta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\nnetworks:\n- network: '192.168.110.0/24'\ntags: ['fabric', 'leaf']\nranges:\n- start: 10.0.0.9\nend: 10.0.0.11\ntags: ['fabric', 'l2leaf']\n
"},{"location":"usage-inventory-catalog/#test-catalog","title":"Test Catalog","text":"In addition to your inventory file, you also have to define a catalog of tests to execute against all your devices. This catalogue list all your tests and their parameters.
Its format is a YAML file and keys are tests functions inherited from the python path. Let\u2019s take an example below:
"},{"location":"usage-inventory-catalog/#default-tests-catalog","title":"Default tests catalog","text":"All tests are located under anta.tests
module and are categorised per family (one submodule). So to run test for software version, you can do:
anta.tests.software:\n- VerifyEosVersion:\n
It will load the test VerifyEosVersion
located in anta.tests.software
. But since this function has parameters, we will create a catalog with the following structure:
anta.tests.software:\n- VerifyEosVersion:\n# List of allowed EOS versions.\nversions:\n- 4.25.4M\n- 4.26.1F\n
To get a list of all available tests and their respective parameters, you can read the tests section of this website.
The following example gives a very minimal tests catalog you can use in almost any situation
---\n# Load anta.tests.software\nanta.tests.software:\n# Verifies the device is running one of the allowed EOS version.\n- VerifyEosVersion:\n# List of allowed EOS versions.\nversions:\n- 4.25.4M\n- 4.26.1F\n\n# Load anta.tests.system\nanta.tests.system:\n# Verifies the device uptime is higher than a value.\n- VerifyUptime:\nminimum: 1\n\n# Load anta.tests.configuration\nanta.tests.configuration:\n# Verifies ZeroTouch is disabled.\n- VerifyZeroTouch:\n- VerifyRunningConfigDiffs:\n
If your test is based on AntaTestTemplate
, you have to provide inputs for EOS CLI template by using tpl_options
list:
anta.tests.routing.bgp:\n- VerifyBGPIPv4UnicastCount:\nnumber: 3\ntemplate_params:\n- vrf: default\n- vrf: customer-01\n
Which is required for the following test definition:
class VerifyBGPIPv4UnicastCount(AntaTest):\n\"\"\"\n ...\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastCount\"\n description = \"...\"\n categories = [\"routing\", \"bgp\"]\n template = AntaTestTemplate(template=\"show bgp ipv4 unicast summary vrf {vrf}\")\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n pass\n
"},{"location":"usage-inventory-catalog/#custom-tests-catalog","title":"Custom tests catalog","text":"In case you want to leverage your own tests collection, you can use the following syntax:
<your package name>:\n- <your test in your package name>:\n
So for instance, it could be:
titom73.tests.system:\n- VerifyPlatform:\ntype: ['cEOS-LAB']\n
How to create custom tests
To create your custom tests, you should refer to this following documentation
"},{"location":"advanced_usages/as-python-lib/","title":"ANTA as python lib","text":""},{"location":"advanced_usages/as-python-lib/#how-to-use-anta-as-a-python-library","title":"How to use ANTA as a Python Library","text":"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.
"},{"location":"advanced_usages/as-python-lib/#inventory-manager","title":"Inventory Manager","text":"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.
from anta.inventory import AntaInventory\n\ninventory = AntaInventory.parse(\n inventory_file=\"inventory.yml\",\n username=\"username\",\n password=\"password\",\n enable_password=\"enable\",\n timeout=1,\n)\n
Then it is easy to get all devices or only active devices with the following method:
# print the non reachable devices\nfor device in inventory.get_inventory(established_only=False):\n if device.established is False:\n print(f\"Could not connect to device {device.host}\")\n\n# run an EOS commands list on the reachable devices from the inventory\nfor device in inventory.get_inventory(established_only=True):\n device.session.runCmds(\n 1, [\"show version\", \"show ip bgp summary\"]\n )\n
You can find the ANTA Inventory module here.
How to create your inventory file Please visit this dedicated section for how to use inventory and catalog files.
"},{"location":"advanced_usages/as-python-lib/#use-tests-from-anta","title":"Use tests from ANTA","text":"All the test functions are based on the exact same input and returns a generic structure with different information.
"},{"location":"advanced_usages/as-python-lib/#test-input","title":"Test input","text":"Any test input is based on an InventoryDevice
object and a list of options. Here is an example to check uptime and check it is higher than minimum
option.
def verify_uptime(device: InventoryDevice, minimum: int = None) -> TestResult:\n
In general, InventoryDevice
is an object created by AntaInventory
. But it can be manually generated by following required data model.
Here is an example of a list of InventoryDevice
[\n {\n \"InventoryDevice(host=IPv4Address('192.168.0.17')\",\n \"username='ansible'\",\n \"password='ansible'\",\n \"session=<ServerProxy for ansible:ansible@192.168.0.17/command-api>\",\n \"url='https://ansible:ansible@192.168.0.17/command-api'\",\n \"established=True\",\n \"is_online=True\",\n \"hw_model=cEOS-LAB\",\n },\n\n {\n \"InventoryDevice(host=IPv4Address('192.168.0.2')\",\n \"username='ansible'\",\n \"password='ansible'\",\n \"session=None\",\n \"url='https://ansible:ansible@192.168.0.2/command-api'\",\n \"established=False\"\n \"is_online=False\",\n \"tags\": ['dc1', 'spine', 'pod01'],\n \"hw_model=unset\",\n }\n]\n
"},{"location":"advanced_usages/as-python-lib/#test-output","title":"Test output","text":"All tests return a TestResult structure with the following elements:
result
: Can be success
, skipped
, failure
, error
and report result of the test host
: IP address of the tested device test
: Test name runs on host
message
: Optional message returned by the test.
"},{"location":"advanced_usages/as-python-lib/#test-structure","title":"Test structure","text":"All tests are built on a class named AntaTest
which provides a complete toolset for a test:
- Object creation
- Test definition
- TestResult definition
- Abstracted method to collect data
This approach means each time you create a test it will be based on this AntaTest
class. Besides that, you will have to provide some elements:
name
: Name of the test description
: A human readable description of your test categories
: a list of categories to sort test. commands
: a list of command to run. This list must be a list of AntaTestCommand
which is described in the next part of this document.
Here is an example of a hardware test related to device temperature:
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
When you run the test, object will automatically call its anta.models.AntaTest.collect()
method to get device output. This method does a loop to call anta.inventory.models.InventoryDevice.collect()
methods which is in charge of managing device connection and how to get data.
run test offline You can also pass eos data directly to your test if you want to validate data collected in a different workflow. An example is provided below just for information:
test = VerifyTemperature(mocked_device, eos_data=test_data[\"eos_data\"])\nasyncio.run(test.test())\n
test function is always the same and must be defined with the @AntaTest.anta_test
decorator. This function takes at least one argument which is a anta.inventory.models.InventoryDevice
object and can have multiple additional parameters depending of your test definition. All parameters must come with a default value and the test function should validate the parameters values.
class VerifyTemperature(AntaTest):\n ...\n @AntaTest.anta_test\n def test(self) -> None:\n pass\n\nclass VerifyTransceiversManufacturers(AntaTest):\n ...\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n pass\n
The test itself does not return any value, but the result is directly availble from your object and exposes a anta.result_manager.models.TestResult
object with result, name of the test and optional messages.
from anta.tests.hardware import VerifyTemperature\n\ntest = VerifyTemperature(mocked_device, eos_data=test_data[\"eos_data\"])\nasyncio.run(test.test())\nassert test.result.result == \"success\"\n
"},{"location":"advanced_usages/as-python-lib/#commands-for-test","title":"Commands for test","text":"To make it easier to get data, ANTA defines 2 different classes to manage commands to send to device:
"},{"location":"advanced_usages/as-python-lib/#antamodelsantatestcommand","title":"anta.models.AntaTestCommand
","text":"Abstract a command with following information:
- Command to run,
- Ouput format expected
- eAPI version
- Output of the command
Usage example:
from anta.models import AntaTestCommand\n\ncmd1 = AntaTestCommand(command=\"show zerotouch\")\ncmd2 = AntaTestCommand(command=\"show running-config diffs\", ofmt=\"text\")\n
"},{"location":"advanced_usages/as-python-lib/#antamodelsantatesttemplate","title":"anta.models.AntaTestTemplate
","text":"Because some command can require more dynamic than just a command with no parameter provided by user, ANTA supports command template: you define a template in your test class and user provide parameters when creating test object.
class RunArbitraryTemplateCommand(AntaTest):\n\"\"\"\n Run an EOS command and return result\n Based on AntaTest to build relevant output for pytest\n \"\"\"\n\n name = \"Run aributrary EOS command\"\n description = \"To be used only with anta debug commands\"\n template = AntaTestTemplate(template=\"show interfaces {ifd}\")\n categories = [\"debug\"]\n\n @AntaTest.anta_test\n def test(self) -> None:\n errdisabled_interfaces = [interface for interface, value in response[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n ...\n\n\nparams = [{\"ifd\": \"Ethernet2\"}, {\"ifd\": \"Ethernet49/1\"}]\nrun_command1 = RunArbitraryTemplateCommand(device_anta, params)\n
In this example, test waits for interfaces to check from user setup and will only check for interfaces in params
"},{"location":"advanced_usages/custom-tests/","title":"Create your own Library","text":""},{"location":"advanced_usages/custom-tests/#create-your-own-custom-tests","title":"Create your own custom tests","text":"This documentation applies for both create tests in ANTA package or your custom package.
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\u2019s guide to package Python code. We assume it is well known and we won\u2019t focus on this aspect. Thus, your package must be impartable by ANTA hence available in $PYTHONPATH
by any method.
"},{"location":"advanced_usages/custom-tests/#generic-approach","title":"Generic approach","text":"ANTA comes with a class to use to build test. This class provides all the toolset required to define, collect and test data. The next code is an example of how to use ANTA to build a test
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"advanced_usages/custom-tests/#python-imports","title":"Python imports","text":""},{"location":"advanced_usages/custom-tests/#mandatory-imports","title":"Mandatory imports","text":"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. anta.models.AntaTest
: class that gives you all the tooling for your test anta.models.AntaTestCommand
: A class to abstract an Arista EOS command
from anta.models import AntaTest, AntaTestCommand\n\n\nclass VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK.\n \"\"\"\n ...\n\n @AntaTest.anta_test\n def test(self) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#optional-anta-imports","title":"Optional ANTA imports","text":"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\n\n\nclass VerifyTransceiversManufacturers(AntaTest):\n ...\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#optional-python-imports","title":"Optional python imports","text":"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.
If your test development is part of a pull request for ANTA, it is stringly advised to also import typing
since our code testing requires to be compatible with Mypy.
"},{"location":"advanced_usages/custom-tests/#code-for-a-test","title":"Code for a test","text":"A test is a python class where a test function is defined and will be run by the framework. So first you need to declare your class and then define your test function.
"},{"location":"advanced_usages/custom-tests/#create-test-class","title":"Create Test Class","text":"To create class, you have to provide 4 elements:
Metadata information
name
: Name of the test description
: A human readable description of your test categories
: a list of categories to sort test.
Commands to run
commands
: a list of command to run. This list must be a list of AntaTestCommand
which is described in the next part of this document. template
: a command template (AntaTestTemplate
) to run where variables are provided during test execution.
It is either commands
or template
. But not both.
from __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, List, Optional, cast\n\nfrom anta.models import AntaTest, AntaTestCommand\n\n\nclass <YourTestName>(AntaTest):\n\"\"\"\n <a docstring description of your test>\n \"\"\"\n\n name = \"YourTestName\" # should be your class name\n description = \"<test description in human reading format>\"\n categories = [\"<a list of arbitrary categories>\"]\n commands = [\n AntaTestCommand(\n command=\"<eos command to run>\",\n ofmt=\"<command format output>\",\n version=\"<eapi version to use>\"\n )\n ]\n
This class will inherit methods from AntaTest and specfically the __init__(self,...)
method to build your object. This function takes following arguments when you instantiate an object:
device (InventoryDevice)
: Device object where to test happens. template_params
: If template is used in the test definition, then we provide data to build list of commands. eos_data
: Potential EOS data to pass if we don\u2019t want to connect to device to grab data. labels
: a list of labels. It is not used yet and it is for futur use.
"},{"location":"advanced_usages/custom-tests/#function-definition","title":"Function definition","text":"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:
class <YourTestName>(AntaTest):\n ...\n def test(self) -> None:\n pass\n
If you want to support option in your test, just declare your options in your test method:
class <YourTestName>(AntaTest):\n ...\n def test(self, my_param1: str) -> None:\n pass\n
"},{"location":"advanced_usages/custom-tests/#check-inputs","title":"Check inputs","text":"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\nif not minimum:\n self.result.is_skipped(\"verify_dynamic_vlan was run without minimum value set\")\nelse:\n ...\n
"},{"location":"advanced_usages/custom-tests/#implement-your-logic","title":"Implement your logic","text":"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
# Grab data for your command\ncommand_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n# Do your test: In this example we count number of vlans with field dynamic set to true\nnum_dyn_vlan = len([ vlan for vlan,data in command_output['vlans'].items() if command_output['dynamic'] is True])\nif num_dyn_vlan >= minimum:\n self.result.is_success()\nelse:\n self.result.is_failure(f\"Device has {num_dyn_vlan} configured, we expect at least {minimum}\")\n
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')\n
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\n
"},{"location":"advanced_usages/custom-tests/#create-your-catalog","title":"Create your catalog","text":"This section is required only if you are not merging your development into ANTA. Otherwise, just follow contribution guide.
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:\n- VerifyFeatureX:\nminimum: 1\n
And now you can run your NRFU tests with the CLI: anta nrfu text --catalog test_custom.yml\nspine01 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)\nspine02 :: verify_dynamic_vlan :: FAILURE (Device has 0 configured, we expect at least 1)\nleaf01 :: verify_dynamic_vlan :: SUCCESS\nleaf02 :: verify_dynamic_vlan :: SUCCESS\nleaf03 :: verify_dynamic_vlan :: SUCCESS\nleaf04 :: verify_dynamic_vlan :: SUCCESS\n
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.
"},{"location":"api/device/","title":"AntaDevice Abstracted class","text":""},{"location":"api/device/#antadevice-abstracted-class","title":"AntaDevice Abstracted class","text":" Bases: ABC
Abstract class representing a device in ANTA. An implementation of this class needs must override the abstract coroutines collect()
and refresh()
.
Instance 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 hw_model: Hardware model of the device tags: List of tags for this device
Source code in anta/device.py
class AntaDevice(ABC):\n\"\"\"\n Abstract class representing a device in ANTA.\n An implementation of this class needs must override the abstract coroutines `collect()` and\n `refresh()`.\n\n Instance attributes:\n name: Device name\n is_online: True if the device IP is reachable and a port can be open\n established: True if remote command execution succeeds\n hw_model: Hardware model of the device\n tags: List of tags for this device\n \"\"\"\n\n def __init__(self, name: str, tags: Optional[List[str]] = None) -> None:\n\"\"\"\n Constructor of AntaDevice\n\n Args:\n name: Device name\n tags: List of tags for this device\n \"\"\"\n self.name: str = name\n self.hw_model: Optional[str] = None\n self.tags: List[str] = tags if tags is not None else []\n self.is_online: bool = False\n self.established: bool = False\n\n # Ensure tag 'all' is always set\n if DEFAULT_TAG not in self.tags:\n self.tags.append(DEFAULT_TAG)\n\n def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield \"name\", self.name\n yield \"tags\", self.tags\n yield \"hw_model\", self.hw_model\n yield \"is_online\", self.is_online\n yield \"established\", self.established\n\n @abstractmethod\n def __eq__(self, other: object) -> bool:\n\"\"\"\n AntaDevice equality depends on the class implementation.\n \"\"\"\n\n @abstractmethod\n async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output.\n This abstract coroutine can be used to implement any command collection method\n for a device in ANTA.\n\n The `collect()` implementation needs to populate the `output` attribute\n of the `AntaTestCommand` object passed as argument.\n\n If a failure occurs, the `collect()` implementation is expected to catch the\n exception and implement proper logging, the `output` attribute of the\n `AntaTestCommand` object passed as argument would be `None` in this case.\n\n Args:\n command: the command to collect\n \"\"\"\n\n async def collect_commands(self, commands: List[AntaTestCommand]) -> None:\n\"\"\"\n Collect multiple commands.\n\n Args:\n commands: the commands to collect\n \"\"\"\n await asyncio.gather(*(self.collect(command=command) for command in commands))\n\n @abstractmethod\n async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AntaDevice instance.\n\n This coroutine must update the following attributes of AntaDevice:\n - is_online: When the device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n\n async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device, usually through SCP.\n It is not mandatory to implement this for a valid AntaDevice subclass.\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n raise NotImplementedError(f\"copy() method has not been implemented in {self.__class__.__name__} definition\")\n
"},{"location":"api/device/#anta.device.AntaDevice.__eq__","title":"__eq__(other)
abstractmethod
","text":"AntaDevice equality depends on the class implementation.
Source code in anta/device.py
@abstractmethod\ndef __eq__(self, other: object) -> bool:\n\"\"\"\n AntaDevice equality depends on the class implementation.\n \"\"\"\n
"},{"location":"api/device/#anta.device.AntaDevice.__init__","title":"__init__(name, tags=None)
","text":"Constructor of AntaDevice
Parameters:
Name Type Description Default name
str
Device name
required tags
Optional[List[str]]
List of tags for this device
None
Source code in anta/device.py
def __init__(self, name: str, tags: Optional[List[str]] = None) -> None:\n\"\"\"\n Constructor of AntaDevice\n\n Args:\n name: Device name\n tags: List of tags for this device\n \"\"\"\n self.name: str = name\n self.hw_model: Optional[str] = None\n self.tags: List[str] = tags if tags is not None else []\n self.is_online: bool = False\n self.established: bool = False\n\n # Ensure tag 'all' is always set\n if DEFAULT_TAG not in self.tags:\n self.tags.append(DEFAULT_TAG)\n
"},{"location":"api/device/#anta.device.AntaDevice.__rich_repr__","title":"__rich_repr__()
","text":"Implements Rich Repr Protocol https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol
Source code in anta/device.py
def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield \"name\", self.name\n yield \"tags\", self.tags\n yield \"hw_model\", self.hw_model\n yield \"is_online\", self.is_online\n yield \"established\", self.established\n
"},{"location":"api/device/#anta.device.AntaDevice.collect","title":"collect(command)
abstractmethod
async
","text":"Collect device command output. This abstract coroutine can be used to implement any command collection method for a device in ANTA.
The collect()
implementation needs to populate the output
attribute of the AntaTestCommand
object passed as argument.
If a failure occurs, the collect()
implementation is expected to catch the exception and implement proper logging, the output
attribute of the AntaTestCommand
object passed as argument would be None
in this case.
Parameters:
Name Type Description Default command
AntaTestCommand
the command to collect
required Source code in anta/device.py
@abstractmethod\nasync def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output.\n This abstract coroutine can be used to implement any command collection method\n for a device in ANTA.\n\n The `collect()` implementation needs to populate the `output` attribute\n of the `AntaTestCommand` object passed as argument.\n\n If a failure occurs, the `collect()` implementation is expected to catch the\n exception and implement proper logging, the `output` attribute of the\n `AntaTestCommand` object passed as argument would be `None` in this case.\n\n Args:\n command: the command to collect\n \"\"\"\n
"},{"location":"api/device/#anta.device.AntaDevice.collect_commands","title":"collect_commands(commands)
async
","text":"Collect multiple commands.
Parameters:
Name Type Description Default commands
List[AntaTestCommand]
the commands to collect
required Source code in anta/device.py
async def collect_commands(self, commands: List[AntaTestCommand]) -> None:\n\"\"\"\n Collect multiple commands.\n\n Args:\n commands: the commands to collect\n \"\"\"\n await asyncio.gather(*(self.collect(command=command) for command in commands))\n
"},{"location":"api/device/#anta.device.AntaDevice.copy","title":"copy(sources, destination, direction='from')
async
","text":"Copy files to and from the device, usually through SCP. It is not mandatory to implement this for a valid AntaDevice subclass.
Parameters:
Name Type Description Default sources
List[Path]
List of files to copy to or from the device.
required destination
Path
Local or remote destination when copying the files. Can be a folder.
required direction
Literal['to', 'from']
Defines if this coroutine copies files to or from the device.
'from'
Source code in anta/device.py
async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device, usually through SCP.\n It is not mandatory to implement this for a valid AntaDevice subclass.\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n raise NotImplementedError(f\"copy() method has not been implemented in {self.__class__.__name__} definition\")\n
"},{"location":"api/device/#anta.device.AntaDevice.refresh","title":"refresh()
abstractmethod
async
","text":"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
Source code in anta/device.py
@abstractmethod\nasync def refresh(self) -> None:\n\"\"\"\n Update attributes of an AntaDevice instance.\n\n This coroutine must update the following attributes of AntaDevice:\n - is_online: When the device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n
"},{"location":"api/device/#asynceosdevice-class","title":"AsyncEOSDevice class","text":" Bases: AntaDevice
Implementation of AntaDevice for EOS using aio-eapi.
Instance 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 hw_model: Hardware model of the device tags: List of tags for this device
Source code in anta/device.py
class AsyncEOSDevice(AntaDevice):\n\"\"\"\n Implementation of AntaDevice for EOS using aio-eapi.\n\n Instance attributes:\n name: Device name\n is_online: True if the device IP is reachable and a port can be open\n established: True if remote command execution succeeds\n hw_model: Hardware model of the device\n tags: List of tags for this device\n \"\"\"\n\n # Hardware model definition in show version\n HW_MODEL_KEY: str = \"modelName\"\n # Maximum concurrent SSH connections opened with EOS\n MAX_SSH_CONNECTIONS: int = 10\n\n def __init__( # pylint: disable=R0913\n self,\n host: str,\n username: str,\n password: str,\n name: Optional[str] = None,\n enable_password: Optional[str] = None,\n port: Optional[int] = None,\n ssh_port: Optional[int] = 22,\n tags: Optional[List[str]] = None,\n timeout: Optional[float] = None,\n insecure: bool = False,\n proto: Literal[\"http\", \"https\"] = \"https\",\n ) -> None:\n\"\"\"\n Constructor of AsyncEOSDevice\n\n Args:\n host: Device FQDN or IP\n username: Username to connect to eAPI and SSH\n password: Password to connect to eAPI and SSH\n name: Device name\n enable_password: Password used to gain privileged access on EOS\n proto: eAPI protocol. Value can be 'http' or 'https'\n port: eAPI port. Defaults to 80 is proto is 'http' or 443 if proto is 'https'.\n ssh_port: SSH port\n insecure: Disable SSH Host Key validation\n tags: List of tags for this device\n timeout: Timeout value in seconds for outgoing connections. Default to 10 secs.\n \"\"\"\n if name is None:\n name = f\"{host}:{port}\"\n super().__init__(name, tags)\n self._enable_password = enable_password\n self._session: Device = Device(host=host, port=port, username=username, password=password, proto=proto, timeout=timeout)\n ssh_params: Dict[str, Any] = {}\n if insecure:\n ssh_params.update({\"known_hosts\": None})\n self._ssh_opts: SSHClientConnectionOptions = SSHClientConnectionOptions(host=host, port=ssh_port, username=username, password=password, **ssh_params)\n\n def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield from super().__rich_repr__()\n yield \"host\", self._session.host\n yield \"eapi_port\", self._session.port\n yield \"username\", self._ssh_opts.username\n yield \"password\", self._ssh_opts.password\n yield \"enable_password\", self._enable_password\n yield \"insecure\", self._ssh_opts.known_hosts is None\n if __DEBUG__:\n yield \"_session\", vars(self._session)\n yield \"_ssh_opts\", vars(self._ssh_opts)\n\n def __eq__(self, other: object) -> bool:\n\"\"\"\n Two AsyncEOSDevice objects are equal if the hostname and the port are the same.\n This covers the use case of port forwarding when the host is localhost and the devices have different ports.\n \"\"\"\n if not isinstance(other, AsyncEOSDevice):\n return False\n return self._session.host == other._session.host and self._session.port == other._session.port\n\n async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output from EOS using aio-eapi.\n\n Supports outformat `json` and `text` as output structure.\n Gain privileged access using the `enable_password` attribute\n of the `AntaDevice` instance if populated.\n\n Args:\n command: the command to collect\n \"\"\"\n try:\n if self._enable_password is not None:\n enable_cmd = {\n \"cmd\": \"enable\",\n \"input\": str(self._enable_password),\n }\n else:\n enable_cmd = {\"cmd\": \"enable\"}\n response = await self._session.cli(\n commands=[enable_cmd, command.command],\n ofmt=command.ofmt,\n version=command.version,\n )\n # remove first dict related to enable command\n # only applicable to json output\n if command.ofmt in [\"json\", \"text\"]:\n # selecting only our command output\n response = response[1]\n command.output = response\n logger.debug(f\"{self.name}: {command}\")\n\n except EapiCommandError as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Command '{command.command}' failed on {self.name}: {e.errmsg}\")\n logger.debug(command)\n except (HTTPError, ConnectError) as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Cannot connect to device {self.name}: {exc_to_str(e)}\")\n except Exception as e: # pylint: disable=broad-exception-caught\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.critical(f\"Exception raised while collecting command '{command.command}' on device {self.name} - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n logger.debug(command)\n\n async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AsyncEOSDevice instance.\n\n This coroutine must update the following attributes of AsyncEOSDevice:\n - is_online: When a device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n logger.debug(f\"Refreshing device {self.name}\")\n self.is_online = await self._session.check_connection()\n if self.is_online:\n try:\n response = await self._session.cli(command=\"show version\")\n except EapiCommandError as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {e.errmsg}\")\n except (HTTPError, ConnectError) as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {type(e).__name__}{'' if not str(e) else f' ({str(e)})'}\")\n else:\n if self.HW_MODEL_KEY in response:\n self.hw_model = response[self.HW_MODEL_KEY]\n else:\n logger.warning(f\"Cannot get hardware information from device {self.name}: cannot parse 'show version'\")\n else:\n logger.warning(f\"Could not connect to device {self.name}: cannot open eAPI port\")\n self.established = bool(self.is_online and self.hw_model)\n\n async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device using asyncssh.scp().\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n async with asyncssh.connect(\n host=self._ssh_opts.host,\n port=self._ssh_opts.port,\n tunnel=self._ssh_opts.tunnel,\n family=self._ssh_opts.family,\n local_addr=self._ssh_opts.local_addr,\n options=self._ssh_opts,\n ) as conn:\n src: Union[List[Tuple[SSHClientConnection, Path]], List[Path]]\n dst: Union[Tuple[SSHClientConnection, Path], Path]\n if direction == \"from\":\n src = [(conn, file) for file in sources]\n dst = destination\n for file in sources:\n logger.info(f\"Copying '{file}' from device {self.name} to '{destination}' locally\")\n elif direction == \"to\":\n src = sources\n dst = (conn, destination)\n for file in sources:\n logger.info(f\"Copying '{file}' to device {self.name} to '{destination}' remotely\")\n else:\n logger.critical(f\"'direction' argument to copy() fonction is invalid: {direction}\")\n return\n await asyncssh.scp(src, dst)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__eq__","title":"__eq__(other)
","text":"Two AsyncEOSDevice objects are equal if the hostname and the port are the same. This covers the use case of port forwarding when the host is localhost and the devices have different ports.
Source code in anta/device.py
def __eq__(self, other: object) -> bool:\n\"\"\"\n Two AsyncEOSDevice objects are equal if the hostname and the port are the same.\n This covers the use case of port forwarding when the host is localhost and the devices have different ports.\n \"\"\"\n if not isinstance(other, AsyncEOSDevice):\n return False\n return self._session.host == other._session.host and self._session.port == other._session.port\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__init__","title":"__init__(host, username, password, name=None, enable_password=None, port=None, ssh_port=22, tags=None, timeout=None, insecure=False, proto='https')
","text":"Constructor of AsyncEOSDevice
Parameters:
Name Type Description Default host
str
Device FQDN or IP
required username
str
Username to connect to eAPI and SSH
required password
str
Password to connect to eAPI and SSH
required name
Optional[str]
Device name
None
enable_password
Optional[str]
Password used to gain privileged access on EOS
None
proto
Literal['http', 'https']
eAPI protocol. Value can be \u2018http\u2019 or \u2018https\u2019
'https'
port
Optional[int]
eAPI port. Defaults to 80 is proto is \u2018http\u2019 or 443 if proto is \u2018https\u2019.
None
ssh_port
Optional[int]
SSH port
22
insecure
bool
Disable SSH Host Key validation
False
tags
Optional[List[str]]
List of tags for this device
None
timeout
Optional[float]
Timeout value in seconds for outgoing connections. Default to 10 secs.
None
Source code in anta/device.py
def __init__( # pylint: disable=R0913\n self,\n host: str,\n username: str,\n password: str,\n name: Optional[str] = None,\n enable_password: Optional[str] = None,\n port: Optional[int] = None,\n ssh_port: Optional[int] = 22,\n tags: Optional[List[str]] = None,\n timeout: Optional[float] = None,\n insecure: bool = False,\n proto: Literal[\"http\", \"https\"] = \"https\",\n) -> None:\n\"\"\"\n Constructor of AsyncEOSDevice\n\n Args:\n host: Device FQDN or IP\n username: Username to connect to eAPI and SSH\n password: Password to connect to eAPI and SSH\n name: Device name\n enable_password: Password used to gain privileged access on EOS\n proto: eAPI protocol. Value can be 'http' or 'https'\n port: eAPI port. Defaults to 80 is proto is 'http' or 443 if proto is 'https'.\n ssh_port: SSH port\n insecure: Disable SSH Host Key validation\n tags: List of tags for this device\n timeout: Timeout value in seconds for outgoing connections. Default to 10 secs.\n \"\"\"\n if name is None:\n name = f\"{host}:{port}\"\n super().__init__(name, tags)\n self._enable_password = enable_password\n self._session: Device = Device(host=host, port=port, username=username, password=password, proto=proto, timeout=timeout)\n ssh_params: Dict[str, Any] = {}\n if insecure:\n ssh_params.update({\"known_hosts\": None})\n self._ssh_opts: SSHClientConnectionOptions = SSHClientConnectionOptions(host=host, port=ssh_port, username=username, password=password, **ssh_params)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.__rich_repr__","title":"__rich_repr__()
","text":"Implements Rich Repr Protocol https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol
Source code in anta/device.py
def __rich_repr__(self) -> Iterator[Tuple[str, Any]]:\n\"\"\"\n Implements Rich Repr Protocol\n https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol\n \"\"\"\n yield from super().__rich_repr__()\n yield \"host\", self._session.host\n yield \"eapi_port\", self._session.port\n yield \"username\", self._ssh_opts.username\n yield \"password\", self._ssh_opts.password\n yield \"enable_password\", self._enable_password\n yield \"insecure\", self._ssh_opts.known_hosts is None\n if __DEBUG__:\n yield \"_session\", vars(self._session)\n yield \"_ssh_opts\", vars(self._ssh_opts)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.collect","title":"collect(command)
async
","text":"Collect device command output from EOS using aio-eapi.
Supports outformat json
and text
as output structure. Gain privileged access using the enable_password
attribute of the AntaDevice
instance if populated.
Parameters:
Name Type Description Default command
AntaTestCommand
the command to collect
required Source code in anta/device.py
async def collect(self, command: AntaTestCommand) -> None:\n\"\"\"\n Collect device command output from EOS using aio-eapi.\n\n Supports outformat `json` and `text` as output structure.\n Gain privileged access using the `enable_password` attribute\n of the `AntaDevice` instance if populated.\n\n Args:\n command: the command to collect\n \"\"\"\n try:\n if self._enable_password is not None:\n enable_cmd = {\n \"cmd\": \"enable\",\n \"input\": str(self._enable_password),\n }\n else:\n enable_cmd = {\"cmd\": \"enable\"}\n response = await self._session.cli(\n commands=[enable_cmd, command.command],\n ofmt=command.ofmt,\n version=command.version,\n )\n # remove first dict related to enable command\n # only applicable to json output\n if command.ofmt in [\"json\", \"text\"]:\n # selecting only our command output\n response = response[1]\n command.output = response\n logger.debug(f\"{self.name}: {command}\")\n\n except EapiCommandError as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Command '{command.command}' failed on {self.name}: {e.errmsg}\")\n logger.debug(command)\n except (HTTPError, ConnectError) as e:\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.error(f\"Cannot connect to device {self.name}: {exc_to_str(e)}\")\n except Exception as e: # pylint: disable=broad-exception-caught\n # TODO @mtache - propagate the exception in some AntaTestCommand attribute\n logger.critical(f\"Exception raised while collecting command '{command.command}' on device {self.name} - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n logger.debug(command)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.copy","title":"copy(sources, destination, direction='from')
async
","text":"Copy files to and from the device using asyncssh.scp().
Parameters:
Name Type Description Default sources
List[Path]
List of files to copy to or from the device.
required destination
Path
Local or remote destination when copying the files. Can be a folder.
required direction
Literal['to', 'from']
Defines if this coroutine copies files to or from the device.
'from'
Source code in anta/device.py
async def copy(self, sources: List[Path], destination: Path, direction: Literal[\"to\", \"from\"] = \"from\") -> None:\n\"\"\"\n Copy files to and from the device using asyncssh.scp().\n\n Args:\n sources: List of files to copy to or from the device.\n destination: Local or remote destination when copying the files. Can be a folder.\n direction: Defines if this coroutine copies files to or from the device.\n \"\"\"\n async with asyncssh.connect(\n host=self._ssh_opts.host,\n port=self._ssh_opts.port,\n tunnel=self._ssh_opts.tunnel,\n family=self._ssh_opts.family,\n local_addr=self._ssh_opts.local_addr,\n options=self._ssh_opts,\n ) as conn:\n src: Union[List[Tuple[SSHClientConnection, Path]], List[Path]]\n dst: Union[Tuple[SSHClientConnection, Path], Path]\n if direction == \"from\":\n src = [(conn, file) for file in sources]\n dst = destination\n for file in sources:\n logger.info(f\"Copying '{file}' from device {self.name} to '{destination}' locally\")\n elif direction == \"to\":\n src = sources\n dst = (conn, destination)\n for file in sources:\n logger.info(f\"Copying '{file}' to device {self.name} to '{destination}' remotely\")\n else:\n logger.critical(f\"'direction' argument to copy() fonction is invalid: {direction}\")\n return\n await asyncssh.scp(src, dst)\n
"},{"location":"api/device/#anta.device.AsyncEOSDevice.refresh","title":"refresh()
async
","text":"Update attributes of an AsyncEOSDevice instance.
This coroutine must update the following attributes of AsyncEOSDevice: - is_online: When a device IP is reachable and a port can be open - established: When a command execution succeeds - hw_model: The hardware model of the device
Source code in anta/device.py
async def refresh(self) -> None:\n\"\"\"\n Update attributes of an AsyncEOSDevice instance.\n\n This coroutine must update the following attributes of AsyncEOSDevice:\n - is_online: When a device IP is reachable and a port can be open\n - established: When a command execution succeeds\n - hw_model: The hardware model of the device\n \"\"\"\n logger.debug(f\"Refreshing device {self.name}\")\n self.is_online = await self._session.check_connection()\n if self.is_online:\n try:\n response = await self._session.cli(command=\"show version\")\n except EapiCommandError as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {e.errmsg}\")\n except (HTTPError, ConnectError) as e:\n logger.warning(f\"Cannot get hardware information from device {self.name}: {type(e).__name__}{'' if not str(e) else f' ({str(e)})'}\")\n else:\n if self.HW_MODEL_KEY in response:\n self.hw_model = response[self.HW_MODEL_KEY]\n else:\n logger.warning(f\"Cannot get hardware information from device {self.name}: cannot parse 'show version'\")\n else:\n logger.warning(f\"Could not connect to device {self.name}: cannot open eAPI port\")\n self.established = bool(self.is_online and self.hw_model)\n
"},{"location":"api/inventory/","title":"Inventory module","text":""},{"location":"api/inventory/#anta-inventory-module","title":"ANTA Inventory module","text":" Bases: dict
Inventory abstraction for ANTA framework.
Source code in anta/inventory/__init__.py
class AntaInventory(dict): # type: ignore\n # dict[str, AntaDevice] - not working in python 3.8 hence the ignore\n\"\"\"\n Inventory abstraction for ANTA framework.\n \"\"\"\n\n # Root key of inventory part of the inventory file\n INVENTORY_ROOT_KEY = \"anta_inventory\"\n # Supported Output format\n INVENTORY_OUTPUT_FORMAT = [\"native\", \"json\"]\n\n def __str__(self) -> str:\n\"\"\"Human readable string representing the inventory\"\"\"\n devs = {}\n for dev in self.values():\n if (dev_type := dev.__class__.__name__) not in devs:\n devs[dev_type] = 1\n else:\n devs[dev_type] += 1\n return f\"ANTA Inventory contains {' '.join([f'{n} devices ({t})' for t, n in devs.items()])}\"\n\n @staticmethod\n def parse(\n inventory_file: str, username: str, password: str, enable_password: Optional[str] = None, timeout: Optional[float] = None, insecure: bool = False\n ) -> AntaInventory:\n # pylint: disable=too-many-arguments\n\"\"\"\n Create an AntaInventory object from an inventory file.\n Instantiate AntaDevice objects using the AsyncEOSDevice subclass.\n\n Args:\n inventory_file (str): Path to inventory YAML file where user has described his inputs\n username (str): Username to use to connect to devices\n password (str): Password to use to connect to devices\n timeout (float, optional): timeout in seconds for every API call.\n\n Raises:\n InventoryRootKeyError: Root key of inventory is missing.\n InventoryIncorrectSchema: Inventory file is not following AntaInventory Schema.\n InventoryUnknownFormat: Output format is not supported.\n \"\"\"\n\n inventory = AntaInventory()\n kwargs: Dict[str, Any] = {\"username\": username, \"password\": password, \"enable_password\": enable_password, \"timeout\": timeout, \"insecure\": insecure}\n kwargs = {k: v for k, v in kwargs.items() if v is not None}\n\n with open(inventory_file, \"r\", encoding=\"UTF-8\") as file:\n data = safe_load(file)\n\n # Load data using Pydantic\n try:\n inventory_input = AntaInventoryInput(**data[AntaInventory.INVENTORY_ROOT_KEY])\n except KeyError as exc:\n logger.error(f\"Inventory root key is missing: {AntaInventory.INVENTORY_ROOT_KEY}\")\n raise InventoryRootKeyError(f\"Inventory root key ({AntaInventory.INVENTORY_ROOT_KEY}) is not defined in your inventory\") from exc\n except ValidationError as exc:\n logger.error(\"Inventory data are not compliant with inventory models\")\n raise InventoryIncorrectSchema(f\"Inventory is not following the schema: {str(exc)}\") from exc\n\n # Read data from input\n if inventory_input.hosts is not None:\n for host in inventory_input.hosts:\n device = AsyncEOSDevice(name=host.name, host=str(host.host), port=host.port, tags=host.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.networks is not None:\n for network in inventory_input.networks:\n for host_ip in IPNetwork(str(network.network)):\n device = AsyncEOSDevice(host=str(host_ip), tags=network.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.ranges is not None:\n for range_def in inventory_input.ranges:\n range_increment = IPAddress(str(range_def.start))\n range_stop = IPAddress(str(range_def.end))\n while range_increment <= range_stop:\n device = AsyncEOSDevice(host=str(range_increment), tags=range_def.tags, **kwargs)\n inventory.add_device(device)\n range_increment += 1\n\n return inventory\n\n ###########################################################################\n # Public methods\n ###########################################################################\n\n ###########################################################################\n # GET methods\n ###########################################################################\n\n def get_inventory(self, established_only: bool = False, tags: Optional[List[str]] = None) -> AntaInventory:\n\"\"\"\n Returns a filtered inventory.\n\n Args:\n established_only: Whether or not to include only established devices. Default False.\n tags: List of tags to filter devices.\n\n Returns:\n AntaInventory: An inventory with filtered AntaDevice objects.\n \"\"\"\n\n def _filter_devices(device: AntaDevice) -> bool:\n\"\"\"\n Helper function to select the devices based on the input tags\n and the requirement for an established connection.\n \"\"\"\n if tags is not None and all(tag not in tags for tag in device.tags):\n return False\n return bool(not established_only or device.established)\n\n devices: List[AntaDevice] = list(filter(_filter_devices, self.values()))\n result = AntaInventory()\n for device in devices:\n result.add_device(device)\n return result\n\n ###########################################################################\n # SET methods\n ###########################################################################\n\n def __setitem__(self, key: str, value: AntaDevice) -> None:\n if key != value.name:\n raise RuntimeError(f\"The key must be the device name for device '{value.name}'. Use AntaInventory.add_device().\")\n return super().__setitem__(key, value)\n\n def add_device(self, device: AntaDevice) -> None:\n\"\"\"Add a device to final inventory.\n\n Args:\n device: Device object to be added\n \"\"\"\n self[device.name] = device\n\n ###########################################################################\n # MISC methods\n ###########################################################################\n\n async def connect_inventory(self) -> None:\n\"\"\"Run `refresh()` coroutines for all AntaDevice objects in this inventory.\"\"\"\n logger.debug(\"Refreshing devices...\")\n results = await asyncio.gather(\n *(device.refresh() for device in self.values()),\n return_exceptions=True,\n )\n for r in results:\n if isinstance(r, Exception):\n logger.error(f\"Error when refreshing inventory: {r.__class__.__name__}{'' if not str(r) else f' ({str(r)})'}\")\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.__str__","title":"__str__()
","text":"Human readable string representing the inventory
Source code in anta/inventory/__init__.py
def __str__(self) -> str:\n\"\"\"Human readable string representing the inventory\"\"\"\n devs = {}\n for dev in self.values():\n if (dev_type := dev.__class__.__name__) not in devs:\n devs[dev_type] = 1\n else:\n devs[dev_type] += 1\n return f\"ANTA Inventory contains {' '.join([f'{n} devices ({t})' for t, n in devs.items()])}\"\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.add_device","title":"add_device(device)
","text":"Add a device to final inventory.
Parameters:
Name Type Description Default device
AntaDevice
Device object to be added
required Source code in anta/inventory/__init__.py
def add_device(self, device: AntaDevice) -> None:\n\"\"\"Add a device to final inventory.\n\n Args:\n device: Device object to be added\n \"\"\"\n self[device.name] = device\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.connect_inventory","title":"connect_inventory()
async
","text":"Run refresh()
coroutines for all AntaDevice objects in this inventory.
Source code in anta/inventory/__init__.py
async def connect_inventory(self) -> None:\n\"\"\"Run `refresh()` coroutines for all AntaDevice objects in this inventory.\"\"\"\n logger.debug(\"Refreshing devices...\")\n results = await asyncio.gather(\n *(device.refresh() for device in self.values()),\n return_exceptions=True,\n )\n for r in results:\n if isinstance(r, Exception):\n logger.error(f\"Error when refreshing inventory: {r.__class__.__name__}{'' if not str(r) else f' ({str(r)})'}\")\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.get_inventory","title":"get_inventory(established_only=False, tags=None)
","text":"Returns a filtered inventory.
Parameters:
Name Type Description Default established_only
bool
Whether or not to include only established devices. Default False.
False
tags
Optional[List[str]]
List of tags to filter devices.
None
Returns:
Name Type Description AntaInventory
AntaInventory
An inventory with filtered AntaDevice objects.
Source code in anta/inventory/__init__.py
def get_inventory(self, established_only: bool = False, tags: Optional[List[str]] = None) -> AntaInventory:\n\"\"\"\n Returns a filtered inventory.\n\n Args:\n established_only: Whether or not to include only established devices. Default False.\n tags: List of tags to filter devices.\n\n Returns:\n AntaInventory: An inventory with filtered AntaDevice objects.\n \"\"\"\n\n def _filter_devices(device: AntaDevice) -> bool:\n\"\"\"\n Helper function to select the devices based on the input tags\n and the requirement for an established connection.\n \"\"\"\n if tags is not None and all(tag not in tags for tag in device.tags):\n return False\n return bool(not established_only or device.established)\n\n devices: List[AntaDevice] = list(filter(_filter_devices, self.values()))\n result = AntaInventory()\n for device in devices:\n result.add_device(device)\n return result\n
"},{"location":"api/inventory/#anta.inventory.AntaInventory.parse","title":"parse(inventory_file, username, password, enable_password=None, timeout=None, insecure=False)
staticmethod
","text":"Create an AntaInventory object from an inventory file. Instantiate AntaDevice objects using the AsyncEOSDevice subclass.
Parameters:
Name Type Description Default inventory_file
str
Path to inventory YAML file where user has described his inputs
required username
str
Username to use to connect to devices
required password
str
Password to use to connect to devices
required timeout
float
timeout in seconds for every API call.
None
Raises:
Type Description InventoryRootKeyError
Root key of inventory is missing.
InventoryIncorrectSchema
Inventory file is not following AntaInventory Schema.
InventoryUnknownFormat
Output format is not supported.
Source code in anta/inventory/__init__.py
@staticmethod\ndef parse(\n inventory_file: str, username: str, password: str, enable_password: Optional[str] = None, timeout: Optional[float] = None, insecure: bool = False\n) -> AntaInventory:\n # pylint: disable=too-many-arguments\n\"\"\"\n Create an AntaInventory object from an inventory file.\n Instantiate AntaDevice objects using the AsyncEOSDevice subclass.\n\n Args:\n inventory_file (str): Path to inventory YAML file where user has described his inputs\n username (str): Username to use to connect to devices\n password (str): Password to use to connect to devices\n timeout (float, optional): timeout in seconds for every API call.\n\n Raises:\n InventoryRootKeyError: Root key of inventory is missing.\n InventoryIncorrectSchema: Inventory file is not following AntaInventory Schema.\n InventoryUnknownFormat: Output format is not supported.\n \"\"\"\n\n inventory = AntaInventory()\n kwargs: Dict[str, Any] = {\"username\": username, \"password\": password, \"enable_password\": enable_password, \"timeout\": timeout, \"insecure\": insecure}\n kwargs = {k: v for k, v in kwargs.items() if v is not None}\n\n with open(inventory_file, \"r\", encoding=\"UTF-8\") as file:\n data = safe_load(file)\n\n # Load data using Pydantic\n try:\n inventory_input = AntaInventoryInput(**data[AntaInventory.INVENTORY_ROOT_KEY])\n except KeyError as exc:\n logger.error(f\"Inventory root key is missing: {AntaInventory.INVENTORY_ROOT_KEY}\")\n raise InventoryRootKeyError(f\"Inventory root key ({AntaInventory.INVENTORY_ROOT_KEY}) is not defined in your inventory\") from exc\n except ValidationError as exc:\n logger.error(\"Inventory data are not compliant with inventory models\")\n raise InventoryIncorrectSchema(f\"Inventory is not following the schema: {str(exc)}\") from exc\n\n # Read data from input\n if inventory_input.hosts is not None:\n for host in inventory_input.hosts:\n device = AsyncEOSDevice(name=host.name, host=str(host.host), port=host.port, tags=host.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.networks is not None:\n for network in inventory_input.networks:\n for host_ip in IPNetwork(str(network.network)):\n device = AsyncEOSDevice(host=str(host_ip), tags=network.tags, **kwargs)\n inventory.add_device(device)\n if inventory_input.ranges is not None:\n for range_def in inventory_input.ranges:\n range_increment = IPAddress(str(range_def.start))\n range_stop = IPAddress(str(range_def.end))\n while range_increment <= range_stop:\n device = AsyncEOSDevice(host=str(range_increment), tags=range_def.tags, **kwargs)\n inventory.add_device(device)\n range_increment += 1\n\n return inventory\n
"},{"location":"api/inventory/#exceptions","title":"Exceptions","text":"Manage Exception in Inventory module.
"},{"location":"api/inventory/#anta.inventory.exceptions.InventoryIncorrectSchema","title":"InventoryIncorrectSchema
","text":" Bases: Exception
Error when user data does not follow ANTA schema.
Source code in anta/inventory/exceptions.py
class InventoryIncorrectSchema(Exception):\n\"\"\"Error when user data does not follow ANTA schema.\"\"\"\n
"},{"location":"api/inventory/#anta.inventory.exceptions.InventoryRootKeyError","title":"InventoryRootKeyError
","text":" Bases: Exception
Error raised when inventory root key is not found.
Source code in anta/inventory/exceptions.py
class InventoryRootKeyError(Exception):\n\"\"\"Error raised when inventory root key is not found.\"\"\"\n
"},{"location":"api/inventory.models.input/","title":"Inventory models","text":""},{"location":"api/inventory.models.input/#data-models-for-antainventory","title":"Data models for anta.inventory","text":" Bases: BaseModel
User\u2019s inventory model.
Attributes:
Name Type Description networks
List[AntaInventoryNetwork], Optional
List of AntaInventoryNetwork objects for networks.
hosts
List[AntaInventoryHost], Optional
List of AntaInventoryHost objects for hosts.
range
List[AntaInventoryRange], Optional
List of AntaInventoryRange objects for ranges.
Source code in anta/inventory/models.py
class AntaInventoryInput(BaseModel):\n\"\"\"\n User's inventory model.\n\n Attributes:\n networks (List[AntaInventoryNetwork],Optional): List of AntaInventoryNetwork objects for networks.\n hosts (List[AntaInventoryHost],Optional): List of AntaInventoryHost objects for hosts.\n range (List[AntaInventoryRange],Optional): List of AntaInventoryRange objects for ranges.\n \"\"\"\n\n networks: Optional[List[AntaInventoryNetwork]]\n hosts: Optional[List[AntaInventoryHost]]\n ranges: Optional[List[AntaInventoryRange]]\n
"},{"location":"api/inventory.models.input/#user-inventory-components","title":"User inventory components","text":" Bases: BaseModel
Host definition for user\u2019s inventory.
Attributes:
Name Type Description host
IPvAnyAddress
IPv4 or IPv6 address of the device
port
int
(Optional) eAPI port to use Default is 443.
name
str
(Optional) Name to display during tests report. Default is hostname:port
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryHost(BaseModel):\n\"\"\"\n Host definition for user's inventory.\n\n Attributes:\n host (IPvAnyAddress): IPv4 or IPv6 address of the device\n port (int): (Optional) eAPI port to use Default is 443.\n name (str): (Optional) Name to display during tests report. Default is hostname:port\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n name: Optional[str]\n host: Union[constr(regex=RFC_1123_REGEX), IPvAnyAddress] # type: ignore\n port: Optional[conint(gt=1, lt=65535)] # type: ignore\n tags: Optional[List[str]]\n
Bases: BaseModel
Network definition for user\u2019s inventory.
Attributes:
Name Type Description network
IPvAnyNetwork
Subnet to use for testing.
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryNetwork(BaseModel):\n\"\"\"\n Network definition for user's inventory.\n\n Attributes:\n network (IPvAnyNetwork): Subnet to use for testing.\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n network: IPvAnyNetwork\n tags: Optional[List[str]]\n
Bases: BaseModel
IP Range definition for user\u2019s inventory.
Attributes:
Name Type Description start
IPvAnyAddress
IPv4 or IPv6 address for the begining of the range.
stop
IPvAnyAddress
IPv4 or IPv6 address for the end of the range.
tags
List[str]
List of attached tags read from inventory file.
Source code in anta/inventory/models.py
class AntaInventoryRange(BaseModel):\n\"\"\"\n IP Range definition for user's inventory.\n\n Attributes:\n start (IPvAnyAddress): IPv4 or IPv6 address for the begining of the range.\n stop (IPvAnyAddress): IPv4 or IPv6 address for the end of the range.\n tags (List[str]): List of attached tags read from inventory file.\n \"\"\"\n\n start: IPvAnyAddress\n end: IPvAnyAddress\n tags: Optional[List[str]]\n
"},{"location":"api/models/","title":"Test models","text":""},{"location":"api/models/#antatest-definition","title":"AntaTest definition","text":" Bases: ABC
Abstract class defining a test for Anta
The goal of this class is to handle the heavy lifting and make writing a test as simple as possible.
TODO - complete doctstring with example
Source code in anta/models.py
class AntaTest(ABC):\n\"\"\"Abstract class defining a test for Anta\n\n The goal of this class is to handle the heavy lifting and make\n writing a test as simple as possible.\n\n TODO - complete doctstring with example\n \"\"\"\n\n # Mandatory class attributes\n # TODO - find a way to tell mypy these are mandatory for child classes - maybe Protocol\n name: ClassVar[str]\n description: ClassVar[str]\n categories: ClassVar[list[str]]\n # Or any child type\n commands: ClassVar[list[AntaTestCommand]]\n # TODO - today we support only one template per Test\n template: ClassVar[AntaTestTemplate]\n\n # Optional class attributes\n test_filters: ClassVar[list[AntaTestFilter]]\n\n def __init__(\n self,\n device: AntaDevice,\n template_params: list[dict[str, Any]] | None = None,\n # TODO document very well the order of eos_data\n eos_data: list[dict[Any, Any] | str] | None = None,\n labels: list[str] | None = None,\n ):\n\"\"\"Class constructor\"\"\"\n self.device = device\n self.result = TestResult(name=device.name, test=self.name, test_category=self.categories, test_description=self.description)\n self.labels = labels or []\n\n # TODO - check optimization for deepcopy\n # Generating instance_commands from list of commands and template\n self.instance_commands = []\n if hasattr(self.__class__, \"commands\") and (cmds := self.__class__.commands) is not None:\n self.instance_commands.extend(deepcopy(cmds))\n if hasattr(self.__class__, \"template\") and (tpl := self.__class__.template) is not None:\n if template_params is None:\n self.result.is_error(\"Command has template but no params were given\")\n return\n self.template_params = template_params\n self.instance_commands.extend(\n AntaTestCommand(\n command=tpl.template.format(**param),\n ofmt=tpl.ofmt,\n version=tpl.version,\n template=tpl,\n template_params=param,\n )\n for param in template_params\n )\n\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n def save_commands_data(self, eos_data: list[dict[Any, Any] | str]) -> None:\n\"\"\"Called at init or at test execution time\"\"\"\n if len(eos_data) != len(self.instance_commands):\n self.result.is_error(\"Test initialization error: Trying to save more data than there are commands for the test\")\n return\n for index, data in enumerate(eos_data or []):\n self.instance_commands[index].output = data\n\n def all_data_collected(self) -> bool:\n\"\"\"returns True if output is populated for every command\"\"\"\n return all(command.output is not None for command in self.instance_commands)\n\n def __init_subclass__(cls) -> None:\n\"\"\"\n Verify that the mandatory class attributes are defined\n \"\"\"\n mandatory_attributes = [\"name\", \"description\", \"categories\"]\n for attr in mandatory_attributes:\n if not hasattr(cls, attr):\n raise NotImplementedError(f\"Class {cls} is missing required class attribute {attr}\")\n # Check that either commands or template exist\n if not (hasattr(cls, \"commands\") or hasattr(cls, \"template\")):\n raise NotImplementedError(f\"Class {cls} is missing required either commands or template attribute\")\n\n async def collect(self) -> None:\n\"\"\"\n Method used to collect outputs of all commands of this test class from the device of this test instance.\n \"\"\"\n logger.debug(f\"Test {self.name} on device {self.device.name}: running command outputs collection\")\n try:\n await self.device.collect_commands(self.instance_commands)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised while collecting commands for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n\n @staticmethod\n def anta_test(function: F) -> Callable[..., Coroutine[Any, Any, TestResult]]:\n\"\"\"\n Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing\n \"\"\"\n\n @wraps(function)\n async def wrapper(\n self: AntaTest,\n eos_data: list[dict[Any, Any] | str] | None = None,\n **kwargs: dict[str, Any],\n ) -> TestResult:\n\"\"\"\n Wraps the test function and implement (in this order):\n 1. Instantiate the command outputs if `eos_data` is provided\n 2. Collect missing command outputs from the device\n 3. Run the test function\n 4. Catches and set the result if the test function raises an exception\n\n Returns:\n TestResult: self.result, populated with the correct exit status\n \"\"\"\n if self.result.result != \"unset\":\n return self.result\n\n # TODO maybe_skip decorators\n\n # Data\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n # No test data is present, try to collect\n if not self.all_data_collected():\n await self.collect()\n if self.result.result != \"unset\":\n return self.result\n\n try:\n if not self.all_data_collected():\n raise ValueError(\"Some command output is missing\")\n logger.debug(f\"Test {self.name} on device {self.device.name}: running test\")\n function(self, **kwargs)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n return self.result\n\n return wrapper\n\n @abstractmethod\n def test(self) -> Coroutine[Any, Any, TestResult]:\n\"\"\"\n This abstract method is the core of the test.\n It MUST set the correct status of self.result with the appropriate error messages\n\n it must be implemented as follow\n\n @AntaTest.anta_test\n def test(self) -> None:\n '''\n assert code\n '''\n \"\"\"\n
"},{"location":"api/models/#anta.models.AntaTest.__init__","title":"__init__(device, template_params=None, eos_data=None, labels=None)
","text":"Class constructor
Source code in anta/models.py
def __init__(\n self,\n device: AntaDevice,\n template_params: list[dict[str, Any]] | None = None,\n # TODO document very well the order of eos_data\n eos_data: list[dict[Any, Any] | str] | None = None,\n labels: list[str] | None = None,\n):\n\"\"\"Class constructor\"\"\"\n self.device = device\n self.result = TestResult(name=device.name, test=self.name, test_category=self.categories, test_description=self.description)\n self.labels = labels or []\n\n # TODO - check optimization for deepcopy\n # Generating instance_commands from list of commands and template\n self.instance_commands = []\n if hasattr(self.__class__, \"commands\") and (cmds := self.__class__.commands) is not None:\n self.instance_commands.extend(deepcopy(cmds))\n if hasattr(self.__class__, \"template\") and (tpl := self.__class__.template) is not None:\n if template_params is None:\n self.result.is_error(\"Command has template but no params were given\")\n return\n self.template_params = template_params\n self.instance_commands.extend(\n AntaTestCommand(\n command=tpl.template.format(**param),\n ofmt=tpl.ofmt,\n version=tpl.version,\n template=tpl,\n template_params=param,\n )\n for param in template_params\n )\n\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n
"},{"location":"api/models/#anta.models.AntaTest.__init_subclass__","title":"__init_subclass__()
","text":"Verify that the mandatory class attributes are defined
Source code in anta/models.py
def __init_subclass__(cls) -> None:\n\"\"\"\n Verify that the mandatory class attributes are defined\n \"\"\"\n mandatory_attributes = [\"name\", \"description\", \"categories\"]\n for attr in mandatory_attributes:\n if not hasattr(cls, attr):\n raise NotImplementedError(f\"Class {cls} is missing required class attribute {attr}\")\n # Check that either commands or template exist\n if not (hasattr(cls, \"commands\") or hasattr(cls, \"template\")):\n raise NotImplementedError(f\"Class {cls} is missing required either commands or template attribute\")\n
"},{"location":"api/models/#anta.models.AntaTest.all_data_collected","title":"all_data_collected()
","text":"returns True if output is populated for every command
Source code in anta/models.py
def all_data_collected(self) -> bool:\n\"\"\"returns True if output is populated for every command\"\"\"\n return all(command.output is not None for command in self.instance_commands)\n
"},{"location":"api/models/#anta.models.AntaTest.anta_test","title":"anta_test(function)
staticmethod
","text":"Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing
Source code in anta/models.py
@staticmethod\ndef anta_test(function: F) -> Callable[..., Coroutine[Any, Any, TestResult]]:\n\"\"\"\n Decorator for anta_test that handles injecting test data if given and collecting it using asyncio if missing\n \"\"\"\n\n @wraps(function)\n async def wrapper(\n self: AntaTest,\n eos_data: list[dict[Any, Any] | str] | None = None,\n **kwargs: dict[str, Any],\n ) -> TestResult:\n\"\"\"\n Wraps the test function and implement (in this order):\n 1. Instantiate the command outputs if `eos_data` is provided\n 2. Collect missing command outputs from the device\n 3. Run the test function\n 4. Catches and set the result if the test function raises an exception\n\n Returns:\n TestResult: self.result, populated with the correct exit status\n \"\"\"\n if self.result.result != \"unset\":\n return self.result\n\n # TODO maybe_skip decorators\n\n # Data\n if eos_data is not None:\n logger.debug(\"Test initialized with input data\")\n self.save_commands_data(eos_data)\n\n # No test data is present, try to collect\n if not self.all_data_collected():\n await self.collect()\n if self.result.result != \"unset\":\n return self.result\n\n try:\n if not self.all_data_collected():\n raise ValueError(\"Some command output is missing\")\n logger.debug(f\"Test {self.name} on device {self.device.name}: running test\")\n function(self, **kwargs)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n return self.result\n\n return wrapper\n
"},{"location":"api/models/#anta.models.AntaTest.collect","title":"collect()
async
","text":"Method used to collect outputs of all commands of this test class from the device of this test instance.
Source code in anta/models.py
async def collect(self) -> None:\n\"\"\"\n Method used to collect outputs of all commands of this test class from the device of this test instance.\n \"\"\"\n logger.debug(f\"Test {self.name} on device {self.device.name}: running command outputs collection\")\n try:\n await self.device.collect_commands(self.instance_commands)\n except Exception as e: # pylint: disable=broad-exception-caught\n logger.error(f\"Exception raised while collecting commands for test {self.name} (on device {self.device.name}) - {exc_to_str(e)}\")\n logger.debug(tb_to_str(e))\n self.result.is_error(exc_to_str(e))\n
"},{"location":"api/models/#anta.models.AntaTest.save_commands_data","title":"save_commands_data(eos_data)
","text":"Called at init or at test execution time
Source code in anta/models.py
def save_commands_data(self, eos_data: list[dict[Any, Any] | str]) -> None:\n\"\"\"Called at init or at test execution time\"\"\"\n if len(eos_data) != len(self.instance_commands):\n self.result.is_error(\"Test initialization error: Trying to save more data than there are commands for the test\")\n return\n for index, data in enumerate(eos_data or []):\n self.instance_commands[index].output = data\n
"},{"location":"api/models/#anta.models.AntaTest.test","title":"test()
abstractmethod
","text":"This abstract method is the core of the test. It MUST set the correct status of self.result with the appropriate error messages
it must be implemented as follow
@AntaTest.anta_test def test(self) -> None: \u2018\u2019\u2018 assert code \u2018\u2019\u2018
Source code in anta/models.py
@abstractmethod\ndef test(self) -> Coroutine[Any, Any, TestResult]:\n\"\"\"\n This abstract method is the core of the test.\n It MUST set the correct status of self.result with the appropriate error messages\n\n it must be implemented as follow\n\n @AntaTest.anta_test\n def test(self) -> None:\n '''\n assert code\n '''\n \"\"\"\n
"},{"location":"api/models/#antatestcommand-definition","title":"AntaTestCommand definition","text":" Bases: BaseModel
Class to define a test command with its API version
Attributes:
Name Type Description command(str)
Test command
version
Union[int, Literal['latest']]
eAPI version - valid values are integers or the string \u201clatest\u201d - default is \u201clatest\u201d
ofmt(str)
Union[int, Literal['latest']]
eAPI output - json or text - default is json
output
Optional[Union[Dict[str, Any], str]]
collected output either dict for json or str for text
template
Optional(AntaTestTemplate
Template used to generate the command
template_params
Optional(dict
params used in the template to generate the command
Source code in anta/models.py
class AntaTestCommand(BaseModel):\n\"\"\"Class to define a test command with its API version\n\n Attributes:\n command(str): Test command\n version: eAPI version - valid values are integers or the string \"latest\" - default is \"latest\"\n ofmt(str): eAPI output - json or text - default is json\n output: collected output either dict for json or str for text\n template Optional(AntaTestTemplate): Template used to generate the command\n template_params Optional(dict): params used in the template to generate the command\n \"\"\"\n\n command: str\n version: Union[int, Literal[\"latest\"]] = \"latest\"\n ofmt: str = \"json\"\n output: Optional[Union[Dict[str, Any], str]]\n template: Optional[AntaTestTemplate] = None\n template_params: Optional[Dict[str, str]]\n\n @validator(\"template_params\")\n def prevent_none_when_template_is_set(cls: Type[AntaTestTemplate], value: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: # type: ignore\n\"\"\"\n Raises if template is set but no params are given\n \"\"\"\n if hasattr(cls, \"template\") and cls.template is not None:\n assert value is not None\n\n return value\n
"},{"location":"api/models/#anta.models.AntaTestCommand.prevent_none_when_template_is_set","title":"prevent_none_when_template_is_set(value)
","text":"Raises if template is set but no params are given
Source code in anta/models.py
@validator(\"template_params\")\ndef prevent_none_when_template_is_set(cls: Type[AntaTestTemplate], value: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: # type: ignore\n\"\"\"\n Raises if template is set but no params are given\n \"\"\"\n if hasattr(cls, \"template\") and cls.template is not None:\n assert value is not None\n\n return value\n
"},{"location":"api/models/#antatesttemplate-definition","title":"AntaTestTemplate definition","text":" Bases: BaseModel
Class to define a test command with its API version
Attributes:
Name Type Description command(str)
Test command
version
Union[int, Literal['latest']]
eAPI version - valid values are integers or the string \u201clatest\u201d - default is \u201clatest\u201d
ofmt(str)
Union[int, Literal['latest']]
eAPI output - json or text - default is json
output
Union[int, Literal['latest']]
collected output either dict for json or str for text
Source code in anta/models.py
class AntaTestTemplate(BaseModel):\n\"\"\"Class to define a test command with its API version\n\n Attributes:\n command(str): Test command\n version: eAPI version - valid values are integers or the string \"latest\" - default is \"latest\"\n ofmt(str): eAPI output - json or text - default is json\n output: collected output either dict for json or str for text\n \"\"\"\n\n template: str\n version: Union[int, Literal[\"latest\"]] = \"latest\"\n ofmt: str = \"json\"\n
"},{"location":"api/report_manager/","title":"Report Manager module","text":""},{"location":"api/report_manager/#anta-reportmanager-module","title":"ANTA ReportManager module","text":"TableReport Generate a Table based on TestResult.
Source code in anta/reporter/__init__.py
class ReportTable:\n\"\"\"TableReport Generate a Table based on TestResult.\"\"\"\n\n def __init__(self) -> None:\n\"\"\"\n __init__ Class constructor\n \"\"\"\n self.colors = []\n self.colors.append(ColorManager(level=\"success\", color=RICH_COLOR_PALETTE.SUCCESS))\n self.colors.append(ColorManager(level=\"failure\", color=RICH_COLOR_PALETTE.FAILURE))\n self.colors.append(ColorManager(level=\"error\", color=RICH_COLOR_PALETTE.ERROR))\n self.colors.append(ColorManager(level=\"skipped\", color=RICH_COLOR_PALETTE.SKIPPED))\n\n def _split_list_to_txt_list(self, usr_list: List[str], delimiter: Optional[str] = None) -> str:\n\"\"\"\n Split list to multi-lines string\n\n Args:\n usr_list (List[str]): List of string to concatenate\n delimiter (str, optional): A delimiter to use to start string. Defaults to None.\n\n Returns:\n str: Multi-lines string\n \"\"\"\n if delimiter is not None:\n return \"\\n\".join(f\"{delimiter} {line}\" for line in usr_list)\n return \"\\n\".join(f\"{line}\" for line in usr_list)\n\n def _build_headers(self, headers: List[str], table: Table) -> Table:\n\"\"\"\n Create headers for a table.\n\n First key is considered as header and is colored using RICH_COLOR_PALETTE.HEADER\n\n Args:\n headers (List[str]): List of headers\n table (Table): A rich Table instance\n\n Returns:\n Table: A rich Table instance with headers\n \"\"\"\n for idx, header in enumerate(headers):\n if idx == 0:\n table.add_column(header, justify=\"left\", style=RICH_COLOR_PALETTE.HEADER, no_wrap=True)\n else:\n table.add_column(header, justify=\"left\")\n return table\n\n def _color_result(self, status: str, output_type: str = \"Text\") -> Any:\n\"\"\"\n Helper to implement color based on test status.\n\n It gives output for either standard str or Text() colorized with Style()\n\n Args:\n status (str): status value to colorized\n output_type (str, optional): Which format to output code. Defaults to 'Text'.\n\n Returns:\n Any: Can be either str or Text with Style\n \"\"\"\n if len([result for result in self.colors if str(result.level).upper() == status.upper()]) == 1:\n code: ColorManager = [result for result in self.colors if str(result.level).upper() == status.upper()][0]\n return code.style_rich() if output_type == \"Text\" else code.string()\n return None\n\n def report_all(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n testcase: Optional[str] = None,\n title: str = \"All tests results\",\n ) -> Table:\n\"\"\"\n Create a table report with all tests for one or all devices.\n\n Create table with full output: Host / Test / Status / Message\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\"Device IP\", \"Test Name\", \"Test Status\", \"Message(s)\", \"Test description\", \"Test category\"]\n table = self._build_headers(headers=headers, table=table)\n\n for result in result_manager.get_results(output_format=\"list\"):\n # pylint: disable=R0916\n if (host is None and testcase is None) or (host is not None and str(result.name) == host) or (testcase is not None and testcase == str(result.test)):\n state = self._color_result(status=str(result.result), output_type=\"str\")\n message = self._split_list_to_txt_list(result.messages) if len(result.messages) > 0 else \"\"\n test_categories = \", \".join(result.test_category)\n table.add_row(str(result.name), result.test, state, message, result.test_description, test_categories)\n return table\n\n def report_summary_tests(\n self,\n result_manager: ResultManager,\n testcase: Optional[str] = None,\n title: str = \"Summary per test case\",\n ) -> Table:\n\"\"\"\n Create a table report with result agregated per test.\n\n Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n # sourcery skip: class-extract-method\n table = Table(title=title)\n headers = [\n \"Test Case\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed or error nodes\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for testcase_read in result_manager.get_testcases():\n if testcase is None or str(testcase_read) == testcase:\n results = result_manager.get_result_by_test(testcase_read)\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.name) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n testcase_read,\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n\n def report_summary_hosts(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n title: str = \"Summary per host\",\n ) -> Table:\n\"\"\"\n Create a table report with result agregated per host.\n\n Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\n \"Host IP\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed ortest case\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for host_read in result_manager.get_hosts():\n if host is None or str(host_read) == host:\n results = result_manager.get_result_by_host(host_read)\n logger.debug(\"data to use for computation\")\n logger.debug(f\"{host}: {results}\")\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.test) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n str(host_read),\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.__init__","title":"__init__()
","text":"init Class constructor
Source code in anta/reporter/__init__.py
def __init__(self) -> None:\n\"\"\"\n __init__ Class constructor\n \"\"\"\n self.colors = []\n self.colors.append(ColorManager(level=\"success\", color=RICH_COLOR_PALETTE.SUCCESS))\n self.colors.append(ColorManager(level=\"failure\", color=RICH_COLOR_PALETTE.FAILURE))\n self.colors.append(ColorManager(level=\"error\", color=RICH_COLOR_PALETTE.ERROR))\n self.colors.append(ColorManager(level=\"skipped\", color=RICH_COLOR_PALETTE.SKIPPED))\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_all","title":"report_all(result_manager, host=None, testcase=None, title='All tests results')
","text":"Create a table report with all tests for one or all devices.
Create table with full output: Host / Test / Status / Message
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required host
str
IP Address of a host to search for. Defaults to None.
None
testcase
str
A test name to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'All tests results'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_all(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n testcase: Optional[str] = None,\n title: str = \"All tests results\",\n) -> Table:\n\"\"\"\n Create a table report with all tests for one or all devices.\n\n Create table with full output: Host / Test / Status / Message\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\"Device IP\", \"Test Name\", \"Test Status\", \"Message(s)\", \"Test description\", \"Test category\"]\n table = self._build_headers(headers=headers, table=table)\n\n for result in result_manager.get_results(output_format=\"list\"):\n # pylint: disable=R0916\n if (host is None and testcase is None) or (host is not None and str(result.name) == host) or (testcase is not None and testcase == str(result.test)):\n state = self._color_result(status=str(result.result), output_type=\"str\")\n message = self._split_list_to_txt_list(result.messages) if len(result.messages) > 0 else \"\"\n test_categories = \", \".join(result.test_category)\n table.add_row(str(result.name), result.test, state, message, result.test_description, test_categories)\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_summary_hosts","title":"report_summary_hosts(result_manager, host=None, title='Summary per host')
","text":"Create a table report with result agregated per host.
Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required host
str
IP Address of a host to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'Summary per host'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_summary_hosts(\n self,\n result_manager: ResultManager,\n host: Optional[str] = None,\n title: str = \"Summary per host\",\n) -> Table:\n\"\"\"\n Create a table report with result agregated per host.\n\n Create table with full output: Host / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n host (str, optional): IP Address of a host to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n table = Table(title=title)\n headers = [\n \"Host IP\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed ortest case\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for host_read in result_manager.get_hosts():\n if host is None or str(host_read) == host:\n results = result_manager.get_result_by_host(host_read)\n logger.debug(\"data to use for computation\")\n logger.debug(f\"{host}: {results}\")\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.test) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n str(host_read),\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager/#anta.reporter.ReportTable.report_summary_tests","title":"report_summary_tests(result_manager, testcase=None, title='Summary per test case')
","text":"Create a table report with result agregated per test.
Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure
Parameters:
Name Type Description Default result_manager
ResultManager
A manager with a list of tests.
required testcase
str
A test name to search for. Defaults to None.
None
title
str
Title for the report. Defaults to \u2018All tests results\u2019.
'Summary per test case'
Returns:
Name Type Description Table
Table
A fully populated rich Table
Source code in anta/reporter/__init__.py
def report_summary_tests(\n self,\n result_manager: ResultManager,\n testcase: Optional[str] = None,\n title: str = \"Summary per test case\",\n) -> Table:\n\"\"\"\n Create a table report with result agregated per test.\n\n Create table with full output: Test / Number of success / Number of failure / Number of error / List of nodes in error or failure\n\n Args:\n result_manager (ResultManager): A manager with a list of tests.\n testcase (str, optional): A test name to search for. Defaults to None.\n title (str, optional): Title for the report. Defaults to 'All tests results'.\n\n Returns:\n Table: A fully populated rich Table\n \"\"\"\n # sourcery skip: class-extract-method\n table = Table(title=title)\n headers = [\n \"Test Case\",\n \"# of success\",\n \"# of skipped\",\n \"# of failure\",\n \"# of errors\",\n \"List of failed or error nodes\",\n ]\n table = self._build_headers(headers=headers, table=table)\n for testcase_read in result_manager.get_testcases():\n if testcase is None or str(testcase_read) == testcase:\n results = result_manager.get_result_by_test(testcase_read)\n nb_failure = len([result for result in results if result.result == \"failure\"])\n nb_error = len([result for result in results if result.result == \"error\"])\n list_failure = [str(result.name) for result in results if result.result in [\"failure\", \"error\"]]\n nb_success = len([result for result in results if result.result == \"success\"])\n nb_skipped = len([result for result in results if result.result == \"skipped\"])\n table.add_row(\n testcase_read,\n str(nb_success),\n str(nb_skipped),\n str(nb_failure),\n str(nb_error),\n str(list_failure),\n )\n return table\n
"},{"location":"api/report_manager_models/","title":"Report Manager models","text":""},{"location":"api/report_manager_models/#colormanager-entry","title":"ColorManager Entry","text":" Bases: BaseModel
Color management for status report.
Attributes:
Name Type Description level
str
Test result value.
color
str
Associated color.
Source code in anta/reporter/models.py
class ColorManager(BaseModel):\n\"\"\"Color management for status report.\n\n Attributes:\n level (str): Test result value.\n color (str): Associated color.\n \"\"\"\n\n level: str\n color: str\n\n @validator(\"level\", allow_reuse=True)\n def name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined level\n\n Raises:\n ValueError: If level is unsupported\n\n Returns:\n str: level value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n\n def style_rich(self) -> Text:\n\"\"\"\n Build a rich Text syntax with color\n\n Returns:\n Text: object with level string and its associated color.\n \"\"\"\n return Text(self.level, style=self.color)\n\n def string(self) -> str:\n\"\"\"\n Build an str with color code\n\n Returns:\n str: String with level and its associated color\n \"\"\"\n return f\"[{self.color}]{self.level}\"\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.name_must_be_in","title":"name_must_be_in(v)
","text":"Status validator
Validate status is a supported one
Parameters:
Name Type Description Default v
str
User defined level
required Raises:
Type Description ValueError
If level is unsupported
Returns:
Name Type Description str
str
level value
Source code in anta/reporter/models.py
@validator(\"level\", allow_reuse=True)\ndef name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined level\n\n Raises:\n ValueError: If level is unsupported\n\n Returns:\n str: level value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.string","title":"string()
","text":"Build an str with color code
Returns:
Name Type Description str
str
String with level and its associated color
Source code in anta/reporter/models.py
def string(self) -> str:\n\"\"\"\n Build an str with color code\n\n Returns:\n str: String with level and its associated color\n \"\"\"\n return f\"[{self.color}]{self.level}\"\n
"},{"location":"api/report_manager_models/#anta.reporter.models.ColorManager.style_rich","title":"style_rich()
","text":"Build a rich Text syntax with color
Returns:
Name Type Description Text
Text
object with level string and its associated color.
Source code in anta/reporter/models.py
def style_rich(self) -> Text:\n\"\"\"\n Build a rich Text syntax with color\n\n Returns:\n Text: object with level string and its associated color.\n \"\"\"\n return Text(self.level, style=self.color)\n
"},{"location":"api/result_manager/","title":"Result Manager module","text":""},{"location":"api/result_manager/#anta-resultmanager-module","title":"ANTA ResultManager module","text":"Helper to manage Test Results and generate reports.
Examples:
Create Inventory:
inventory_anta = AntaInventory.parse(\n inventory_file='examples/inventory.yml',\n username='ansible',\n password='ansible',\n timeout=0.5\n)\n
Create Result Manager:
manager = ResultManager()\n
Run tests for all connected devices:
for device in inventory_anta.get_inventory():\n manager.add_test_result(\nVerifyNTP(device=device).test()\n)\nmanager.add_test_result(\nVerifyEOSVersion(device=device).test(version='4.28.3M')\n)\n
Print result in native format:
manager.get_results()\n[\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyNTP',\n result='failure',\n message=\"device is not running NTP correctly\"\n ),\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyEOSVersion',\n result='success',\n message=None\n ),\n]\n
Source code in anta/result_manager/__init__.py
class ResultManager:\n\"\"\"\n Helper to manage Test Results and generate reports.\n\n Examples:\n\n Create Inventory:\n\n inventory_anta = AntaInventory.parse(\n inventory_file='examples/inventory.yml',\n username='ansible',\n password='ansible',\n timeout=0.5\n )\n\n Create Result Manager:\n\n manager = ResultManager()\n\n Run tests for all connected devices:\n\n for device in inventory_anta.get_inventory():\n manager.add_test_result(\n VerifyNTP(device=device).test()\n )\n manager.add_test_result(\n VerifyEOSVersion(device=device).test(version='4.28.3M')\n )\n\n Print result in native format:\n\n manager.get_results()\n [\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyNTP',\n result='failure',\n message=\"device is not running NTP correctly\"\n ),\n TestResult(\n host=IPv4Address('192.168.0.10'),\n test='VerifyEOSVersion',\n result='success',\n message=None\n ),\n ]\n \"\"\"\n\n def __init__(self) -> None:\n\"\"\"\n Class constructor.\n\n The status of the class is initialized to \"unset\"\n\n Then when adding a test with a status that is NOT 'error' the following\n table shows the updated status:\n\n | Current Status | Added test Status | Updated Status |\n | -------------- | ------------------------------- | -------------- |\n | unset | Any | Any |\n | skipped | unset, skipped | skipped |\n | skipped | success | success |\n | skipped | failure | failure |\n | success | unset, skipped, success | success |\n | success | failure | failure |\n | failure | unset, skipped success, failure | failure |\n\n If the status of the added test is error, the status is untouched and the\n error_status is set to True.\n \"\"\"\n logger.debug(\"Instantiate result-manager\")\n self._result_entries = ListResult()\n # Initialize status\n self.status = \"unset\"\n self.error_status = False\n\n def __len__(self) -> int:\n\"\"\"\n Implement __len__ method to count number of results.\n \"\"\"\n return len(self._result_entries)\n\n def __update_status(self, test_status: str) -> None:\n\"\"\"\n Update ResultManager status based on the table above.\n \"\"\"\n if test_status not in RESULT_OPTIONS:\n raise ValueError(\"{test_status} is not a valid result option\")\n if test_status == \"error\":\n if not self.error_status:\n logger.info(\"A test has returned an 'error' status\")\n self.error_status = True\n return\n\n if self.status == \"unset\":\n self.status = test_status\n elif self.status == \"skipped\" and test_status in {\"success\", \"failure\"}:\n self.status = test_status\n elif self.status == \"success\" and test_status == \"failure\":\n self.status = \"failure\"\n\n def add_test_result(self, entry: TestResult) -> None:\n\"\"\"Add a result to the list\n\n Args:\n entry (TestResult): TestResult data to add to the report\n \"\"\"\n logger.debug(entry)\n self._result_entries.append(entry)\n self.__update_status(entry.result)\n\n def add_test_results(self, entries: List[TestResult]) -> None:\n\"\"\"Add a list of results to the list\n\n Args:\n entries (List[TestResult]): list of TestResult data to add to the report\n \"\"\"\n for e in entries:\n self.add_test_result(e)\n\n def get_status(self, ignore_error: bool = False) -> str:\n\"\"\"\n Returns the current status including error_status if ignore_error is False\n \"\"\"\n return \"error\" if self.error_status and not ignore_error else self.status\n\n def get_results(self, output_format: str = \"native\") -> Any:\n\"\"\"\n Expose list of all test results in different format\n\n Support multiple format:\n - native: ListResults format\n - list: a list of TestResult\n - json: a native JSON format\n\n Args:\n output_format (str, optional): format selector. Can be either native/list/json. Defaults to 'native'.\n\n Returns:\n any: List of results.\n \"\"\"\n if output_format == \"list\":\n return list(self._result_entries)\n\n if output_format == \"json\":\n return json.dumps(pydantic_to_dict(self._result_entries), indent=4)\n\n # Default return for native format.\n return self._result_entries\n\n def get_result_by_test(self, test_name: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given test.\n\n Args:\n test_name (str): Test name to use to filter results\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n list[TestResult]: List of results related to the test.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.test) == test_name]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if result.test == test_name:\n result_manager_filtered.append(result)\n return result_manager_filtered\n\n def get_result_by_host(self, host_ip: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given host.\n\n Args:\n host_ip (str): IP Address of the host to use to filter results.\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n Any: List of results related to the host.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.name) == host_ip]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if str(result.name) == host_ip:\n result_manager_filtered.append(result)\n return result_manager_filtered\n\n def get_testcases(self) -> List[str]:\n\"\"\"\n Get list of name of all test cases in current manager.\n\n Returns:\n List[str]: List of names for all tests.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.test) not in result_list:\n result_list.append(str(testcase.test))\n return result_list\n\n def get_hosts(self) -> List[str]:\n\"\"\"\n Get list of IP addresses in current manager.\n\n Returns:\n List[str]: List of IP addresses.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.name) not in result_list:\n result_list.append(str(testcase.name))\n return result_list\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__init__","title":"__init__()
","text":"Class constructor.
The status of the class is initialized to \u201cunset\u201d
Then when adding a test with a status that is NOT \u2018error\u2019 the following table shows the updated status:
Current Status Added test Status Updated Status unset Any Any skipped unset, skipped skipped skipped success success skipped failure failure success unset, skipped, success success success failure failure failure unset, skipped success, failure failure If the status of the added test is error, the status is untouched and the error_status is set to True.
Source code in anta/result_manager/__init__.py
def __init__(self) -> None:\n\"\"\"\n Class constructor.\n\n The status of the class is initialized to \"unset\"\n\n Then when adding a test with a status that is NOT 'error' the following\n table shows the updated status:\n\n | Current Status | Added test Status | Updated Status |\n | -------------- | ------------------------------- | -------------- |\n | unset | Any | Any |\n | skipped | unset, skipped | skipped |\n | skipped | success | success |\n | skipped | failure | failure |\n | success | unset, skipped, success | success |\n | success | failure | failure |\n | failure | unset, skipped success, failure | failure |\n\n If the status of the added test is error, the status is untouched and the\n error_status is set to True.\n \"\"\"\n logger.debug(\"Instantiate result-manager\")\n self._result_entries = ListResult()\n # Initialize status\n self.status = \"unset\"\n self.error_status = False\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__len__","title":"__len__()
","text":"Implement len method to count number of results.
Source code in anta/result_manager/__init__.py
def __len__(self) -> int:\n\"\"\"\n Implement __len__ method to count number of results.\n \"\"\"\n return len(self._result_entries)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.__update_status","title":"__update_status(test_status)
","text":"Update ResultManager status based on the table above.
Source code in anta/result_manager/__init__.py
def __update_status(self, test_status: str) -> None:\n\"\"\"\n Update ResultManager status based on the table above.\n \"\"\"\n if test_status not in RESULT_OPTIONS:\n raise ValueError(\"{test_status} is not a valid result option\")\n if test_status == \"error\":\n if not self.error_status:\n logger.info(\"A test has returned an 'error' status\")\n self.error_status = True\n return\n\n if self.status == \"unset\":\n self.status = test_status\n elif self.status == \"skipped\" and test_status in {\"success\", \"failure\"}:\n self.status = test_status\n elif self.status == \"success\" and test_status == \"failure\":\n self.status = \"failure\"\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.add_test_result","title":"add_test_result(entry)
","text":"Add a result to the list
Parameters:
Name Type Description Default entry
TestResult
TestResult data to add to the report
required Source code in anta/result_manager/__init__.py
def add_test_result(self, entry: TestResult) -> None:\n\"\"\"Add a result to the list\n\n Args:\n entry (TestResult): TestResult data to add to the report\n \"\"\"\n logger.debug(entry)\n self._result_entries.append(entry)\n self.__update_status(entry.result)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.add_test_results","title":"add_test_results(entries)
","text":"Add a list of results to the list
Parameters:
Name Type Description Default entries
List[TestResult]
list of TestResult data to add to the report
required Source code in anta/result_manager/__init__.py
def add_test_results(self, entries: List[TestResult]) -> None:\n\"\"\"Add a list of results to the list\n\n Args:\n entries (List[TestResult]): list of TestResult data to add to the report\n \"\"\"\n for e in entries:\n self.add_test_result(e)\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_hosts","title":"get_hosts()
","text":"Get list of IP addresses in current manager.
Returns:
Type Description List[str]
List[str]: List of IP addresses.
Source code in anta/result_manager/__init__.py
def get_hosts(self) -> List[str]:\n\"\"\"\n Get list of IP addresses in current manager.\n\n Returns:\n List[str]: List of IP addresses.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.name) not in result_list:\n result_list.append(str(testcase.name))\n return result_list\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_result_by_host","title":"get_result_by_host(host_ip, output_format='native')
","text":"Get list of test result for a given host.
Parameters:
Name Type Description Default host_ip
str
IP Address of the host to use to filter results.
required output_format
str
format selector. Can be either native/list. Defaults to \u2018native\u2019.
'native'
Returns:
Name Type Description Any
Any
List of results related to the host.
Source code in anta/result_manager/__init__.py
def get_result_by_host(self, host_ip: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given host.\n\n Args:\n host_ip (str): IP Address of the host to use to filter results.\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n Any: List of results related to the host.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.name) == host_ip]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if str(result.name) == host_ip:\n result_manager_filtered.append(result)\n return result_manager_filtered\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_result_by_test","title":"get_result_by_test(test_name, output_format='native')
","text":"Get list of test result for a given test.
Parameters:
Name Type Description Default test_name
str
Test name to use to filter results
required output_format
str
format selector. Can be either native/list. Defaults to \u2018native\u2019.
'native'
Returns:
Type Description Any
list[TestResult]: List of results related to the test.
Source code in anta/result_manager/__init__.py
def get_result_by_test(self, test_name: str, output_format: str = \"native\") -> Any:\n\"\"\"\n Get list of test result for a given test.\n\n Args:\n test_name (str): Test name to use to filter results\n output_format (str, optional): format selector. Can be either native/list. Defaults to 'native'.\n\n Returns:\n list[TestResult]: List of results related to the test.\n \"\"\"\n if output_format == \"list\":\n return [result for result in self._result_entries if str(result.test) == test_name]\n\n result_manager_filtered = ListResult()\n for result in self._result_entries:\n if result.test == test_name:\n result_manager_filtered.append(result)\n return result_manager_filtered\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_results","title":"get_results(output_format='native')
","text":"Expose list of all test results in different format
Support multiple format - native: ListResults format
- list: a list of TestResult
- json: a native JSON format
Parameters:
Name Type Description Default output_format
str
format selector. Can be either native/list/json. Defaults to \u2018native\u2019.
'native'
Returns:
Name Type Description any
Any
List of results.
Source code in anta/result_manager/__init__.py
def get_results(self, output_format: str = \"native\") -> Any:\n\"\"\"\n Expose list of all test results in different format\n\n Support multiple format:\n - native: ListResults format\n - list: a list of TestResult\n - json: a native JSON format\n\n Args:\n output_format (str, optional): format selector. Can be either native/list/json. Defaults to 'native'.\n\n Returns:\n any: List of results.\n \"\"\"\n if output_format == \"list\":\n return list(self._result_entries)\n\n if output_format == \"json\":\n return json.dumps(pydantic_to_dict(self._result_entries), indent=4)\n\n # Default return for native format.\n return self._result_entries\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_status","title":"get_status(ignore_error=False)
","text":"Returns the current status including error_status if ignore_error is False
Source code in anta/result_manager/__init__.py
def get_status(self, ignore_error: bool = False) -> str:\n\"\"\"\n Returns the current status including error_status if ignore_error is False\n \"\"\"\n return \"error\" if self.error_status and not ignore_error else self.status\n
"},{"location":"api/result_manager/#anta.result_manager.ResultManager.get_testcases","title":"get_testcases()
","text":"Get list of name of all test cases in current manager.
Returns:
Type Description List[str]
List[str]: List of names for all tests.
Source code in anta/result_manager/__init__.py
def get_testcases(self) -> List[str]:\n\"\"\"\n Get list of name of all test cases in current manager.\n\n Returns:\n List[str]: List of names for all tests.\n \"\"\"\n result_list = []\n for testcase in self._result_entries:\n if str(testcase.test) not in result_list:\n result_list.append(str(testcase.test))\n return result_list\n
"},{"location":"api/result_manager_models/","title":"Result Manager models","text":""},{"location":"api/result_manager_models/#testresult-entry","title":"TestResult Entry","text":" Bases: BaseModel
Describe the result of a test from a single device.
Attributes:
Name Type Description name
str
Device name where the test has run.
test
str
Test name runs on the device.
test_category
List[str]
List of test categories the test belongs to.
test_description
str
Test description.
results
str
Result of the test. Can be one of [\u201cunset\u201d, \u201csuccess\u201d, \u201cfailure\u201d, \u201cerror\u201d, \u201cskipped\u201d].
message
str
Message to report after the test if any.
Source code in anta/result_manager/models.py
class TestResult(BaseModel):\n\"\"\"\n Describe the result of a test from a single device.\n\n Attributes:\n name (str): Device name where the test has run.\n test (str): Test name runs on the device.\n test_category (List[str]): List of test categories the test belongs to.\n test_description (str): Test description.\n results (str): Result of the test. Can be one of [\"unset\", \"success\", \"failure\", \"error\", \"skipped\"].\n message (str, optional): Message to report after the test if any.\n \"\"\"\n\n name: str\n test: str\n test_category: List[str]\n test_description: str\n result: str = \"unset\"\n messages: List[str] = []\n\n @classmethod\n @validator(\"result\", allow_reuse=True)\n def name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined status\n\n Raises:\n ValueError: If status is unsupported\n\n Returns:\n str: status value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n\n def is_success(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to success\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"success\", message)\n\n def is_failure(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to failure\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"failure\", message)\n\n def is_skipped(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to skipped\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"skipped\", message)\n\n def is_error(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to error\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"error\", message)\n\n def _set_status(self, status: str, message: str = \"\") -> bool:\n\"\"\"\n Set status and insert optional message\n\n Args:\n status (str): status of the test\n message (str): optional message\n\n Returns:\n bool: Always true\n \"\"\"\n self.result = status\n if message != \"\":\n self.messages.append(message)\n return True\n\n def __str__(self) -> str:\n\"\"\"\n Returns a human readable string of this TestResult\n \"\"\"\n return f\"Test {self.test} on device {self.name} has result {self.result}\"\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.__str__","title":"__str__()
","text":"Returns a human readable string of this TestResult
Source code in anta/result_manager/models.py
def __str__(self) -> str:\n\"\"\"\n Returns a human readable string of this TestResult\n \"\"\"\n return f\"Test {self.test} on device {self.name} has result {self.result}\"\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_error","title":"is_error(message='')
","text":"Helper to set status to error
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_error(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to error\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"error\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_failure","title":"is_failure(message='')
","text":"Helper to set status to failure
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_failure(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to failure\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"failure\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_skipped","title":"is_skipped(message='')
","text":"Helper to set status to skipped
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_skipped(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to skipped\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"skipped\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.is_success","title":"is_success(message='')
","text":"Helper to set status to success
Parameters:
Name Type Description Default message
str
Optional message related to the test
''
Returns:
Name Type Description bool
bool
Always true
Source code in anta/result_manager/models.py
def is_success(self, message: str = \"\") -> bool:\n\"\"\"\n Helper to set status to success\n\n Args:\n message (str): Optional message related to the test\n\n Returns:\n bool: Always true\n \"\"\"\n return self._set_status(\"success\", message)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.TestResult.name_must_be_in","title":"name_must_be_in(v)
classmethod
","text":"Status validator
Validate status is a supported one
Parameters:
Name Type Description Default v
str
User defined status
required Raises:
Type Description ValueError
If status is unsupported
Returns:
Name Type Description str
str
status value
Source code in anta/result_manager/models.py
@classmethod\n@validator(\"result\", allow_reuse=True)\ndef name_must_be_in(cls, v: str) -> str:\n\"\"\"\n Status validator\n\n Validate status is a supported one\n\n Args:\n v (str): User defined status\n\n Raises:\n ValueError: If status is unsupported\n\n Returns:\n str: status value\n \"\"\"\n if v not in RESULT_OPTIONS:\n raise ValueError(f\"must be one of {RESULT_OPTIONS}\")\n return v\n
"},{"location":"api/result_manager_models/#listresult","title":"ListResult","text":" Bases: BaseModel
List result for all tests on all devices.
Attributes:
Name Type Description __root__
List[TestResult]
A list of TestResult objects.
Source code in anta/result_manager/models.py
class ListResult(BaseModel):\n\"\"\"\n List result for all tests on all devices.\n\n Attributes:\n __root__ (List[TestResult]): A list of TestResult objects.\n \"\"\"\n\n # pylint: disable=R0801\n\n __root__: List[TestResult] = []\n\n def extend(self, values: List[TestResult]) -> None:\n\"\"\"Add support for extend method.\"\"\"\n self.__root__.extend(values)\n\n def append(self, value: TestResult) -> None:\n\"\"\"Add support for append method.\"\"\"\n self.__root__.append(value)\n\n def __iter__(self) -> Iterator[TestResult]: # type: ignore\n\"\"\"Use custom iter method.\"\"\"\n # TODO - mypy is not happy because we overwrite BaseModel.__iter__\n # return type and are breaking Liskov Substitution Principle.\n return iter(self.__root__)\n\n def __getitem__(self, item: int) -> TestResult:\n\"\"\"Use custom getitem method.\"\"\"\n return self.__root__[item]\n\n def __len__(self) -> int:\n\"\"\"Support for length of __root__\"\"\"\n return len(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__getitem__","title":"__getitem__(item)
","text":"Use custom getitem method.
Source code in anta/result_manager/models.py
def __getitem__(self, item: int) -> TestResult:\n\"\"\"Use custom getitem method.\"\"\"\n return self.__root__[item]\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__iter__","title":"__iter__()
","text":"Use custom iter method.
Source code in anta/result_manager/models.py
def __iter__(self) -> Iterator[TestResult]: # type: ignore\n\"\"\"Use custom iter method.\"\"\"\n # TODO - mypy is not happy because we overwrite BaseModel.__iter__\n # return type and are breaking Liskov Substitution Principle.\n return iter(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.__len__","title":"__len__()
","text":"Support for length of root
Source code in anta/result_manager/models.py
def __len__(self) -> int:\n\"\"\"Support for length of __root__\"\"\"\n return len(self.__root__)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.append","title":"append(value)
","text":"Add support for append method.
Source code in anta/result_manager/models.py
def append(self, value: TestResult) -> None:\n\"\"\"Add support for append method.\"\"\"\n self.__root__.append(value)\n
"},{"location":"api/result_manager_models/#anta.result_manager.models.ListResult.extend","title":"extend(values)
","text":"Add support for extend method.
Source code in anta/result_manager/models.py
def extend(self, values: List[TestResult]) -> None:\n\"\"\"Add support for extend method.\"\"\"\n self.__root__.extend(values)\n
"},{"location":"api/tests.aaa/","title":"AAA","text":""},{"location":"api/tests.aaa/#anta-catalog-for-aaa-tests","title":"ANTA catalog for AAA tests","text":"Test functions related to the EOS various AAA settings
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctConsoleMethods","title":"VerifyAcctConsoleMethods
","text":" Bases: AntaTest
Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).
Expected Results - success: The test will pass if the provided AAA accounting console method list is matching in the configured accounting types.
- failure: The test will fail if the provided AAA accounting console method list is NOT matching in the configured accounting types.
- skipped: The test will be skipped if the AAA accounting console method list or accounting type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAcctConsoleMethods(AntaTest):\n\"\"\"\n Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA accounting console method list is matching in the configured accounting types.\n * failure: The test will fail if the provided AAA accounting console method list is NOT matching in the configured accounting types.\n * skipped: The test will be skipped if the AAA accounting console method list or accounting type list are not provided.\n \"\"\"\n\n name = \"VerifyAcctConsoleMethods\"\n description = \"Verifies the AAA accounting console method lists for different accounting types (system, exec, commands, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods accounting\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctConsoleMethods validation.\n\n Args:\n methods: List of AAA accounting console methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"consoleAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"consoleMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA console accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting console methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctConsoleMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAcctConsoleMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA accounting console methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of accounting types to verify. List elements must be: commands, exec, system, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctConsoleMethods validation.\n\n Args:\n methods: List of AAA accounting console methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"consoleAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"consoleMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA console accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting console methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctDefaultMethods","title":"VerifyAcctDefaultMethods
","text":" Bases: AntaTest
Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).
Expected Results - success: The test will pass if the provided AAA accounting default method list is matching in the configured accounting types.
- failure: The test will fail if the provided AAA accounting default method list is NOT matching in the configured accounting types.
- skipped: The test will be skipped if the AAA accounting default method list or accounting type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAcctDefaultMethods(AntaTest):\n\"\"\"\n Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA accounting default method list is matching in the configured accounting types.\n * failure: The test will fail if the provided AAA accounting default method list is NOT matching in the configured accounting types.\n * skipped: The test will be skipped if the AAA accounting default method list or accounting type list are not provided.\n \"\"\"\n\n name = \"VerifyAcctDefaultMethods\"\n description = \"Verifies the AAA accounting default method lists for different accounting types (system, exec, commands, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods accounting\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctDefaultMethods validation.\n\n Args:\n methods: List of AAA accounting default methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"defaultAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"defaultMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA default accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting default methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAcctDefaultMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAcctDefaultMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA accounting default methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of accounting types to verify. List elements must be: commands, exec, system, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAcctDefaultMethods validation.\n\n Args:\n methods: List of AAA accounting default methods. Methods should be in the right order.\n auth_types: List of accounting types to verify. List elements must be: commands, exec, system, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"system\", \"exec\", \"commands\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n not_configured = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AcctMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if not command_output[auth_type_key][method_key].get(\"defaultAction\"):\n not_configured.append(auth_type)\n\n if command_output[auth_type_key][method_key][\"defaultMethods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not_configured:\n self.result.is_failure(f\"AAA default accounting is not configured for {not_configured}\")\n return\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA accounting default methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthenMethods","title":"VerifyAuthenMethods
","text":" Bases: AntaTest
Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).
Expected Results - success: The test will pass if the provided AAA authentication method list is matching in the configured authentication types.
- failure: The test will fail if the provided AAA authentication method list is NOT matching in the configured authentication types.
- skipped: The test will be skipped if the AAA authentication method list or authentication type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAuthenMethods(AntaTest):\n\"\"\"\n Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).\n\n Expected Results:\n * success: The test will pass if the provided AAA authentication method list is matching in the configured authentication types.\n * failure: The test will fail if the provided AAA authentication method list is NOT matching in the configured authentication types.\n * skipped: The test will be skipped if the AAA authentication method list or authentication type list are not provided.\n \"\"\"\n\n name = \"VerifyAuthenMethods\"\n description = \"Verifies the AAA authentication method lists for different authentication types (login, enable, dot1x).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods authentication\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthenMethods validation.\n\n Args:\n methods: List of AAA authentication methods. Methods should be in the right order.\n auth_types: List of authentication types to verify. List elements must be: login, enable, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"login\", \"enable\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthenMethods\"\n\n if auth_type_key == \"loginAuthenMethods\":\n if not command_output[auth_type_key].get(\"login\"):\n self.result.is_failure(\"AAA authentication methods are not configured for login console\")\n return\n\n if command_output[auth_type_key][\"login\"][\"methods\"] != methods_with_group:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for login console\")\n return\n\n if command_output[auth_type_key][\"default\"][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthenMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAuthenMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA authentication methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of authentication types to verify. List elements must be: login, enable, dot1x.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthenMethods validation.\n\n Args:\n methods: List of AAA authentication methods. Methods should be in the right order.\n auth_types: List of authentication types to verify. List elements must be: login, enable, dot1x.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n methods_with_group = _check_group_methods(methods)\n\n _check_auth_type(auth_types, [\"login\", \"enable\", \"dot1x\"])\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthenMethods\"\n\n if auth_type_key == \"loginAuthenMethods\":\n if not command_output[auth_type_key].get(\"login\"):\n self.result.is_failure(\"AAA authentication methods are not configured for login console\")\n return\n\n if command_output[auth_type_key][\"login\"][\"methods\"] != methods_with_group:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for login console\")\n return\n\n if command_output[auth_type_key][\"default\"][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authentication methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthzMethods","title":"VerifyAuthzMethods
","text":" Bases: AntaTest
Verifies the AAA authorization method lists for different authorization types (commands, exec).
Expected Results - success: The test will pass if the provided AAA authorization method list is matching in the configured authorization types.
- failure: The test will fail if the provided AAA authorization method list is NOT matching in the configured authorization types.
- skipped: The test will be skipped if the AAA authentication method list or authorization type list are not provided.
Source code in anta/tests/aaa.py
class VerifyAuthzMethods(AntaTest):\n\"\"\"\n Verifies the AAA authorization method lists for different authorization types (commands, exec).\n\n Expected Results:\n * success: The test will pass if the provided AAA authorization method list is matching in the configured authorization types.\n * failure: The test will fail if the provided AAA authorization method list is NOT matching in the configured authorization types.\n * skipped: The test will be skipped if the AAA authentication method list or authorization type list are not provided.\n \"\"\"\n\n name = \"VerifyAuthzMethods\"\n description = \"Verifies the AAA authorization method lists for different authorization types (commands, exec).\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show aaa methods authorization\")]\n\n @AntaTest.anta_test\n def test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthzMethods validation.\n\n Args:\n methods: List of AAA authorization methods. Methods should be in the right order.\n auth_types: List of authorization types to verify. List elements must be: commands, exec.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n _check_auth_type(auth_types, [\"commands\", \"exec\"])\n\n methods_with_group = _check_group_methods(methods)\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthzMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if command_output[auth_type_key][method_key][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authorization methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyAuthzMethods.test","title":"test(methods=None, auth_types=None)
","text":"Run VerifyAuthzMethods validation.
Parameters:
Name Type Description Default methods
Optional[List[str]]
List of AAA authorization methods. Methods should be in the right order.
None
auth_types
Optional[List[str]]
List of authorization types to verify. List elements must be: commands, exec.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, methods: Optional[List[str]] = None, auth_types: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyAuthzMethods validation.\n\n Args:\n methods: List of AAA authorization methods. Methods should be in the right order.\n auth_types: List of authorization types to verify. List elements must be: commands, exec.\n \"\"\"\n if not methods or not auth_types:\n self.result.is_skipped(f\"{self.__class__.name} did not run because methods or auth_types were not supplied\")\n return\n\n _check_auth_type(auth_types, [\"commands\", \"exec\"])\n\n methods_with_group = _check_group_methods(methods)\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n not_matching = []\n\n for auth_type in auth_types:\n auth_type_key = f\"{auth_type}AuthzMethods\"\n\n method_key = list(command_output[auth_type_key].keys())[0]\n\n if command_output[auth_type_key][method_key][\"methods\"] != methods_with_group:\n not_matching.append(auth_type)\n\n if not not_matching:\n self.result.is_success()\n else:\n self.result.is_failure(f\"AAA authorization methods {methods} are not matching for {not_matching}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServerGroups","title":"VerifyTacacsServerGroups
","text":" Bases: AntaTest
Verifies if the provided TACACS server group(s) are configured.
Expected Results - success: The test will pass if the provided TACACS server group(s) are configured.
- failure: The test will fail if one or all the provided TACACS server group(s) are NOT configured.
- skipped: The test will be skipped if TACACS server group(s) are not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsServerGroups(AntaTest):\n\"\"\"\n Verifies if the provided TACACS server group(s) are configured.\n\n Expected Results:\n * success: The test will pass if the provided TACACS server group(s) are configured.\n * failure: The test will fail if one or all the provided TACACS server group(s) are NOT configured.\n * skipped: The test will be skipped if TACACS server group(s) are not provided.\n \"\"\"\n\n name = \"VerifyTacacsServerGroups\"\n description = \"Verifies if the provided TACACS server group(s) are configured.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, groups: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTacacsServerGroups validation.\n\n Args:\n groups: List of TACACS server group.\n \"\"\"\n if not groups:\n self.result.is_skipped(f\"{self.__class__.name} did not run because groups were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_groups = command_output[\"groups\"]\n\n if not tacacs_groups:\n self.result.is_failure(\"No TACACS server group(s) are configured\")\n return\n\n not_configured = [group for group in groups if group not in tacacs_groups]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS server group(s) {not_configured} are not configured\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServerGroups.test","title":"test(groups=None)
","text":"Run VerifyTacacsServerGroups validation.
Parameters:
Name Type Description Default groups
Optional[List[str]]
List of TACACS server group.
None
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, groups: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTacacsServerGroups validation.\n\n Args:\n groups: List of TACACS server group.\n \"\"\"\n if not groups:\n self.result.is_skipped(f\"{self.__class__.name} did not run because groups were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_groups = command_output[\"groups\"]\n\n if not tacacs_groups:\n self.result.is_failure(\"No TACACS server group(s) are configured\")\n return\n\n not_configured = [group for group in groups if group not in tacacs_groups]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS server group(s) {not_configured} are not configured\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServers","title":"VerifyTacacsServers
","text":" Bases: AntaTest
Verifies TACACS servers are configured for a specified VRF.
Expected Results - success: The test will pass if the provided TACACS servers are configured in the specified VRF.
- failure: The test will fail if the provided TACACS servers are NOT configured in the specified VRF.
- skipped: The test will be skipped if TACACS servers or VRF are not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsServers(AntaTest):\n\"\"\"\n Verifies TACACS servers are configured for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided TACACS servers are configured in the specified VRF.\n * failure: The test will fail if the provided TACACS servers are NOT configured in the specified VRF.\n * skipped: The test will be skipped if TACACS servers or VRF are not provided.\n \"\"\"\n\n name = \"VerifyTacacsServers\"\n description = \"Verifies TACACS servers are configured for a specified VRF.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, servers: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsServers validation.\n\n Args:\n servers: List of TACACS servers IP addresses.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not servers or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because servers or vrf were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_servers = command_output[\"tacacsServers\"]\n\n if not tacacs_servers:\n self.result.is_failure(\"No TACACS servers are configured\")\n return\n\n not_configured = [\n server\n for server in servers\n if not any(server == tacacs_server[\"serverInfo\"][\"hostname\"] and vrf == tacacs_server[\"serverInfo\"][\"vrf\"] for tacacs_server in tacacs_servers)\n ]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsServers.test","title":"test(servers=None, vrf='default')
","text":"Run VerifyTacacsServers validation.
Parameters:
Name Type Description Default servers
Optional[List[str]]
List of TACACS servers IP addresses.
None
vrf
str
The name of the VRF to transport TACACS messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, servers: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsServers validation.\n\n Args:\n servers: List of TACACS servers IP addresses.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not servers or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because servers or vrf were not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n tacacs_servers = command_output[\"tacacsServers\"]\n\n if not tacacs_servers:\n self.result.is_failure(\"No TACACS servers are configured\")\n return\n\n not_configured = [\n server\n for server in servers\n if not any(server == tacacs_server[\"serverInfo\"][\"hostname\"] and vrf == tacacs_server[\"serverInfo\"][\"vrf\"] for tacacs_server in tacacs_servers)\n ]\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"TACACS servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsSourceIntf","title":"VerifyTacacsSourceIntf
","text":" Bases: AntaTest
Verifies TACACS source-interface for a specified VRF.
Expected Results - success: The test will pass if the provided TACACS source-interface is configured in the specified VRF.
- failure: The test will fail if the provided TACACS source-interface is NOT configured in the specified VRF.
- skipped: The test will be skipped if source-interface or VRF is not provided.
Source code in anta/tests/aaa.py
class VerifyTacacsSourceIntf(AntaTest):\n\"\"\"\n Verifies TACACS source-interface for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided TACACS source-interface is configured in the specified VRF.\n * failure: The test will fail if the provided TACACS source-interface is NOT configured in the specified VRF.\n * skipped: The test will be skipped if source-interface or VRF is not provided.\n \"\"\"\n\n name = \"VerifyTacacsSourceIntf\"\n description = \"Verifies TACACS source-interface for a specified VRF.\"\n categories = [\"aaa\"]\n commands = [AntaTestCommand(command=\"show tacacs\")]\n\n @AntaTest.anta_test\n def test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsSourceIntf validation.\n\n Args:\n intf: Source-interface to use as source IP of TACACS messages.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"srcIntf\"][vrf] == intf:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Wrong source-interface configured in VRF {vrf}\")\n\n except KeyError:\n self.result.is_failure(f\"Source-interface {intf} is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.aaa/#anta.tests.aaa.VerifyTacacsSourceIntf.test","title":"test(intf=None, vrf='default')
","text":"Run VerifyTacacsSourceIntf validation.
Parameters:
Name Type Description Default intf
Optional[str]
Source-interface to use as source IP of TACACS messages.
None
vrf
str
The name of the VRF to transport TACACS messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/aaa.py
@AntaTest.anta_test\ndef test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyTacacsSourceIntf validation.\n\n Args:\n intf: Source-interface to use as source IP of TACACS messages.\n vrf: The name of the VRF to transport TACACS messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"srcIntf\"][vrf] == intf:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Wrong source-interface configured in VRF {vrf}\")\n\n except KeyError:\n self.result.is_failure(f\"Source-interface {intf} is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.configuration/","title":"Configuration","text":""},{"location":"api/tests.configuration/#anta-catalog-for-configuration-tests","title":"ANTA catalog for configuration tests","text":"Test functions related to the device configuration
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyRunningConfigDiffs","title":"VerifyRunningConfigDiffs
","text":" Bases: AntaTest
Verifies there is no difference between the running-config and the startup-config.
Source code in anta/tests/configuration.py
class VerifyRunningConfigDiffs(AntaTest):\n\"\"\"\n Verifies there is no difference between the running-config and the startup-config.\n \"\"\"\n\n name = \"VerifyRunningConfigDiffs\"\n description = \"\"\n categories = [\"configuration\"]\n commands = [AntaTestCommand(command=\"show running-config diffs\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyRunningConfigDiffs validation\"\"\"\n logger.debug(f\"self.instance_commands is {self.instance_commands}\")\n command_output = self.instance_commands[0].output\n logger.debug(f\"command_output is {command_output}\")\n if command_output is None or command_output == \"\":\n self.result.is_success()\n else:\n self.result.is_failure()\n self.result.is_failure(str(command_output))\n logger.debug(f\"result is {self.result}\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyRunningConfigDiffs.test","title":"test()
","text":"Run VerifyRunningConfigDiffs validation
Source code in anta/tests/configuration.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyRunningConfigDiffs validation\"\"\"\n logger.debug(f\"self.instance_commands is {self.instance_commands}\")\n command_output = self.instance_commands[0].output\n logger.debug(f\"command_output is {command_output}\")\n if command_output is None or command_output == \"\":\n self.result.is_success()\n else:\n self.result.is_failure()\n self.result.is_failure(str(command_output))\n logger.debug(f\"result is {self.result}\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyZeroTouch","title":"VerifyZeroTouch
","text":" Bases: AntaTest
Verifies ZeroTouch is disabled.
Source code in anta/tests/configuration.py
class VerifyZeroTouch(AntaTest):\n\"\"\"\n Verifies ZeroTouch is disabled.\n \"\"\"\n\n name = \"VerifyZeroTouch\"\n description = \"Verifies ZeroTouch is disabled.\"\n categories = [\"configuration\"]\n commands = [AntaTestCommand(command=\"show zerotouch\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyZeroTouch validation\"\"\"\n\n command_output = self.instance_commands[0].output\n\n assert isinstance(command_output, dict)\n if command_output[\"mode\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"ZTP is NOT disabled\")\n
"},{"location":"api/tests.configuration/#anta.tests.configuration.VerifyZeroTouch.test","title":"test()
","text":"Run VerifyZeroTouch validation
Source code in anta/tests/configuration.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyZeroTouch validation\"\"\"\n\n command_output = self.instance_commands[0].output\n\n assert isinstance(command_output, dict)\n if command_output[\"mode\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"ZTP is NOT disabled\")\n
"},{"location":"api/tests.connectivity/","title":"Connectivity","text":""},{"location":"api/tests.connectivity/#anta-catalog-for-connectivity-tests","title":"ANTA catalog for connectivity tests","text":"Test functions related to various connectivity checks
"},{"location":"api/tests.connectivity/#anta.tests.connectivity.VerifyReachability","title":"VerifyReachability
","text":" Bases: AntaTest
Test network reachability to one or many destination IP(s).
Expected Results - success: The test will pass if all destination IP(s) are reachable.
- failure: The test will fail if one or many destination IP(s) are unreachable.
- error: The test will give an error if the destination IP(s) or the source interface/IP(s) are not provided as template_params.
Source code in anta/tests/connectivity.py
class VerifyReachability(AntaTest):\n\"\"\"\n Test network reachability to one or many destination IP(s).\n\n Expected Results:\n * success: The test will pass if all destination IP(s) are reachable.\n * failure: The test will fail if one or many destination IP(s) are unreachable.\n * error: The test will give an error if the destination IP(s) or the source interface/IP(s) are not provided as template_params.\n \"\"\"\n\n name = \"VerifyReachability\"\n description = \"Test the network reachability to one or many destination IP(s).\"\n categories = [\"connectivity\"]\n template = AntaTestTemplate(template=\"ping {dst} source {src} repeat 2\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyReachability validation.\n \"\"\"\n\n failures = []\n\n for index, command in enumerate(self.instance_commands):\n src, dst = (cast(Dict[str, str], command.template_params)[\"src\"], cast(Dict[str, str], command.template_params)[\"dst\"])\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n if \"2 received\" not in command_output[\"messages\"][0]:\n failures.append((src, dst))\n\n if not failures:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"Connectivity test failed for the following source-destination pairs: {failures}\")\n
"},{"location":"api/tests.connectivity/#anta.tests.connectivity.VerifyReachability.test","title":"test()
","text":"Run VerifyReachability validation.
Source code in anta/tests/connectivity.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyReachability validation.\n \"\"\"\n\n failures = []\n\n for index, command in enumerate(self.instance_commands):\n src, dst = (cast(Dict[str, str], command.template_params)[\"src\"], cast(Dict[str, str], command.template_params)[\"dst\"])\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n if \"2 received\" not in command_output[\"messages\"][0]:\n failures.append((src, dst))\n\n if not failures:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"Connectivity test failed for the following source-destination pairs: {failures}\")\n
"},{"location":"api/tests.field_notices/","title":"Field Notices","text":""},{"location":"api/tests.field_notices/#anta-catalog-for-field-notices-tests","title":"ANTA catalog for Field Notices tests","text":"Test functions to flag field notices
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice44Resolution","title":"VerifyFieldNotice44Resolution
","text":" Bases: AntaTest
Verifies the device is using an Aboot version that fix the bug discussed in the field notice 44 (Aboot manages system settings prior to EOS initialization).
https://www.arista.com/en/support/advisories-notices/field-notice/8756-field-notice-44
Source code in anta/tests/field_notices.py
class VerifyFieldNotice44Resolution(AntaTest):\n\"\"\"\n Verifies the device is using an Aboot version that fix the bug discussed\n in the field notice 44 (Aboot manages system settings prior to EOS initialization).\n\n https://www.arista.com/en/support/advisories-notices/field-notice/8756-field-notice-44\n \"\"\"\n\n name = \"VerifyFieldNotice44Resolution\"\n description = (\n \"Verifies the device is using an Aboot version that fix the bug discussed in the field notice 44 (Aboot manages system settings prior to EOS initialization)\"\n )\n categories = [\"field notices\", \"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n # TODO maybe implement ONLY ON PLATFORMS instead\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice44Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\n \"DCS-7010T-48\",\n \"DCS-7010T-48-DC\",\n \"DCS-7050TX-48\",\n \"DCS-7050TX-64\",\n \"DCS-7050TX-72\",\n \"DCS-7050TX-72Q\",\n \"DCS-7050TX-96\",\n \"DCS-7050TX2-128\",\n \"DCS-7050SX-64\",\n \"DCS-7050SX-72\",\n \"DCS-7050SX-72Q\",\n \"DCS-7050SX2-72Q\",\n \"DCS-7050SX-96\",\n \"DCS-7050SX2-128\",\n \"DCS-7050QX-32S\",\n \"DCS-7050QX2-32S\",\n \"DCS-7050SX3-48YC12\",\n \"DCS-7050CX3-32S\",\n \"DCS-7060CX-32S\",\n \"DCS-7060CX2-32S\",\n \"DCS-7060SX2-48YC6\",\n \"DCS-7160-48YC6\",\n \"DCS-7160-48TC6\",\n \"DCS-7160-32CQ\",\n \"DCS-7280SE-64\",\n \"DCS-7280SE-68\",\n \"DCS-7280SE-72\",\n \"DCS-7150SC-24-CLD\",\n \"DCS-7150SC-64-CLD\",\n \"DCS-7020TR-48\",\n \"DCS-7020TRA-48\",\n \"DCS-7020SR-24C2\",\n \"DCS-7020SRG-24C2\",\n \"DCS-7280TR-48C6\",\n \"DCS-7280TRA-48C6\",\n \"DCS-7280SR-48C6\",\n \"DCS-7280SRA-48C6\",\n \"DCS-7280SRAM-48C6\",\n \"DCS-7280SR2K-48C6-M\",\n \"DCS-7280SR2-48YC6\",\n \"DCS-7280SR2A-48YC6\",\n \"DCS-7280SRM-40CX2\",\n \"DCS-7280QR-C36\",\n \"DCS-7280QRA-C36S\",\n ]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n\n model = cast(str, command_output[\"modelName\"])\n # TODO this list could be a regex\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"device is not impacted by FN044\")\n return\n\n for component in command_output[\"details\"][\"components\"]:\n if component[\"name\"] == \"Aboot\":\n aboot_version = component[\"version\"].split(\"-\")[2]\n self.result.is_success()\n if aboot_version.startswith(\"4.0.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"4.1.\") and int(aboot_version.split(\".\")[2]) < 1:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.0.\") and int(aboot_version.split(\".\")[2]) < 9:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.1.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice44Resolution.test","title":"test()
","text":"Run VerifyFieldNotice44Resolution validation
Source code in anta/tests/field_notices.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice44Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\n \"DCS-7010T-48\",\n \"DCS-7010T-48-DC\",\n \"DCS-7050TX-48\",\n \"DCS-7050TX-64\",\n \"DCS-7050TX-72\",\n \"DCS-7050TX-72Q\",\n \"DCS-7050TX-96\",\n \"DCS-7050TX2-128\",\n \"DCS-7050SX-64\",\n \"DCS-7050SX-72\",\n \"DCS-7050SX-72Q\",\n \"DCS-7050SX2-72Q\",\n \"DCS-7050SX-96\",\n \"DCS-7050SX2-128\",\n \"DCS-7050QX-32S\",\n \"DCS-7050QX2-32S\",\n \"DCS-7050SX3-48YC12\",\n \"DCS-7050CX3-32S\",\n \"DCS-7060CX-32S\",\n \"DCS-7060CX2-32S\",\n \"DCS-7060SX2-48YC6\",\n \"DCS-7160-48YC6\",\n \"DCS-7160-48TC6\",\n \"DCS-7160-32CQ\",\n \"DCS-7280SE-64\",\n \"DCS-7280SE-68\",\n \"DCS-7280SE-72\",\n \"DCS-7150SC-24-CLD\",\n \"DCS-7150SC-64-CLD\",\n \"DCS-7020TR-48\",\n \"DCS-7020TRA-48\",\n \"DCS-7020SR-24C2\",\n \"DCS-7020SRG-24C2\",\n \"DCS-7280TR-48C6\",\n \"DCS-7280TRA-48C6\",\n \"DCS-7280SR-48C6\",\n \"DCS-7280SRA-48C6\",\n \"DCS-7280SRAM-48C6\",\n \"DCS-7280SR2K-48C6-M\",\n \"DCS-7280SR2-48YC6\",\n \"DCS-7280SR2A-48YC6\",\n \"DCS-7280SRM-40CX2\",\n \"DCS-7280QR-C36\",\n \"DCS-7280QRA-C36S\",\n ]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n\n model = cast(str, command_output[\"modelName\"])\n # TODO this list could be a regex\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"device is not impacted by FN044\")\n return\n\n for component in command_output[\"details\"][\"components\"]:\n if component[\"name\"] == \"Aboot\":\n aboot_version = component[\"version\"].split(\"-\")[2]\n self.result.is_success()\n if aboot_version.startswith(\"4.0.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"4.1.\") and int(aboot_version.split(\".\")[2]) < 1:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.0.\") and int(aboot_version.split(\".\")[2]) < 9:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n elif aboot_version.startswith(\"6.1.\") and int(aboot_version.split(\".\")[2]) < 7:\n self.result.is_failure(f\"device is running incorrect version of aboot ({aboot_version})\")\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice72Resolution","title":"VerifyFieldNotice72Resolution
","text":" Bases: AntaTest
Checks if the device is potentially exposed to Field Notice 72, and if the issue has been mitigated.
https://www.arista.com/en/support/advisories-notices/field-notice/17410-field-notice-0072
Source code in anta/tests/field_notices.py
class VerifyFieldNotice72Resolution(AntaTest):\n\"\"\"\n Checks if the device is potentially exposed to Field Notice 72, and if the issue has been mitigated.\n\n https://www.arista.com/en/support/advisories-notices/field-notice/17410-field-notice-0072\n \"\"\"\n\n name = \"VerifyFieldNotice72Resolution\"\n description = \"Verifies if the device has exposeure to FN72, and if the issue has been mitigated\"\n categories = [\"field notices\", \"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n # TODO maybe implement ONLY ON PLATFORMS instead\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice72Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\"DCS-7280SR3-48YC8\", \"DCS-7280SR3K-48YC8\"]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n model = cast(str, command_output[\"modelName\"])\n\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"Platform is not impacted by FN072\")\n return\n\n serial = command_output[\"serialNumber\"]\n number = int(serial[3:7])\n\n if \"JPE\" not in serial and \"JAS\" not in serial:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JPE\" in serial and number >= 2131:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JPE\" in serial and number >= 2134:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n # Because each of the if checks above will return if taken, we only run the long\n # check if we get this far\n for entry in command_output[\"details\"][\"components\"]:\n if entry[\"name\"] == \"FixedSystemvrm1\":\n if int(entry[\"version\"]) < 7:\n self.result.is_failure(\"Device is exposed to FN72\")\n else:\n self.result.is_success(\"FN72 is mitigated\")\n return\n # We should never hit this point\n self.result.is_error(\"Error in running test - FixedSystemvrm1 not found\")\n return\n
"},{"location":"api/tests.field_notices/#anta.tests.field_notices.VerifyFieldNotice72Resolution.test","title":"test()
","text":"Run VerifyFieldNotice72Resolution validation
Source code in anta/tests/field_notices.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None: # type: ignore[override]\n\"\"\"Run VerifyFieldNotice72Resolution validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n devices = [\"DCS-7280SR3-48YC8\", \"DCS-7280SR3K-48YC8\"]\n variants = [\"-SSD-F\", \"-SSD-R\", \"-M-F\", \"-M-R\", \"-F\", \"-R\"]\n model = cast(str, command_output[\"modelName\"])\n\n for variant in variants:\n model = model.replace(variant, \"\")\n if model not in devices:\n self.result.is_skipped(\"Platform is not impacted by FN072\")\n return\n\n serial = command_output[\"serialNumber\"]\n number = int(serial[3:7])\n\n if \"JPE\" not in serial and \"JAS\" not in serial:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JPE\" in serial and number >= 2131:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JPE\" in serial and number >= 2134:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n if model == \"DCS-7280SR3K-48YC8\" and \"JAS\" in serial and number >= 2041:\n self.result.is_skipped(\"Device not exposed\")\n return\n\n # Because each of the if checks above will return if taken, we only run the long\n # check if we get this far\n for entry in command_output[\"details\"][\"components\"]:\n if entry[\"name\"] == \"FixedSystemvrm1\":\n if int(entry[\"version\"]) < 7:\n self.result.is_failure(\"Device is exposed to FN72\")\n else:\n self.result.is_success(\"FN72 is mitigated\")\n return\n # We should never hit this point\n self.result.is_error(\"Error in running test - FixedSystemvrm1 not found\")\n return\n
"},{"location":"api/tests.hardware/","title":"Hardware","text":""},{"location":"api/tests.hardware/#anta-catalog-for-hardware-tests","title":"ANTA catalog for hardware tests","text":"Test functions related to the hardware or environement
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyAdverseDrops","title":"VerifyAdverseDrops
","text":" Bases: AntaTest
Verifies there is no adverse drops on DCS7280E and DCS7500E.
Source code in anta/tests/hardware.py
class VerifyAdverseDrops(AntaTest):\n\"\"\"\n Verifies there is no adverse drops on DCS7280E and DCS7500E.\n \"\"\"\n\n name = \"VerifyAdverseDrops\"\n description = \"Verifies there is no adverse drops on DCS7280E and DCS7500E\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show hardware counter drop\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyAdverseDrops validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_adverse_drop = command_output[\"totalAdverseDrops\"] if \"totalAdverseDrops\" in command_output.keys() else \"\"\n if total_adverse_drop == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device TotalAdverseDrops counter is {total_adverse_drop}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyAdverseDrops.test","title":"test()
","text":"Run VerifyAdverseDrops validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyAdverseDrops validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_adverse_drop = command_output[\"totalAdverseDrops\"] if \"totalAdverseDrops\" in command_output.keys() else \"\"\n if total_adverse_drop == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device TotalAdverseDrops counter is {total_adverse_drop}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentCooling","title":"VerifyEnvironmentCooling
","text":" Bases: AntaTest
Verifies the fans status is in the accepted states list.
Default accepted states list is [\u2018ok\u2019]
Source code in anta/tests/hardware.py
class VerifyEnvironmentCooling(AntaTest):\n\"\"\"\n Verifies the fans status is in the accepted states list.\n\n Default accepted states list is ['ok']\n \"\"\"\n\n name = \"VerifyEnvironmentCooling\"\n description = \"Verifies the fans status is OK for fans\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment cooling\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentCooling validation\n\n Args:\n accepted_states: Accepted states list for fan status\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n self.result.is_success()\n # First go through power supplies fans\n for power_supply in command_output.get(\"powerSupplySlots\", []):\n for fan in power_supply.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on PowerSupply {power_supply['label']} has state '{state}'.\")\n # Then go through Fan Trays\n for fan_tray in command_output.get(\"fanTraySlots\", []):\n for fan in fan_tray.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on Fan Tray {fan_tray['label']} has state '{state}'.\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentCooling.test","title":"test(accepted_states=None)
","text":"Run VerifyEnvironmentCooling validation
Parameters:
Name Type Description Default accepted_states
Optional[List[str]]
Accepted states list for fan status
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentCooling validation\n\n Args:\n accepted_states: Accepted states list for fan status\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n self.result.is_success()\n # First go through power supplies fans\n for power_supply in command_output.get(\"powerSupplySlots\", []):\n for fan in power_supply.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on PowerSupply {power_supply['label']} has state '{state}'.\")\n # Then go through Fan Trays\n for fan_tray in command_output.get(\"fanTraySlots\", []):\n for fan in fan_tray.get(\"fans\", []):\n if (state := fan[\"status\"]) not in accepted_states:\n if self.result.result == \"success\":\n self.result.is_failure(f\"Some fans state are not in the accepted list: {accepted_states}.\")\n self.result.is_failure(f\"Fan {fan['label']} on Fan Tray {fan_tray['label']} has state '{state}'.\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentPower","title":"VerifyEnvironmentPower
","text":" Bases: AntaTest
Verifies the power supplies status is in the accepted states list
The default accepted states list is [\u2018ok\u2019]
Source code in anta/tests/hardware.py
class VerifyEnvironmentPower(AntaTest):\n\"\"\"\n Verifies the power supplies status is in the accepted states list\n\n The default accepted states list is ['ok']\n \"\"\"\n\n name = \"VerifyEnvironmentPower\"\n description = \"Verifies the power supplies status is OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment power\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentPower validation\n\n Args:\n accepted_states: Accepted states list for power supplies\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n power_supplies = command_output[\"powerSupplies\"] if \"powerSupplies\" in command_output.keys() else \"{}\"\n wrong_power_supplies = {\n powersupply: {\"state\": value[\"state\"]} for powersupply, value in dict(power_supplies).items() if value[\"state\"] not in accepted_states\n }\n if not wrong_power_supplies:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following power supplies states are not in the accepted_states list {accepted_states}\")\n self.result.messages.append(str(wrong_power_supplies))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentPower.test","title":"test(accepted_states=None)
","text":"Run VerifyEnvironmentPower validation
Parameters:
Name Type Description Default accepted_states
Optional[List[str]]
Accepted states list for power supplies
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, accepted_states: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEnvironmentPower validation\n\n Args:\n accepted_states: Accepted states list for power supplies\n \"\"\"\n if accepted_states is None:\n accepted_states = [\"ok\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n power_supplies = command_output[\"powerSupplies\"] if \"powerSupplies\" in command_output.keys() else \"{}\"\n wrong_power_supplies = {\n powersupply: {\"state\": value[\"state\"]} for powersupply, value in dict(power_supplies).items() if value[\"state\"] not in accepted_states\n }\n if not wrong_power_supplies:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following power supplies states are not in the accepted_states list {accepted_states}\")\n self.result.messages.append(str(wrong_power_supplies))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentSystemCooling","title":"VerifyEnvironmentSystemCooling
","text":" Bases: AntaTest
Verifies the System Cooling is ok.
Source code in anta/tests/hardware.py
class VerifyEnvironmentSystemCooling(AntaTest):\n\"\"\"\n Verifies the System Cooling is ok.\n \"\"\"\n\n name = \"VerifyEnvironmentSystemCooling\"\n description = \"Verifies the fans status is OK for fans\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment cooling\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyEnvironmentCooling validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sys_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n\n self.result.is_success()\n if sys_status != \"coolingOk\":\n self.result.is_failure(f\"Device System cooling is not OK: {sys_status}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyEnvironmentSystemCooling.test","title":"test()
","text":"Run VerifyEnvironmentCooling validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyEnvironmentCooling validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sys_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n\n self.result.is_success()\n if sys_status != \"coolingOk\":\n self.result.is_failure(f\"Device System cooling is not OK: {sys_status}\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTemperature","title":"VerifyTemperature
","text":" Bases: AntaTest
Verifies device temparture is currently OK (temperatureOK).
Source code in anta/tests/hardware.py
class VerifyTemperature(AntaTest):\n\"\"\"\n Verifies device temparture is currently OK (temperatureOK).\n \"\"\"\n\n name = \"VerifyTemperature\"\n description = \"Verifies device temparture is currently OK (temperatureOK)\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTemperature.test","title":"test()
","text":"Run VerifyTemperature validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n temperature_status = command_output[\"systemStatus\"] if \"systemStatus\" in command_output.keys() else \"\"\n if temperature_status == \"temperatureOk\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device temperature is not OK, systemStatus: {temperature_status }\")\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversManufacturers","title":"VerifyTransceiversManufacturers
","text":" Bases: AntaTest
Verifies Manufacturers of all Transceivers.
Source code in anta/tests/hardware.py
class VerifyTransceiversManufacturers(AntaTest):\n\"\"\"\n Verifies Manufacturers of all Transceivers.\n \"\"\"\n\n name = \"VerifyTransceiversManufacturers\"\n description = \"\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show inventory\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, manufacturers: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTransceiversManufacturers validation\n\n Args:\n manufacturers: List of allowed transceivers manufacturers.\n \"\"\"\n if not manufacturers:\n self.result.is_skipped(f\"{self.__class__.name} was not run as no manufacturers were given\")\n else:\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n wrong_manufacturers = {interface: value[\"mfgName\"] for interface, value in command_output[\"xcvrSlots\"].items() if value[\"mfgName\"] not in manufacturers}\n if not wrong_manufacturers:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following interfaces have transceivers from unauthorized manufacturers\")\n self.result.messages.append(str(wrong_manufacturers))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversManufacturers.test","title":"test(manufacturers=None)
","text":"Run VerifyTransceiversManufacturers validation
Parameters:
Name Type Description Default manufacturers
Optional[List[str]]
List of allowed transceivers manufacturers.
None
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, manufacturers: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTransceiversManufacturers validation\n\n Args:\n manufacturers: List of allowed transceivers manufacturers.\n \"\"\"\n if not manufacturers:\n self.result.is_skipped(f\"{self.__class__.name} was not run as no manufacturers were given\")\n else:\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n wrong_manufacturers = {interface: value[\"mfgName\"] for interface, value in command_output[\"xcvrSlots\"].items() if value[\"mfgName\"] not in manufacturers}\n if not wrong_manufacturers:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following interfaces have transceivers from unauthorized manufacturers\")\n self.result.messages.append(str(wrong_manufacturers))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversTemperature","title":"VerifyTransceiversTemperature
","text":" Bases: AntaTest
Verifies Transceivers temperature is currently OK.
Source code in anta/tests/hardware.py
class VerifyTransceiversTemperature(AntaTest):\n\"\"\"\n Verifies Transceivers temperature is currently OK.\n \"\"\"\n\n name = \"VerifyTransceiversTemperature\"\n description = \"Verifies Transceivers temperature is currently OK\"\n categories = [\"hardware\"]\n commands = [AntaTestCommand(command=\"show system environment temperature transceiver\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyTransceiversTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sensors = command_output[\"tempSensors\"] if \"tempSensors\" in command_output.keys() else \"\"\n wrong_sensors = {\n sensor[\"name\"]: {\n \"hwStatus\": sensor[\"hwStatus\"],\n \"alertCount\": sensor[\"alertCount\"],\n }\n for sensor in sensors\n if sensor[\"hwStatus\"] != \"ok\" or sensor[\"alertCount\"] != 0\n }\n if not wrong_sensors:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following sensors do not have the correct temperature or had alarms in the past:\")\n self.result.messages.append(str(wrong_sensors))\n
"},{"location":"api/tests.hardware/#anta.tests.hardware.VerifyTransceiversTemperature.test","title":"test()
","text":"Run VerifyTransceiversTemperature validation
Source code in anta/tests/hardware.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyTransceiversTemperature validation\"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n sensors = command_output[\"tempSensors\"] if \"tempSensors\" in command_output.keys() else \"\"\n wrong_sensors = {\n sensor[\"name\"]: {\n \"hwStatus\": sensor[\"hwStatus\"],\n \"alertCount\": sensor[\"alertCount\"],\n }\n for sensor in sensors\n if sensor[\"hwStatus\"] != \"ok\" or sensor[\"alertCount\"] != 0\n }\n if not wrong_sensors:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following sensors do not have the correct temperature or had alarms in the past:\")\n self.result.messages.append(str(wrong_sensors))\n
"},{"location":"api/tests.interfaces/","title":"Interfaces","text":""},{"location":"api/tests.interfaces/#anta-catalog-for-interfaces-tests","title":"ANTA catalog for interfaces tests","text":"Test functions related to the device interfaces
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIPProxyARP","title":"VerifyIPProxyARP
","text":" Bases: AntaTest
Verifies if Proxy-ARP is enabled for the provided list of interface(s).
Expected Results - success: The test will pass if Proxy-ARP is enabled on the specified interface(s).
- failure: The test will fail if Proxy-ARP is disabled on the specified interface(s).
- error: The test will give an error if a list of interface(s) is not provided as template_params.
Source code in anta/tests/interfaces.py
class VerifyIPProxyARP(AntaTest):\n\"\"\"\n Verifies if Proxy-ARP is enabled for the provided list of interface(s).\n\n Expected Results:\n * success: The test will pass if Proxy-ARP is enabled on the specified interface(s).\n * failure: The test will fail if Proxy-ARP is disabled on the specified interface(s).\n * error: The test will give an error if a list of interface(s) is not provided as template_params.\n\n \"\"\"\n\n name = \"VerifyIPProxyARP\"\n description = \"Verifies if Proxy-ARP is enabled for the provided list of interface(s).\"\n categories = [\"interfaces\"]\n template = AntaTestTemplate(template=\"show ip interface {intf}\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyIPProxyARP validation.\n \"\"\"\n\n disabled_intf = []\n\n for index, command in enumerate(self.instance_commands):\n intf = cast(Dict[str, str], command.template_params).get(\"intf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not command_output[\"interfaces\"][intf][\"proxyArp\"]:\n disabled_intf.append(intf)\n\n if disabled_intf:\n self.result.is_failure(f\"The following interface(s) have Proxy-ARP disabled: {disabled_intf}\")\n\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIPProxyARP.test","title":"test()
","text":"Run VerifyIPProxyARP validation.
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyIPProxyARP validation.\n \"\"\"\n\n disabled_intf = []\n\n for index, command in enumerate(self.instance_commands):\n intf = cast(Dict[str, str], command.template_params).get(\"intf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not command_output[\"interfaces\"][intf][\"proxyArp\"]:\n disabled_intf.append(intf)\n\n if disabled_intf:\n self.result.is_failure(f\"The following interface(s) have Proxy-ARP disabled: {disabled_intf}\")\n\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIllegalLACP","title":"VerifyIllegalLACP
","text":" Bases: AntaTest
Verifies there is no illegal LACP packets received.
Source code in anta/tests/interfaces.py
class VerifyIllegalLACP(AntaTest):\n\"\"\"\n Verifies there is no illegal LACP packets received.\n \"\"\"\n\n name = \"VerifyIllegalLACP\"\n description = \"Verifies there is no illegal LACP packets received.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show lacp counters all-ports\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyIllegalLACP validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_illegal_lacp: List[Dict[str, Dict[str, int]]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n po_with_illegal_lacp.extend(\n {portchannel: interface} for interface, interface_dict in portchannel_dict[\"interfaces\"].items() if interface_dict[\"illegalRxCount\"] != 0\n )\n\n if not po_with_illegal_lacp:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following port-channels have recieved illegal lacp packets on the \" f\"following ports: {po_with_illegal_lacp}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyIllegalLACP.test","title":"test()
","text":"Run VerifyIllegalLACP validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyIllegalLACP validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_illegal_lacp: List[Dict[str, Dict[str, int]]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n po_with_illegal_lacp.extend(\n {portchannel: interface} for interface, interface_dict in portchannel_dict[\"interfaces\"].items() if interface_dict[\"illegalRxCount\"] != 0\n )\n\n if not po_with_illegal_lacp:\n self.result.is_success()\n else:\n self.result.is_failure(\"The following port-channels have recieved illegal lacp packets on the \" f\"following ports: {po_with_illegal_lacp}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceDiscards","title":"VerifyInterfaceDiscards
","text":" Bases: AntaTest
Verifies interfaces packet discard counters are equal to zero.
Source code in anta/tests/interfaces.py
class VerifyInterfaceDiscards(AntaTest):\n\"\"\"\n Verifies interfaces packet discard counters are equal to zero.\n \"\"\"\n\n name = \"VerifyInterfaceDiscards\"\n description = \"Verifies interfaces packet discard counters are equal to zero.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces counters discards\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceDiscards validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n\n for interface, outer_v in command_output[\"interfaces\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 discard counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceDiscards.test","title":"test()
","text":"Run VerifyInterfaceDiscards validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceDiscards validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n\n for interface, outer_v in command_output[\"interfaces\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 discard counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrDisabled","title":"VerifyInterfaceErrDisabled
","text":" Bases: AntaTest
Verifies there is no interface in error disable state.
Source code in anta/tests/interfaces.py
class VerifyInterfaceErrDisabled(AntaTest):\n\"\"\"\n Verifies there is no interface in error disable state.\n \"\"\"\n\n name = \"VerifyInterfaceErrDisabled\"\n description = \"Verifies there is no interface in error disable state.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces status\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceErrDisabled validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n errdisabled_interfaces = [interface for interface, value in command_output[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n\n if errdisabled_interfaces:\n self.result.is_failure(f\"The following interfaces are in error disabled state: {errdisabled_interfaces}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrDisabled.test","title":"test()
","text":"Run VerifyInterfaceErrDisabled validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceErrDisabled validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n errdisabled_interfaces = [interface for interface, value in command_output[\"interfaceStatuses\"].items() if value[\"linkStatus\"] == \"errdisabled\"]\n\n if errdisabled_interfaces:\n self.result.is_failure(f\"The following interfaces are in error disabled state: {errdisabled_interfaces}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrors","title":"VerifyInterfaceErrors
","text":" Bases: AntaTest
Verifies interfaces error counters are equal to zero.
Source code in anta/tests/interfaces.py
class VerifyInterfaceErrors(AntaTest):\n\"\"\"\n Verifies interfaces error counters are equal to zero.\n \"\"\"\n\n name = \"VerifyInterfaceErrors\"\n description = \"Verifies interfaces error counters are equal to zero.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces counters errors\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n for interface, outer_v in command_output[\"interfaceErrorCounters\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 error counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceErrors.test","title":"test()
","text":"Run VerifyInterfaceUtilization validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n wrong_interfaces: List[Dict[str, Dict[str, int]]] = []\n for interface, outer_v in command_output[\"interfaceErrorCounters\"].items():\n wrong_interfaces.extend({interface: outer_v} for counter, value in outer_v.items() if value > 0)\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have non 0 error counter(s): {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceUtilization","title":"VerifyInterfaceUtilization
","text":" Bases: AntaTest
Verifies interfaces utilization is below 75%.
Source code in anta/tests/interfaces.py
class VerifyInterfaceUtilization(AntaTest):\n\"\"\"\n Verifies interfaces utilization is below 75%.\n \"\"\"\n\n name = \"VerifyInterfaceUtilization\"\n description = \"Verifies interfaces utilization is below 75%.\"\n categories = [\"interfaces\"]\n # TODO - move from text to json if possible\n commands = [AntaTestCommand(command=\"show interfaces counters rates\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n wrong_interfaces = {}\n for line in command_output.split(\"\\n\")[1:]:\n if len(line) > 0:\n if line.split()[-5] == \"-\" or line.split()[-2] == \"-\":\n pass\n elif float(line.split()[-5].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-5]\n elif float(line.split()[-2].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-2]\n\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have a usage > 75%: {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfaceUtilization.test","title":"test()
","text":"Run VerifyInterfaceUtilization validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyInterfaceUtilization validation\"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n wrong_interfaces = {}\n for line in command_output.split(\"\\n\")[1:]:\n if len(line) > 0:\n if line.split()[-5] == \"-\" or line.split()[-2] == \"-\":\n pass\n elif float(line.split()[-5].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-5]\n elif float(line.split()[-2].replace(\"%\", \"\")) > 75.0:\n wrong_interfaces[line.split()[0]] = line.split()[-2]\n\n if not wrong_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have a usage > 75%: {wrong_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfacesStatus","title":"VerifyInterfacesStatus
","text":" Bases: AntaTest
Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.
Source code in anta/tests/interfaces.py
class VerifyInterfacesStatus(AntaTest):\n\"\"\"\n Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.\n \"\"\"\n\n name = \"VerifyInterfacesStatus\"\n description = \"Verifies the number of Ethernet interfaces up/up on the device is higher or equal than a value.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces description\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyInterfacesStatus validation\n\n Args:\n minimum: Expected minimum number of Ethernet interfaces up/up.\n \"\"\"\n\n if minimum is None or minimum < 0:\n self.result.is_skipped(f\"VerifyInterfacesStatus was not run as an invalid minimum value was given {minimum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n count_up_up = 0\n other_ethernet_interfaces = []\n\n for interface in command_output[\"interfaceDescriptions\"]:\n interface_dict = command_output[\"interfaceDescriptions\"][interface]\n if \"Ethernet\" in interface:\n if re.match(r\"connected|up\", interface_dict[\"lineProtocolStatus\"]) and re.match(r\"connected|up\", interface_dict[\"interfaceStatus\"]):\n count_up_up += 1\n else:\n other_ethernet_interfaces.append(interface)\n\n if count_up_up >= minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Only {count_up_up}, less than {minimum} Ethernet interfaces are UP/UP\")\n self.result.messages.append(f\"The following Ethernet interfaces are not UP/UP: {other_ethernet_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyInterfacesStatus.test","title":"test(minimum=None)
","text":"Run VerifyInterfacesStatus validation
Parameters:
Name Type Description Default minimum
Optional[int]
Expected minimum number of Ethernet interfaces up/up.
None
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyInterfacesStatus validation\n\n Args:\n minimum: Expected minimum number of Ethernet interfaces up/up.\n \"\"\"\n\n if minimum is None or minimum < 0:\n self.result.is_skipped(f\"VerifyInterfacesStatus was not run as an invalid minimum value was given {minimum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n count_up_up = 0\n other_ethernet_interfaces = []\n\n for interface in command_output[\"interfaceDescriptions\"]:\n interface_dict = command_output[\"interfaceDescriptions\"][interface]\n if \"Ethernet\" in interface:\n if re.match(r\"connected|up\", interface_dict[\"lineProtocolStatus\"]) and re.match(r\"connected|up\", interface_dict[\"interfaceStatus\"]):\n count_up_up += 1\n else:\n other_ethernet_interfaces.append(interface)\n\n if count_up_up >= minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Only {count_up_up}, less than {minimum} Ethernet interfaces are UP/UP\")\n self.result.messages.append(f\"The following Ethernet interfaces are not UP/UP: {other_ethernet_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyL3MTU","title":"VerifyL3MTU
","text":" Bases: AntaTest
Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.
Expected Results - success: The test will pass if all layer 3 interfaces have the proper MTU configured.
- failure: The test will fail if one or many layer 3 interfaces have the wrong MTU configured.
- skipped: The test will be skipped if the MTU value is not provided.
Limitations - Only Ethernet, Port-Channel, Vlan interfaces are supported.
- Other interface types, like Management, Loopback, Vxlan, Tunnel are currently not supported.
https://www.arista.com/en/support/toi/eos-4-23-1f/14388-global-knob-to-set-mtu-for-all-layer-3-interfaces
Source code in anta/tests/interfaces.py
class VerifyL3MTU(AntaTest):\n\"\"\"\n Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.\n\n Expected Results:\n * success: The test will pass if all layer 3 interfaces have the proper MTU configured.\n * failure: The test will fail if one or many layer 3 interfaces have the wrong MTU configured.\n * skipped: The test will be skipped if the MTU value is not provided.\n\n Limitations:\n * Only Ethernet, Port-Channel, Vlan interfaces are supported.\n * Other interface types, like Management, Loopback, Vxlan, Tunnel are currently not supported.\n\n https://www.arista.com/en/support/toi/eos-4-23-1f/14388-global-knob-to-set-mtu-for-all-layer-3-interfaces\n\n \"\"\"\n\n name = \"VerifyL3MTU\"\n description = \"Verifies the global layer 3 Maximum Transfer Unit (MTU) for all layer 3 interfaces.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show interfaces\")]\n\n NOT_SUPPORTED_INTERFACES: List[str] = [\"Management\", \"Loopback\", \"Vxlan\", \"Tunnel\"]\n\n @AntaTest.anta_test\n def test(self, mtu: int = 1500) -> None:\n\"\"\"\n Run VerifyL3MTU validation\n\n Args:\n mtu: Layer 3 MTU to verify. Defaults to 1500.\n\n \"\"\"\n\n if not mtu:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mtu was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_l3mtu_intf = []\n\n for interface, values in command_output[\"interfaces\"].items():\n if re.sub(r\"\\d+$\", \"\", interface) not in self.NOT_SUPPORTED_INTERFACES:\n if values[\"forwardingModel\"] == \"routed\" and values[\"mtu\"] != mtu:\n wrong_l3mtu_intf.append(interface)\n\n if not wrong_l3mtu_intf:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"The following interface(s) have the wrong MTU configured: {wrong_l3mtu_intf}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyL3MTU.test","title":"test(mtu=1500)
","text":"Run VerifyL3MTU validation
Parameters:
Name Type Description Default mtu
int
Layer 3 MTU to verify. Defaults to 1500.
1500
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, mtu: int = 1500) -> None:\n\"\"\"\n Run VerifyL3MTU validation\n\n Args:\n mtu: Layer 3 MTU to verify. Defaults to 1500.\n\n \"\"\"\n\n if not mtu:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mtu was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n wrong_l3mtu_intf = []\n\n for interface, values in command_output[\"interfaces\"].items():\n if re.sub(r\"\\d+$\", \"\", interface) not in self.NOT_SUPPORTED_INTERFACES:\n if values[\"forwardingModel\"] == \"routed\" and values[\"mtu\"] != mtu:\n wrong_l3mtu_intf.append(interface)\n\n if not wrong_l3mtu_intf:\n self.result.is_success()\n\n else:\n self.result.is_failure(f\"The following interface(s) have the wrong MTU configured: {wrong_l3mtu_intf}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyLoopbackCount","title":"VerifyLoopbackCount
","text":" Bases: AntaTest
Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.
Source code in anta/tests/interfaces.py
class VerifyLoopbackCount(AntaTest):\n\"\"\"\n Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.\n \"\"\"\n\n name = \"VerifyLoopbackCount\"\n description = \"Verifies the number of loopback interfaces on the device is the one we expect and if none of the loopback is down.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show ip interface brief\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyLoopbackCount validation\n\n Args:\n number: Number of loopback interfaces expected to be present.\n \"\"\"\n\n if number is None:\n self.result.is_skipped(\"VerifyLoopbackCount was not run as no number value was given.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n loopback_count = 0\n down_loopback_interfaces = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Loopback\" in interface:\n loopback_count += 1\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_loopback_interfaces.append(interface)\n\n if loopback_count == number and len(down_loopback_interfaces) == 0:\n self.result.is_success()\n else:\n self.result.is_failure()\n if loopback_count != number:\n self.result.is_failure(f\"Found {loopback_count} Loopbacks when expecting {number}\")\n elif len(down_loopback_interfaces) != 0:\n self.result.is_failure(f\"The following Loopbacks are not up: {down_loopback_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyLoopbackCount.test","title":"test(number=None)
","text":"Run VerifyLoopbackCount validation
Parameters:
Name Type Description Default number
Optional[int]
Number of loopback interfaces expected to be present.
None
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyLoopbackCount validation\n\n Args:\n number: Number of loopback interfaces expected to be present.\n \"\"\"\n\n if number is None:\n self.result.is_skipped(\"VerifyLoopbackCount was not run as no number value was given.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n loopback_count = 0\n down_loopback_interfaces = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Loopback\" in interface:\n loopback_count += 1\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_loopback_interfaces.append(interface)\n\n if loopback_count == number and len(down_loopback_interfaces) == 0:\n self.result.is_success()\n else:\n self.result.is_failure()\n if loopback_count != number:\n self.result.is_failure(f\"Found {loopback_count} Loopbacks when expecting {number}\")\n elif len(down_loopback_interfaces) != 0:\n self.result.is_failure(f\"The following Loopbacks are not up: {down_loopback_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyPortChannels","title":"VerifyPortChannels
","text":" Bases: AntaTest
Verifies there is no inactive port in port channels.
Source code in anta/tests/interfaces.py
class VerifyPortChannels(AntaTest):\n\"\"\"\n Verifies there is no inactive port in port channels.\n \"\"\"\n\n name = \"VerifyPortChannels\"\n description = \"Verifies there is no inactive port in port channels.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show port-channel\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyPortChannels validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_invactive_ports: List[Dict[str, str]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n if len(portchannel_dict[\"inactivePorts\"]) != 0:\n po_with_invactive_ports.extend({portchannel: portchannel_dict[\"inactivePorts\"]})\n\n if not po_with_invactive_ports:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following port-channels have inactive port(s): {po_with_invactive_ports}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyPortChannels.test","title":"test()
","text":"Run VerifyPortChannels validation
Source code in anta/tests/interfaces.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyPortChannels validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n po_with_invactive_ports: List[Dict[str, str]] = []\n for portchannel, portchannel_dict in command_output[\"portChannels\"].items():\n if len(portchannel_dict[\"inactivePorts\"]) != 0:\n po_with_invactive_ports.extend({portchannel: portchannel_dict[\"inactivePorts\"]})\n\n if not po_with_invactive_ports:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following port-channels have inactive port(s): {po_with_invactive_ports}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifySVI","title":"VerifySVI
","text":" Bases: AntaTest
Verifies there is no interface vlan down.
Source code in anta/tests/interfaces.py
class VerifySVI(AntaTest):\n\"\"\"\n Verifies there is no interface vlan down.\n \"\"\"\n\n name = \"VerifySVI\"\n description = \"Verifies there is no interface vlan down.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show ip interface brief\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifySVI validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n down_svis = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Vlan\" in interface:\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_svis.append(interface)\n\n if len(down_svis) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following SVIs are not up: {down_svis}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifySVI.test","title":"test()
","text":"Run VerifySVI validation
Source code in anta/tests/interfaces.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifySVI validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n down_svis = []\n\n for interface in command_output[\"interfaces\"]:\n interface_dict = command_output[\"interfaces\"][interface]\n if \"Vlan\" in interface:\n if not (interface_dict[\"lineProtocolStatus\"] == \"up\" and interface_dict[\"interfaceStatus\"] == \"connected\"):\n down_svis.append(interface)\n\n if len(down_svis) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following SVIs are not up: {down_svis}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyStormControlDrops","title":"VerifyStormControlDrops
","text":" Bases: AntaTest
Verifies the device did not drop packets due its to storm-control configuration.
Source code in anta/tests/interfaces.py
class VerifyStormControlDrops(AntaTest):\n\"\"\"\n Verifies the device did not drop packets due its to storm-control configuration.\n \"\"\"\n\n name = \"VerifyStormControlDrops\"\n description = \"Verifies the device did not drop packets due its to storm-control configuration.\"\n categories = [\"interfaces\"]\n commands = [AntaTestCommand(command=\"show storm-control\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyStormControlDrops validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n storm_controlled_interfaces: Dict[str, Dict[str, Any]] = {}\n for interface, interface_dict in command_output[\"interfaces\"].items():\n for traffic_type, traffic_type_dict in interface_dict[\"trafficTypes\"].items():\n if \"drop\" in traffic_type_dict and traffic_type_dict[\"drop\"] != 0:\n storm_controlled_interface_dict = storm_controlled_interfaces.setdefault(interface, {})\n storm_controlled_interface_dict.update({traffic_type: traffic_type_dict[\"drop\"]})\n\n if not storm_controlled_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have none 0 storm-control drop counters {storm_controlled_interfaces}\")\n
"},{"location":"api/tests.interfaces/#anta.tests.interfaces.VerifyStormControlDrops.test","title":"test()
","text":"Run VerifyStormControlDrops validation
Source code in anta/tests/interfaces.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyStormControlDrops validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n storm_controlled_interfaces: Dict[str, Dict[str, Any]] = {}\n for interface, interface_dict in command_output[\"interfaces\"].items():\n for traffic_type, traffic_type_dict in interface_dict[\"trafficTypes\"].items():\n if \"drop\" in traffic_type_dict and traffic_type_dict[\"drop\"] != 0:\n storm_controlled_interface_dict = storm_controlled_interfaces.setdefault(interface, {})\n storm_controlled_interface_dict.update({traffic_type: traffic_type_dict[\"drop\"]})\n\n if not storm_controlled_interfaces:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following interfaces have none 0 storm-control drop counters {storm_controlled_interfaces}\")\n
"},{"location":"api/tests.logging/","title":"Logging","text":""},{"location":"api/tests.logging/#anta-catalog-for-logging-tests","title":"ANTA catalog for logging tests","text":"Test functions related to the EOS various logging settings
NOTE: \u2018show logging\u2019 does not support json output yet
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingAccounting","title":"VerifyLoggingAccounting
","text":" Bases: AntaTest
Verifies if AAA accounting logs are generated.
Expected Results - success: The test will pass if AAA accounting logs are generated.
- failure: The test will fail if AAA accounting logs are NOT generated.
Source code in anta/tests/logging.py
class VerifyLoggingAccounting(AntaTest):\n\"\"\"\n Verifies if AAA accounting logs are generated.\n\n Expected Results:\n * success: The test will pass if AAA accounting logs are generated.\n * failure: The test will fail if AAA accounting logs are NOT generated.\n \"\"\"\n\n name = \"VerifyLoggingAccounting\"\n description = \"Verifies if AAA accounting logs are generated.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show aaa accounting logs | tail\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingAccountingvalidation.\n \"\"\"\n pattern = r\"cmd=show aaa accounting logs\"\n output = cast(str, self.instance_commands[0].output)\n\n if re.search(pattern, output):\n self.result.is_success()\n else:\n self.result.is_failure(\"AAA accounting logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingAccounting.test","title":"test()
","text":"Run VerifyLoggingAccountingvalidation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingAccountingvalidation.\n \"\"\"\n pattern = r\"cmd=show aaa accounting logs\"\n output = cast(str, self.instance_commands[0].output)\n\n if re.search(pattern, output):\n self.result.is_success()\n else:\n self.result.is_failure(\"AAA accounting logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHostname","title":"VerifyLoggingHostname
","text":" Bases: AntaTest
Verifies if logs are generated with the device FQDN.
Expected Results - success: The test will pass if logs are generated with the device FQDN.
- failure: The test will fail if logs are NOT generated with the device FQDN.
Source code in anta/tests/logging.py
class VerifyLoggingHostname(AntaTest):\n\"\"\"\n Verifies if logs are generated with the device FQDN.\n\n Expected Results:\n * success: The test will pass if logs are generated with the device FQDN.\n * failure: The test will fail if logs are NOT generated with the device FQDN.\n \"\"\"\n\n name = \"VerifyLoggingHostname\"\n description = \"Verifies if logs are generated with the device FQDN.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"show hostname\"),\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingHostname validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingHostname validation.\n \"\"\"\n output_hostname = cast(Dict[str, Any], self.instance_commands[0].output)\n output_logging = cast(str, self.instance_commands[2].output)\n fqdn = output_hostname[\"fqdn\"]\n lines = output_logging.strip().split(\"\\n\")[::-1]\n\n log_pattern = r\"ANTA VerifyLoggingHostname validation\"\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if fqdn in last_line_with_pattern:\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the device FQDN\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHostname.test","title":"test()
","text":"Run VerifyLoggingHostname validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingHostname validation.\n \"\"\"\n output_hostname = cast(Dict[str, Any], self.instance_commands[0].output)\n output_logging = cast(str, self.instance_commands[2].output)\n fqdn = output_hostname[\"fqdn\"]\n lines = output_logging.strip().split(\"\\n\")[::-1]\n\n log_pattern = r\"ANTA VerifyLoggingHostname validation\"\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if fqdn in last_line_with_pattern:\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the device FQDN\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHosts","title":"VerifyLoggingHosts
","text":" Bases: AntaTest
Verifies logging hosts (syslog servers) for a specified VRF.
Expected Results - success: The test will pass if the provided syslog servers are configured in the specified VRF.
- failure: The test will fail if the provided syslog servers are NOT configured in the specified VRF.
- skipped: The test will be skipped if syslog servers or VRF are not provided.
Source code in anta/tests/logging.py
class VerifyLoggingHosts(AntaTest):\n\"\"\"\n Verifies logging hosts (syslog servers) for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided syslog servers are configured in the specified VRF.\n * failure: The test will fail if the provided syslog servers are NOT configured in the specified VRF.\n * skipped: The test will be skipped if syslog servers or VRF are not provided.\n \"\"\"\n\n name = \"VerifyLoggingHosts\"\n description = \"Verifies logging hosts (syslog servers) for a specified VRF.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show logging\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self, hosts: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingHosts validation.\n\n Args:\n hosts: List of hosts (syslog servers) IP addresses.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not hosts or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because hosts or vrf were not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n not_configured = []\n\n for host in hosts:\n pattern = rf\"Logging to '{host}'.*VRF {vrf}\"\n if not re.search(pattern, _get_logging_states(output)):\n not_configured.append(host)\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Syslog servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingHosts.test","title":"test(hosts=None, vrf='default')
","text":"Run VerifyLoggingHosts validation.
Parameters:
Name Type Description Default hosts
Optional[List[str]]
List of hosts (syslog servers) IP addresses.
None
vrf
str
The name of the VRF to transport log messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self, hosts: Optional[List[str]] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingHosts validation.\n\n Args:\n hosts: List of hosts (syslog servers) IP addresses.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not hosts or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because hosts or vrf were not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n not_configured = []\n\n for host in hosts:\n pattern = rf\"Logging to '{host}'.*VRF {vrf}\"\n if not re.search(pattern, _get_logging_states(output)):\n not_configured.append(host)\n\n if not not_configured:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Syslog servers {not_configured} are not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingLogsGeneration","title":"VerifyLoggingLogsGeneration
","text":" Bases: AntaTest
Verifies if logs are generated.
Expected Results - success: The test will pass if logs are generated.
- failure: The test will fail if logs are NOT generated.
Source code in anta/tests/logging.py
class VerifyLoggingLogsGeneration(AntaTest):\n\"\"\"\n Verifies if logs are generated.\n\n Expected Results:\n * success: The test will pass if logs are generated.\n * failure: The test will fail if logs are NOT generated.\n \"\"\"\n\n name = \"VerifyLoggingLogsGeneration\"\n description = \"Verifies if logs are generated.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingLogsGeneration validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingLogs validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingLogsGeneration validation\"\n\n output = cast(str, self.instance_commands[1].output)\n lines = output.strip().split(\"\\n\")[::-1]\n\n for line in lines:\n if re.search(log_pattern, line):\n self.result.is_success()\n return\n\n self.result.is_failure(\"Logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingLogsGeneration.test","title":"test()
","text":"Run VerifyLoggingLogs validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingLogs validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingLogsGeneration validation\"\n\n output = cast(str, self.instance_commands[1].output)\n lines = output.strip().split(\"\\n\")[::-1]\n\n for line in lines:\n if re.search(log_pattern, line):\n self.result.is_success()\n return\n\n self.result.is_failure(\"Logs are not generated\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingPersistent","title":"VerifyLoggingPersistent
","text":" Bases: AntaTest
Verifies if logging persistent is enabled and logs are saved in flash.
Expected Results - success: The test will pass if logging persistent is enabled and logs are in flash.
- failure: The test will fail if logging persistent is disabled or no logs are saved in flash.
Source code in anta/tests/logging.py
class VerifyLoggingPersistent(AntaTest):\n\"\"\"\n Verifies if logging persistent is enabled and logs are saved in flash.\n\n Expected Results:\n * success: The test will pass if logging persistent is enabled and logs are in flash.\n * failure: The test will fail if logging persistent is disabled or no logs are saved in flash.\n \"\"\"\n\n name = \"VerifyLoggingPersistent\"\n description = \"Verifies if logging persistent is enabled and logs are saved in flash.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"show logging\", ofmt=\"text\"),\n AntaTestCommand(command=\"dir flash:/persist/messages\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingPersistent validation.\n \"\"\"\n self.result.is_success()\n\n log_output = cast(str, self.instance_commands[0].output)\n dir_flash_output = cast(str, self.instance_commands[1].output)\n\n if \"Persistent logging: disabled\" in _get_logging_states(log_output):\n self.result.is_failure(\"Persistent logging is disabled\")\n return\n\n pattern = r\"-rw-\\s+(\\d+)\"\n persist_logs = re.search(pattern, dir_flash_output)\n\n if not persist_logs or int(persist_logs.group(1)) == 0:\n self.result.is_failure(\"No persistent logs are saved in flash\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingPersistent.test","title":"test()
","text":"Run VerifyLoggingPersistent validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingPersistent validation.\n \"\"\"\n self.result.is_success()\n\n log_output = cast(str, self.instance_commands[0].output)\n dir_flash_output = cast(str, self.instance_commands[1].output)\n\n if \"Persistent logging: disabled\" in _get_logging_states(log_output):\n self.result.is_failure(\"Persistent logging is disabled\")\n return\n\n pattern = r\"-rw-\\s+(\\d+)\"\n persist_logs = re.search(pattern, dir_flash_output)\n\n if not persist_logs or int(persist_logs.group(1)) == 0:\n self.result.is_failure(\"No persistent logs are saved in flash\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingSourceIntf","title":"VerifyLoggingSourceIntf
","text":" Bases: AntaTest
Verifies logging source-interface for a specified VRF.
Expected Results - success: The test will pass if the provided logging source-interface is configured in the specified VRF.
- failure: The test will fail if the provided logging source-interface is NOT configured in the specified VRF.
- skipped: The test will be skipped if source-interface or VRF is not provided.
Source code in anta/tests/logging.py
class VerifyLoggingSourceIntf(AntaTest):\n\"\"\"\n Verifies logging source-interface for a specified VRF.\n\n Expected Results:\n * success: The test will pass if the provided logging source-interface is configured in the specified VRF.\n * failure: The test will fail if the provided logging source-interface is NOT configured in the specified VRF.\n * skipped: The test will be skipped if source-interface or VRF is not provided.\n \"\"\"\n\n name = \"VerifyLoggingSourceInt\"\n description = \"Verifies logging source-interface for a specified VRF.\"\n categories = [\"logging\"]\n commands = [AntaTestCommand(command=\"show logging\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingSrcDst validation.\n\n Args:\n intf: Source-interface to use as source IP of log messages.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n pattern = rf\"Logging source-interface '{intf}'.*VRF {vrf}\"\n\n if re.search(pattern, _get_logging_states(output)):\n self.result.is_success()\n else:\n self.result.is_failure(f\"Source-interface '{intf}' is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingSourceIntf.test","title":"test(intf=None, vrf='default')
","text":"Run VerifyLoggingSrcDst validation.
Parameters:
Name Type Description Default intf
Optional[str]
Source-interface to use as source IP of log messages.
None
vrf
str
The name of the VRF to transport log messages. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self, intf: Optional[str] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyLoggingSrcDst validation.\n\n Args:\n intf: Source-interface to use as source IP of log messages.\n vrf: The name of the VRF to transport log messages. Defaults to 'default'.\n \"\"\"\n if not intf or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because intf or vrf was not supplied\")\n return\n\n output = cast(str, self.instance_commands[0].output)\n\n pattern = rf\"Logging source-interface '{intf}'.*VRF {vrf}\"\n\n if re.search(pattern, _get_logging_states(output)):\n self.result.is_success()\n else:\n self.result.is_failure(f\"Source-interface '{intf}' is not configured in VRF {vrf}\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingTimestamp","title":"VerifyLoggingTimestamp
","text":" Bases: AntaTest
Verifies if logs are generated with the approprate timestamp.
Expected Results - success: The test will pass if logs are generated with the appropriated timestamp.
- failure: The test will fail if logs are NOT generated with the appropriated timestamp.
Source code in anta/tests/logging.py
class VerifyLoggingTimestamp(AntaTest):\n\"\"\"\n Verifies if logs are generated with the approprate timestamp.\n\n Expected Results:\n * success: The test will pass if logs are generated with the appropriated timestamp.\n * failure: The test will fail if logs are NOT generated with the appropriated timestamp.\n \"\"\"\n\n name = \"VerifyLoggingTimestamp\"\n description = \"Verifies if logs are generated with the appropriate timestamp.\"\n categories = [\"logging\"]\n commands = [\n AntaTestCommand(command=\"send log level informational message ANTA VerifyLoggingTimestamp validation\"),\n AntaTestCommand(command=\"show logging informational last 30 seconds | grep ANTA\", ofmt=\"text\"),\n ]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyLoggingTimestamp validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingTimestamp validation\"\n timestamp_pattern = r\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}-\\d{2}:\\d{2}\"\n\n output = cast(str, self.instance_commands[1].output)\n\n lines = output.strip().split(\"\\n\")[::-1]\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if re.search(timestamp_pattern, last_line_with_pattern):\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the appropriate timestamp format\")\n
"},{"location":"api/tests.logging/#anta.tests.logging.VerifyLoggingTimestamp.test","title":"test()
","text":"Run VerifyLoggingTimestamp validation.
Source code in anta/tests/logging.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyLoggingTimestamp validation.\n \"\"\"\n log_pattern = r\"ANTA VerifyLoggingTimestamp validation\"\n timestamp_pattern = r\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}-\\d{2}:\\d{2}\"\n\n output = cast(str, self.instance_commands[1].output)\n\n lines = output.strip().split(\"\\n\")[::-1]\n\n last_line_with_pattern = \"\"\n for line in lines:\n if re.search(log_pattern, line):\n last_line_with_pattern = line\n break\n\n if re.search(timestamp_pattern, last_line_with_pattern):\n self.result.is_success()\n else:\n self.result.is_failure(\"Logs are not generated with the appropriate timestamp format\")\n
"},{"location":"api/tests/","title":"Overview","text":""},{"location":"api/tests/#anta-tests-landing-page","title":"ANTA Tests landing page","text":"This section describes all the available tests provided by ANTA package.
- AAA
- Configuration
- Connectivity
- Field Notice
- Hardware
- Interfaces
- Logging
- MLAG
- Multicast
- Profiles
- Routing Generic
- Routing BGP
- Routing OSPF
- Security
- SNMP
- Software
- STP
- System
- VxLAN
All these tests can be imported in a catalog to be used by the anta cli or in your own framework
"},{"location":"api/tests.mlag/","title":"MLAG","text":""},{"location":"api/tests.mlag/#anta-catalog-for-mlag-tests","title":"ANTA catalog for mlag tests","text":"Test functions related to Multi-Chassis LAG
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagConfigSanity","title":"VerifyMlagConfigSanity
","text":" Bases: AntaTest
Verifies there are no MLAG config-sanity inconsistencies.
Source code in anta/tests/mlag.py
class VerifyMlagConfigSanity(AntaTest):\n\"\"\"\n Verifies there are no MLAG config-sanity inconsistencies.\n \"\"\"\n\n name = \"VerifyMlagConfigSanity\"\n description = \"Verifies there are no MLAG config-sanity inconsistencies.\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag config-sanity\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"mlagActive\" not in command_output.keys():\n self.result.is_error(\"Incorrect JSON response - mlagActive state not found\")\n elif command_output[\"mlagActive\"] is False:\n self.result.is_skipped(\"MLAG is disabled\")\n elif len(command_output[\"globalConfiguration\"]) > 0 or len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure()\n if len(command_output[\"globalConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Global inconsistancies: \" f\"{command_output['globalConfiguration']}\")\n if len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Interface inconsistancies: \" f\"{command_output['interfaceConfiguration']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagConfigSanity.test","title":"test()
","text":"Run VerifyMlagConfigSanity validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"mlagActive\" not in command_output.keys():\n self.result.is_error(\"Incorrect JSON response - mlagActive state not found\")\n elif command_output[\"mlagActive\"] is False:\n self.result.is_skipped(\"MLAG is disabled\")\n elif len(command_output[\"globalConfiguration\"]) > 0 or len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure()\n if len(command_output[\"globalConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Global inconsistancies: \" f\"{command_output['globalConfiguration']}\")\n if len(command_output[\"interfaceConfiguration\"]) > 0:\n self.result.is_failure(\"MLAG config-sanity returned Interface inconsistancies: \" f\"{command_output['interfaceConfiguration']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagInterfaces","title":"VerifyMlagInterfaces
","text":" Bases: AntaTest
Verifies there are no inactive or active-partial MLAG interfaces.
Source code in anta/tests/mlag.py
class VerifyMlagInterfaces(AntaTest):\n\"\"\"\n Verifies there are no inactive or active-partial MLAG interfaces.\n \"\"\"\n\n name = \"VerifyMlagInterfaces\"\n description = \"Verifies there are no inactive or active-partial MLAG interfaces.\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagInterfaces validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif command_output[\"mlagPorts\"][\"Inactive\"] != 0 or command_output[\"mlagPorts\"][\"Active-partial\"] != 0:\n self.result.is_failure(f\"MLAG status is not OK: {command_output['mlagPorts']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagInterfaces.test","title":"test()
","text":"Run VerifyMlagInterfaces validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagInterfaces validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif command_output[\"mlagPorts\"][\"Inactive\"] != 0 or command_output[\"mlagPorts\"][\"Active-partial\"] != 0:\n self.result.is_failure(f\"MLAG status is not OK: {command_output['mlagPorts']}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagStatus","title":"VerifyMlagStatus
","text":" Bases: AntaTest
Verifies if MLAG us running, and if the status is good state is active, negotiation status is connected, local int is up, peer link is up.
Source code in anta/tests/mlag.py
class VerifyMlagStatus(AntaTest):\n\"\"\"\n Verifies if MLAG us running, and if the status is good\n state is active, negotiation status is connected, local int is up, peer link is up.\n \"\"\"\n\n name = \"VerifyMlagStatus\"\n description = \"Verifies MLAG status\"\n categories = [\"mlag\"]\n commands = [AntaTestCommand(command=\"show mlag\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyMlagStatus validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif (\n command_output[\"state\"] != \"active\"\n or command_output[\"negStatus\"] != \"connected\"\n or command_output[\"localIntfStatus\"] != \"up\"\n or command_output[\"peerLinkStatus\"] != \"up\"\n ):\n self.result.is_failure(f\"MLAG status is not OK: {command_output}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.mlag/#anta.tests.mlag.VerifyMlagStatus.test","title":"test()
","text":"Run VerifyMlagStatus validation
Source code in anta/tests/mlag.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyMlagStatus validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"state\"] == \"disabled\":\n self.result.is_skipped(\"MLAG is disabled\")\n elif (\n command_output[\"state\"] != \"active\"\n or command_output[\"negStatus\"] != \"connected\"\n or command_output[\"localIntfStatus\"] != \"up\"\n or command_output[\"peerLinkStatus\"] != \"up\"\n ):\n self.result.is_failure(f\"MLAG status is not OK: {command_output}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.multicast/","title":"Multicast","text":""},{"location":"api/tests.multicast/#anta-catalog-for-multicast-tests","title":"ANTA catalog for multicast tests","text":"Test functions related to multicast
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingGlobal","title":"VerifyIGMPSnoopingGlobal
","text":" Bases: AntaTest
Verifies the IGMP snooping global configuration.
Parameters:
Name Type Description Default configuration
str
Expected global IGMP snooping configuration (enabled or disabled).
required Source code in anta/tests/multicast.py
class VerifyIGMPSnoopingGlobal(AntaTest):\n\"\"\"\n Verifies the IGMP snooping global configuration.\n\n Args:\n configuration (str): Expected global IGMP snooping configuration (enabled or disabled).\n \"\"\"\n\n name = \"VerifyIGMPSnoopingGlobal\"\n description = \"Verifies the IGMP snooping global configuration.\"\n categories = [\"multicast\", \"igmp\"]\n commands = [AntaTestCommand(command=\"show ip igmp snooping\")]\n\n @AntaTest.anta_test\n def test(self, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingGlobal validation\n\n Args:\n configuration: Expected global IGMP configuration (enabled or disabled).\n \"\"\"\n\n if not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingGlobal was not run as no configuration was given\")\n return\n\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingGlobal was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n if (igmp_state := command_output[\"igmpSnoopingState\"]) != configuration:\n self.result.is_failure(f\"IGMP state is not valid: {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingGlobal.test","title":"test(configuration=None)
","text":"Run VerifyIGMPSnoopingGlobal validation
Parameters:
Name Type Description Default configuration
Optional[str]
Expected global IGMP configuration (enabled or disabled).
None
Source code in anta/tests/multicast.py
@AntaTest.anta_test\ndef test(self, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingGlobal validation\n\n Args:\n configuration: Expected global IGMP configuration (enabled or disabled).\n \"\"\"\n\n if not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingGlobal was not run as no configuration was given\")\n return\n\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingGlobal was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n if (igmp_state := command_output[\"igmpSnoopingState\"]) != configuration:\n self.result.is_failure(f\"IGMP state is not valid: {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingVlans","title":"VerifyIGMPSnoopingVlans
","text":" Bases: AntaTest
Verifies the IGMP snooping configuration for some VLANs.
Parameters:
Name Type Description Default vlans
List[str]
A list of VLANs
required configuration
str
Expected IGMP snooping configuration (enabled or disabled) for these VLANs.
required Source code in anta/tests/multicast.py
class VerifyIGMPSnoopingVlans(AntaTest):\n\"\"\"\n Verifies the IGMP snooping configuration for some VLANs.\n\n Args:\n vlans (List[str]): A list of VLANs\n configuration (str): Expected IGMP snooping configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n name = \"VerifyIGMPSnoopingVlans\"\n description = \"Verifies the IGMP snooping configuration for some VLANs.\"\n categories = [\"multicast\", \"igmp\"]\n commands = [AntaTestCommand(command=\"show ip igmp snooping\")]\n\n @AntaTest.anta_test\n def test(self, vlans: Optional[List[str]] = None, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingVlans validation\n\n Args:\n vlans: List of VLANs.\n configuration: Expected IGMP configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n if not vlans or not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingVlans was not run as no vlans or configuration was given\")\n return\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingVlans was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n for vlan in vlans:\n if vlan not in command_output[\"vlans\"]:\n self.result.is_failure(f\"Supplied vlan {vlan} is not present on the device.\")\n continue\n\n igmp_state = command_output[\"vlans\"][str(vlan)][\"igmpSnoopingState\"]\n if igmp_state != configuration:\n self.result.is_failure(f\"IGMP state for vlan {vlan} is {igmp_state}\")\n
"},{"location":"api/tests.multicast/#anta.tests.multicast.VerifyIGMPSnoopingVlans.test","title":"test(vlans=None, configuration=None)
","text":"Run VerifyIGMPSnoopingVlans validation
Parameters:
Name Type Description Default vlans
Optional[List[str]]
List of VLANs.
None
configuration
Optional[str]
Expected IGMP configuration (enabled or disabled) for these VLANs.
None
Source code in anta/tests/multicast.py
@AntaTest.anta_test\ndef test(self, vlans: Optional[List[str]] = None, configuration: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyIGMPSnoopingVlans validation\n\n Args:\n vlans: List of VLANs.\n configuration: Expected IGMP configuration (enabled or disabled) for these VLANs.\n \"\"\"\n\n if not vlans or not configuration:\n self.result.is_skipped(\"VerifyIGMPSnoopingVlans was not run as no vlans or configuration was given\")\n return\n if configuration not in [\"enabled\", \"disabled\"]:\n self.result.is_error(f\"VerifyIGMPSnoopingVlans was not run as 'configuration': {configuration} is not in the allowed values: ['enabled', 'disabled'])\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n logger.debug(f\"query self.result is: {command_output}\")\n\n self.result.is_success()\n for vlan in vlans:\n if vlan not in command_output[\"vlans\"]:\n self.result.is_failure(f\"Supplied vlan {vlan} is not present on the device.\")\n continue\n\n igmp_state = command_output[\"vlans\"][str(vlan)][\"igmpSnoopingState\"]\n if igmp_state != configuration:\n self.result.is_failure(f\"IGMP state for vlan {vlan} is {igmp_state}\")\n
"},{"location":"api/tests.profiles/","title":"Profiles","text":""},{"location":"api/tests.profiles/#anta-catalog-for-profiles-tests","title":"ANTA catalog for profiles tests","text":"Test functions related to ASIC profiles
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyTcamProfile","title":"VerifyTcamProfile
","text":" Bases: AntaTest
Verifies the device is using the configured TCAM profile.
Source code in anta/tests/profiles.py
class VerifyTcamProfile(AntaTest):\n\"\"\"\n Verifies the device is using the configured TCAM profile.\n \"\"\"\n\n name = \"VerifyTcamProfile\"\n description = \"Verify that the assigned TCAM profile is actually running on the device\"\n categories = [\"profiles\"]\n commands = [AntaTestCommand(command=\"show hardware tcam profile\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyTcamProfile validation\n\n Args:\n profile: Expected TCAM profile.\n \"\"\"\n if not profile:\n self.result.is_skipped(\"VerifyTcamProfile was not run as no profile was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"pmfProfiles\"][\"FixedSystem\"][\"status\"] == command_output[\"pmfProfiles\"][\"FixedSystem\"][\"config\"] == profile:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Incorrect profile running on device: {command_output['pmfProfiles']['FixedSystem']['status']}\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyTcamProfile.test","title":"test(profile=None)
","text":"Run VerifyTcamProfile validation
Parameters:
Name Type Description Default profile
Optional[str]
Expected TCAM profile.
None
Source code in anta/tests/profiles.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyTcamProfile validation\n\n Args:\n profile: Expected TCAM profile.\n \"\"\"\n if not profile:\n self.result.is_skipped(\"VerifyTcamProfile was not run as no profile was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"pmfProfiles\"][\"FixedSystem\"][\"status\"] == command_output[\"pmfProfiles\"][\"FixedSystem\"][\"config\"] == profile:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Incorrect profile running on device: {command_output['pmfProfiles']['FixedSystem']['status']}\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyUnifiedForwardingTableMode","title":"VerifyUnifiedForwardingTableMode
","text":" Bases: AntaTest
Verifies the device is using the expected Unified Forwarding Table mode.
Source code in anta/tests/profiles.py
class VerifyUnifiedForwardingTableMode(AntaTest):\n\"\"\"\n Verifies the device is using the expected Unified Forwarding Table mode.\n \"\"\"\n\n name = \"VerifyUnifiedForwardingTableMode\"\n description = \"\"\n categories = [\"profiles\"]\n commands = [AntaTestCommand(command=\"show platform trident forwarding-table partition\", ofmt=\"json\")]\n\n @skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n @AntaTest.anta_test\n def test(self, mode: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyUnifiedForwardingTableMode validation\n\n Args:\n mode: Expected UFT mode.\n \"\"\"\n if not mode:\n self.result.is_skipped(\"VerifyUnifiedForwardingTableMode was not run as no mode was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"uftMode\"] == mode:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device is not running correct UFT mode (expected: {mode} / running: {command_output['uftMode']})\")\n
"},{"location":"api/tests.profiles/#anta.tests.profiles.VerifyUnifiedForwardingTableMode.test","title":"test(mode=None)
","text":"Run VerifyUnifiedForwardingTableMode validation
Parameters:
Name Type Description Default mode
Optional[str]
Expected UFT mode.
None
Source code in anta/tests/profiles.py
@skip_on_platforms([\"cEOSLab\", \"vEOS-lab\"])\n@AntaTest.anta_test\ndef test(self, mode: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyUnifiedForwardingTableMode validation\n\n Args:\n mode: Expected UFT mode.\n \"\"\"\n if not mode:\n self.result.is_skipped(\"VerifyUnifiedForwardingTableMode was not run as no mode was given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n if command_output[\"uftMode\"] == mode:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Device is not running correct UFT mode (expected: {mode} / running: {command_output['uftMode']})\")\n
"},{"location":"api/tests.routing.bgp/","title":"BGP","text":""},{"location":"api/tests.routing.bgp/#anta-catalog-for-routing-bgp-tests","title":"ANTA catalog for routing-bgp tests","text":"BGP test functions
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNCount","title":"VerifyBGPEVPNCount
","text":" Bases: AntaTest
Verifies all EVPN BGP sessions are established (default VRF) and the actual number of BGP EVPN neighbors is the one we expect (default VRF).
- self.result = \u201cskipped\u201d if the
number
parameter is missing - self.result = \u201csuccess\u201d if all EVPN BGP sessions are Established and if the actual number of BGP EVPN neighbors is the one we expect.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPEVPNCount(AntaTest):\n\"\"\"\n Verifies all EVPN BGP sessions are established (default VRF)\n and the actual number of BGP EVPN neighbors is the one we expect (default VRF).\n\n * self.result = \"skipped\" if the `number` parameter is missing\n * self.result = \"success\" if all EVPN BGP sessions are Established and if the actual\n number of BGP EVPN neighbors is the one we expect.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPEVPNCount\"\n description = \"Verifies all EVPN BGP sessions are established (default VRF) and the actual number of BGP EVPN neighbors is the one we expect (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp evpn summary\")]\n\n @check_bgp_family_enable(\"evpn\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPEVPNCount validation\n\n Args:\n number: The expected number of BGP EVPN neighbors in the default VRF.\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPEVPNCount could not run because number was not supplied.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP EVPN peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNCount.test","title":"test(number=None)
","text":"Run VerifyBGPEVPNCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP EVPN neighbors in the default VRF.
None
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"evpn\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPEVPNCount validation\n\n Args:\n number: The expected number of BGP EVPN neighbors in the default VRF.\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPEVPNCount could not run because number was not supplied.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP EVPN peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNState","title":"VerifyBGPEVPNState
","text":" Bases: AntaTest
Verifies all EVPN BGP sessions are established (default VRF).
- self.result = \u201cskipped\u201d if no BGP EVPN peers are returned by the device
- self.result = \u201csuccess\u201d if all EVPN BGP sessions are established.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPEVPNState(AntaTest):\n\"\"\"\n Verifies all EVPN BGP sessions are established (default VRF).\n\n * self.result = \"skipped\" if no BGP EVPN peers are returned by the device\n * self.result = \"success\" if all EVPN BGP sessions are established.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPEVPNState\"\n description = \"Verifies all EVPN BGP sessions are established (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp evpn summary\")]\n\n @check_bgp_family_enable(\"evpn\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPEVPNState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPEVPNState.test","title":"test()
","text":"Run VerifyBGPEVPNState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"evpn\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPEVPNState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following EVPN peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastCount","title":"VerifyBGPIPv4UnicastCount
","text":" Bases: AntaTest
Verifies all IPv4 unicast BGP sessions are established and all BGP messages queues for these sessions are empty and the actual number of BGP IPv4 unicast neighbors is the one we expect.
- self.result = \u201cskipped\u201d if the
number
or vrf
parameter is missing - self.result = \u201csuccess\u201d if all IPv4 unicast BGP sessions are established and if all BGP messages queues for these sessions are empty and if the actual number of BGP IPv4 unicast neighbors is equal to `number.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv4UnicastCount(AntaTest):\n\"\"\"\n Verifies all IPv4 unicast BGP sessions are established\n and all BGP messages queues for these sessions are empty\n and the actual number of BGP IPv4 unicast neighbors is the one we expect.\n\n * self.result = \"skipped\" if the `number` or `vrf` parameter is missing\n * self.result = \"success\" if all IPv4 unicast BGP sessions are established\n and if all BGP messages queues for these sessions are empty\n and if the actual number of BGP IPv4 unicast neighbors is equal to `number.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastCount\"\n description = (\n \"Verifies all IPv4 unicast BGP sessions are established and all their BGP messages queues are empty and \"\n \" the actual number of BGP IPv4 unicast neighbors is the one we expect.\"\n )\n categories = [\"routing\", \"bgp\"]\n template = AntaTestTemplate(template=\"show bgp ipv4 unicast summary vrf {vrf}\")\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPIPv4UnicastCount validation\n\n Args:\n number: The expected number of BGP IPv4 unicast neighbors.\n vrf: VRF to verify (template parameter)\n \"\"\"\n\n if not number:\n self.result.is_skipped(\"VerifyBGPIPv4UnicastCount could not run because number was not supplied\")\n return\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vrf = cast(Dict[str, str], command.template_params).get(\"vrf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n peers = command_output[\"vrfs\"][vrf][\"peers\"]\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP peer in vrf {vrf} and got {len(peers)}\")\n if state_issue:\n self.result.is_failure(f\"The following IPv4 peers are not established: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastCount.test","title":"test(number=None)
","text":"Run VerifyBGPIPv4UnicastCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP IPv4 unicast neighbors.
None
vrf
VRF to verify (template parameter)
required Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv4\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPIPv4UnicastCount validation\n\n Args:\n number: The expected number of BGP IPv4 unicast neighbors.\n vrf: VRF to verify (template parameter)\n \"\"\"\n\n if not number:\n self.result.is_skipped(\"VerifyBGPIPv4UnicastCount could not run because number was not supplied\")\n return\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vrf = cast(Dict[str, str], command.template_params).get(\"vrf\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n peers = command_output[\"vrfs\"][vrf][\"peers\"]\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP peer in vrf {vrf} and got {len(peers)}\")\n if state_issue:\n self.result.is_failure(f\"The following IPv4 peers are not established: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastState","title":"VerifyBGPIPv4UnicastState
","text":" Bases: AntaTest
Verifies all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cskipped\u201d if no BGP vrf are returned by the device
- self.result = \u201csuccess\u201d if all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv4UnicastState(AntaTest):\n\"\"\"\n Verifies all IPv4 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n\n * self.result = \"skipped\" if no BGP vrf are returned by the device\n * self.result = \"success\" if all IPv4 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv4UnicastState\"\n description = \"Verifies all IPv4 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp ipv4 unicast summary vrf all\")]\n\n @check_bgp_family_enable(\"ipv4\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPIPv4UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv4UnicastState.test","title":"test()
","text":"Run VerifyBGPIPv4UnicastState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv4\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPIPv4UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv6UnicastState","title":"VerifyBGPIPv6UnicastState
","text":" Bases: AntaTest
Verifies all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cskipped\u201d if no BGP vrf are returned by the device
- self.result = \u201csuccess\u201d if all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPIPv6UnicastState(AntaTest):\n\"\"\"\n Verifies all IPv6 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n\n * self.result = \"skipped\" if no BGP vrf are returned by the device\n * self.result = \"success\" if all IPv6 unicast BGP sessions are established (for all VRF)\n and all BGP messages queues for these sessions are empty (for all VRF).\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPIPv6UnicastState\"\n description = \"Verifies all IPv6 unicast BGP sessions are established (for all VRF) and all BGP messages queues for these sessions are empty (for all VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp ipv6 unicast summary vrf all\")]\n\n @check_bgp_family_enable(\"ipv6\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPIPv6UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPIPv6UnicastState.test","title":"test()
","text":"Run VerifyBGPIPv6UnicastState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"ipv6\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPIPv6UnicastState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n state_issue = _check_bgp_vrfs(command_output[\"vrfs\"])\n\n if not state_issue:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Some IPv4 Unicast BGP Peer are not up: {state_issue}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCCount","title":"VerifyBGPRTCCount
","text":" Bases: AntaTest
Verifies all RTC BGP sessions are established (default VRF) and the actual number of BGP RTC neighbors is the one we expect (default VRF).
- self.result = \u201cskipped\u201d if the
number
parameter is missing - self.result = \u201csuccess\u201d if all RTC BGP sessions are Established and if the actual number of BGP RTC neighbors is the one we expect.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPRTCCount(AntaTest):\n\"\"\"\n Verifies all RTC BGP sessions are established (default VRF)\n and the actual number of BGP RTC neighbors is the one we expect (default VRF).\n\n * self.result = \"skipped\" if the `number` parameter is missing\n * self.result = \"success\" if all RTC BGP sessions are Established and if the actual\n number of BGP RTC neighbors is the one we expect.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPRTCCount\"\n description = \"Verifies all RTC BGP sessions are established (default VRF) and the actual number of BGP RTC neighbors is the one we expect (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp rt-membership summary\")]\n\n @check_bgp_family_enable(\"rtc\")\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPRTCCount validation\n\n Args:\n number: The expected number of BGP RTC neighbors (default VRF).\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPRTCCount could not run because number was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP RTC peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCCount.test","title":"test(number=None)
","text":"Run VerifyBGPRTCCount validation
Parameters:
Name Type Description Default number
Optional[int]
The expected number of BGP RTC neighbors (default VRF).
None
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"rtc\")\n@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyBGPRTCCount validation\n\n Args:\n number: The expected number of BGP RTC neighbors (default VRF).\n \"\"\"\n if not number:\n self.result.is_skipped(\"VerifyBGPRTCCount could not run because number was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n peers = command_output[\"vrfs\"][\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers and len(peers) == number:\n self.result.is_success()\n else:\n self.result.is_failure()\n if len(peers) != number:\n self.result.is_failure(f\"Expecting {number} BGP RTC peers and got {len(peers)}\")\n if non_established_peers:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCState","title":"VerifyBGPRTCState
","text":" Bases: AntaTest
Verifies all RTC BGP sessions are established (default VRF).
- self.result = \u201cskipped\u201d if no BGP RTC peers are returned by the device
- self.result = \u201csuccess\u201d if all RTC BGP sessions are established.
- self.result = \u201cfailure\u201d otherwise.
Source code in anta/tests/routing/bgp.py
class VerifyBGPRTCState(AntaTest):\n\"\"\"\n Verifies all RTC BGP sessions are established (default VRF).\n\n * self.result = \"skipped\" if no BGP RTC peers are returned by the device\n * self.result = \"success\" if all RTC BGP sessions are established.\n * self.result = \"failure\" otherwise.\n \"\"\"\n\n name = \"VerifyBGPRTCState\"\n description = \"Verifies all RTC BGP sessions are established (default VRF).\"\n categories = [\"routing\", \"bgp\"]\n commands = [AntaTestCommand(command=\"show bgp rt-membership summary\")]\n\n @check_bgp_family_enable(\"rtc\")\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBGPRTCState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.bgp/#anta.tests.routing.bgp.VerifyBGPRTCState.test","title":"test()
","text":"Run VerifyBGPRTCState validation
Source code in anta/tests/routing/bgp.py
@check_bgp_family_enable(\"rtc\")\n@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBGPRTCState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n bgp_vrfs = command_output[\"vrfs\"]\n\n peers = bgp_vrfs[\"default\"][\"peers\"]\n non_established_peers = [peer for peer, peer_dict in peers.items() if peer_dict[\"peerState\"] != \"Established\"]\n\n if not non_established_peers:\n self.result.is_success()\n else:\n self.result.is_failure(f\"The following RTC peers are not established: {non_established_peers}\")\n
"},{"location":"api/tests.routing.generic/","title":"Generic","text":""},{"location":"api/tests.routing.generic/#anta-catalog-for-routing-generic-tests","title":"ANTA catalog for routing-generic tests","text":"Generic routing test functions
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyBFD","title":"VerifyBFD
","text":" Bases: AntaTest
Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).
Source code in anta/tests/routing/generic.py
class VerifyBFD(AntaTest):\n\"\"\"\n Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).\n \"\"\"\n\n name = \"VerifyBFD\"\n description = \"Verifies there is no BFD peer in down state (all VRF, IPv4 neighbors).\"\n categories = [\"routing\", \"generic\"]\n # revision 1 as later revision introduce additional nesting for type\n commands = [AntaTestCommand(command=\"show bfd peers\", version=1)]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyBFD validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n self.result.is_success()\n\n for _, vrf_data in command_output[\"vrfs\"].items():\n for _, neighbor_data in vrf_data[\"ipv4Neighbors\"].items():\n for peer, peer_data in neighbor_data[\"peerStats\"].items():\n if (peer_status := peer_data[\"status\"]) != \"up\":\n failure_message = f\"bfd state for peer '{peer}' is {peer_status} (expected up).\"\n if (peer_l3intf := peer_data.get(\"l3intf\")) is not None and peer_l3intf != \"\":\n failure_message += f\" Interface: {peer_l3intf}.\"\n self.result.is_failure(failure_message)\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyBFD.test","title":"test()
","text":"Run VerifyBFD validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyBFD validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n self.result.is_success()\n\n for _, vrf_data in command_output[\"vrfs\"].items():\n for _, neighbor_data in vrf_data[\"ipv4Neighbors\"].items():\n for peer, peer_data in neighbor_data[\"peerStats\"].items():\n if (peer_status := peer_data[\"status\"]) != \"up\":\n failure_message = f\"bfd state for peer '{peer}' is {peer_status} (expected up).\"\n if (peer_l3intf := peer_data.get(\"l3intf\")) is not None and peer_l3intf != \"\":\n failure_message += f\" Interface: {peer_l3intf}.\"\n self.result.is_failure(failure_message)\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingProtocolModel","title":"VerifyRoutingProtocolModel
","text":" Bases: AntaTest
Verifies the configured routing protocol model is the one we expect. And if there is no mismatch between the configured and operating routing protocol model.
model(str): Expected routing protocol model (multi-agent or ribd). Default is multi-agent\n
Source code in anta/tests/routing/generic.py
class VerifyRoutingProtocolModel(AntaTest):\n\"\"\"\n Verifies the configured routing protocol model is the one we expect.\n And if there is no mismatch between the configured and operating routing protocol model.\n\n model(str): Expected routing protocol model (multi-agent or ribd). Default is multi-agent\n \"\"\"\n\n name = \"VerifyRoutingProtocolModel\"\n description = (\n \"Verifies the configured routing protocol model is the expected one and if there is no mismatch between the configured and operating routing protocol model.\"\n )\n categories = [\"routing\", \"generic\"]\n # \"revision\": 3\n commands = [AntaTestCommand(command=\"show ip route summary\")]\n\n @AntaTest.anta_test\n def test(self, model: Optional[str] = \"multi-agent\") -> None:\n\"\"\"Run VerifyRoutingProtocolModel validation\"\"\"\n\n if not model:\n self.result.is_skipped(\"VerifyRoutingProtocolModel was not run as no model was given\")\n return\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n configured_model = command_output[\"protoModelStatus\"][\"configuredProtoModel\"]\n operating_model = command_output[\"protoModelStatus\"][\"operatingProtoModel\"]\n if configured_model == operating_model == model:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {model}\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingProtocolModel.test","title":"test(model='multi-agent')
","text":"Run VerifyRoutingProtocolModel validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self, model: Optional[str] = \"multi-agent\") -> None:\n\"\"\"Run VerifyRoutingProtocolModel validation\"\"\"\n\n if not model:\n self.result.is_skipped(\"VerifyRoutingProtocolModel was not run as no model was given\")\n return\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n configured_model = command_output[\"protoModelStatus\"][\"configuredProtoModel\"]\n operating_model = command_output[\"protoModelStatus\"][\"operatingProtoModel\"]\n if configured_model == operating_model == model:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing model is misconfigured: configured: {configured_model} - operating: {operating_model} - expected: {model}\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingTableSize","title":"VerifyRoutingTableSize
","text":" Bases: AntaTest
Verifies the size of the IP routing table (default VRF). Should be between the two provided thresholds.
Parameters:
Name Type Description Default minimum(int)
Expected minimum routing table (default VRF) size.
required maximum(int)
Expected maximum routing table (default VRF) size.
required Source code in anta/tests/routing/generic.py
class VerifyRoutingTableSize(AntaTest):\n\"\"\"\n Verifies the size of the IP routing table (default VRF).\n Should be between the two provided thresholds.\n\n Args:\n minimum(int): Expected minimum routing table (default VRF) size.\n maximum(int): Expected maximum routing table (default VRF) size.\n \"\"\"\n\n name = \"VerifyRoutingTableSize\"\n description = \"Verifies the size of the IP routing table (default VRF). Should be between the two provided thresholds.\"\n categories = [\"routing\", \"generic\"]\n # \"revision\": 3\n commands = [AntaTestCommand(command=\"show ip route summary\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None, maximum: Optional[int] = None) -> None:\n\"\"\"Run VerifyRoutingTableSize validation\"\"\"\n\n if not minimum or not maximum:\n self.result.is_skipped(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} was not provided\")\n return\n if not isinstance(minimum, int) or not isinstance(maximum, int):\n self.result.is_error(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} is not a valid value (integer)\")\n return\n if maximum < minimum:\n self.result.is_error(f\"VerifyRoutingTableSize was not run as minimum {minimum} is greate than maximum {maximum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_routes = int(command_output[\"vrfs\"][\"default\"][\"totalRoutes\"])\n if minimum <= total_routes <= maximum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing-table has {total_routes} routes and not between min ({minimum}) and maximum ({maximum})\")\n
"},{"location":"api/tests.routing.generic/#anta.tests.routing.generic.VerifyRoutingTableSize.test","title":"test(minimum=None, maximum=None)
","text":"Run VerifyRoutingTableSize validation
Source code in anta/tests/routing/generic.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None, maximum: Optional[int] = None) -> None:\n\"\"\"Run VerifyRoutingTableSize validation\"\"\"\n\n if not minimum or not maximum:\n self.result.is_skipped(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} was not provided\")\n return\n if not isinstance(minimum, int) or not isinstance(maximum, int):\n self.result.is_error(f\"VerifyRoutingTableSize was not run as either minimum {minimum} or maximum {maximum} is not a valid value (integer)\")\n return\n if maximum < minimum:\n self.result.is_error(f\"VerifyRoutingTableSize was not run as minimum {minimum} is greate than maximum {maximum}.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n total_routes = int(command_output[\"vrfs\"][\"default\"][\"totalRoutes\"])\n if minimum <= total_routes <= maximum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"routing-table has {total_routes} routes and not between min ({minimum}) and maximum ({maximum})\")\n
"},{"location":"api/tests.routing.ospf/","title":"OSPF","text":""},{"location":"api/tests.routing.ospf/#anta-catalog-for-routing-ospf-tests","title":"ANTA catalog for routing-ospf tests","text":"OSPF test functions
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborCount","title":"VerifyOSPFNeighborCount
","text":" Bases: AntaTest
Verifies the number of OSPF neighbors in FULL state is the one we expect.
Parameters:
Name Type Description Default number
int
The expected number of OSPF neighbors in FULL state.
required Source code in anta/tests/routing/ospf.py
class VerifyOSPFNeighborCount(AntaTest):\n\"\"\"\n Verifies the number of OSPF neighbors in FULL state is the one we expect.\n\n Args:\n number (int): The expected number of OSPF neighbors in FULL state.\n \"\"\"\n\n name = \"VerifyOSPFNeighborCount\"\n description = \"Verifies the number of OSPF neighbors in FULL state is the one we expect.\"\n categories = [\"routing\", \"ospf\"]\n commands = [AntaTestCommand(command=\"show ip ospf neighbor\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None) -> None:\n\"\"\"Run VerifyOSPFNeighborCount validation\"\"\"\n if not (isinstance(number, int) and number >= 0):\n self.result.is_skipped(f\"VerifyOSPFNeighborCount was not run as the number given '{number}' is not a valid value.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if (neighbor_count := _count_ospf_neighbor(command_output)) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n if neighbor_count != number:\n self.result.is_failure(f\"device has {neighbor_count} neighbors (expected {number})\")\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n print(not_full_neighbors)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborCount.test","title":"test(number=None)
","text":"Run VerifyOSPFNeighborCount validation
Source code in anta/tests/routing/ospf.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None) -> None:\n\"\"\"Run VerifyOSPFNeighborCount validation\"\"\"\n if not (isinstance(number, int) and number >= 0):\n self.result.is_skipped(f\"VerifyOSPFNeighborCount was not run as the number given '{number}' is not a valid value.\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if (neighbor_count := _count_ospf_neighbor(command_output)) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n if neighbor_count != number:\n self.result.is_failure(f\"device has {neighbor_count} neighbors (expected {number})\")\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n print(not_full_neighbors)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborState","title":"VerifyOSPFNeighborState
","text":" Bases: AntaTest
Verifies all OSPF neighbors are in FULL state.
Source code in anta/tests/routing/ospf.py
class VerifyOSPFNeighborState(AntaTest):\n\"\"\"\n Verifies all OSPF neighbors are in FULL state.\n \"\"\"\n\n name = \"VerifyOSPFNeighborState\"\n description = \"Verifies all OSPF neighbors are in FULL state.\"\n categories = [\"routing\", \"ospf\"]\n commands = [AntaTestCommand(command=\"show ip ospf neighbor\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyOSPFNeighborState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if _count_ospf_neighbor(command_output) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.routing.ospf/#anta.tests.routing.ospf.VerifyOSPFNeighborState.test","title":"test()
","text":"Run VerifyOSPFNeighborState validation
Source code in anta/tests/routing/ospf.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyOSPFNeighborState validation\"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if _count_ospf_neighbor(command_output) == 0:\n self.result.is_skipped(\"no OSPF neighbor found\")\n return\n\n self.result.is_success()\n\n not_full_neighbors = _get_not_full_ospf_neighbors(command_output)\n if not_full_neighbors:\n self.result.is_failure(f\"Some neighbors are not correctly configured: {not_full_neighbors}.\")\n
"},{"location":"api/tests.security/","title":"Security","text":""},{"location":"api/tests.security/#anta-catalog-for-security-tests","title":"ANTA catalog for security tests","text":"Test functions related to the EOS various security settings
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpStatus","title":"VerifyAPIHttpStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyAPIHttpStatus(AntaTest):\n\"\"\"\n Verifies if eAPI HTTP server is disabled globally.\n\n Expected Results:\n * success: The test will pass if eAPI HTTP server is disabled globally.\n * failure: The test will fail if eAPI HTTP server is NOT disabled globally.\n \"\"\"\n\n name = \"VerifyAPIHttpStatus\"\n description = \"Verifies if eAPI HTTP server is disabled globally.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyAPIHTTPStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and not command_output[\"httpServer\"][\"running\"]:\n self.result.is_success()\n else:\n self.result.is_failure(\"eAPI HTTP server is enabled globally\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpStatus.test","title":"test()
","text":"Run VerifyAPIHTTPStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyAPIHTTPStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and not command_output[\"httpServer\"][\"running\"]:\n self.result.is_success()\n else:\n self.result.is_failure(\"eAPI HTTP server is enabled globally\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpsSSL","title":"VerifyAPIHttpsSSL
","text":" Bases: AntaTest
Verifies if eAPI HTTPS server SSL profile is configured and valid.
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.
- skipped: The test will be skipped if the SSL profile is not provided.
Source code in anta/tests/security.py
class VerifyAPIHttpsSSL(AntaTest):\n\"\"\"\n Verifies if eAPI HTTPS server SSL profile is configured and valid.\n\n Expected results:\n * success: The test will pass if the eAPI HTTPS server SSL profile is configured and valid.\n * failure: The test will fail if the eAPI HTTPS server SSL profile is NOT configured, misconfigured or invalid.\n * skipped: The test will be skipped if the SSL profile is not provided.\n \"\"\"\n\n name = \"VerifyAPIHttpsSSL\"\n description = \"Verifies if eAPI HTTPS server SSL profile is configured and valid.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands\")]\n\n @AntaTest.anta_test\n def test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyAPIHttpsSSL validation.\n\n Args:\n profile: SSL profile to verify.\n \"\"\"\n if not profile:\n self.result.is_skipped(f\"{self.__class__.name} did not run because profile was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"sslProfile\"][\"name\"] == profile and command_output[\"sslProfile\"][\"state\"] == \"valid\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is misconfigured or invalid\")\n\n except KeyError:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is not configured\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIHttpsSSL.test","title":"test(profile=None)
","text":"Run VerifyAPIHttpsSSL validation.
Parameters:
Name Type Description Default profile
Optional[str]
SSL profile to verify.
None
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, profile: Optional[str] = None) -> None:\n\"\"\"\n Run VerifyAPIHttpsSSL validation.\n\n Args:\n profile: SSL profile to verify.\n \"\"\"\n if not profile:\n self.result.is_skipped(f\"{self.__class__.name} did not run because profile was not supplied\")\n return\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n try:\n if command_output[\"sslProfile\"][\"name\"] == profile and command_output[\"sslProfile\"][\"state\"] == \"valid\":\n self.result.is_success()\n else:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is misconfigured or invalid\")\n\n except KeyError:\n self.result.is_failure(f\"eAPI HTTPS server SSL profile ({profile}) is not configured\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv4Acl","title":"VerifyAPIIPv4Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifyAPIIPv4Acl(AntaTest):\n\"\"\"\n Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if eAPI has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if eAPI has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifyAPIIPv4Acl\"\n description = \"Verifies if eAPI has the right number IPv4 ACL(s) configured for a specified VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands ip access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifyAPIIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for eAPI. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv6Acl","title":"VerifyAPIIPv6Acl
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyAPIIPv6Acl(AntaTest):\n\"\"\"\n Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if eAPI has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if eAPI has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifyAPIIPv6Acl\"\n description = \"Verifies if eAPI has the right number IPv6 ACL(s) configured for a specified VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management api http-commands ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyAPIIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifyAPIIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for eAPI. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifyAPIIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for eAPI. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} eAPI IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"eAPI IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv4Acl","title":"VerifySSHIPv4Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifySSHIPv4Acl(AntaTest):\n\"\"\"\n Verifies if the SSHD agent has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SSHD agent has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if the SSHD agent has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySSHIPv4Acl\"\n description = \"Verifies if the SSHD agent has IPv4 ACL(s) configured.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh ip access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySSHIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SSHD agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv6Acl","title":"VerifySSHIPv6Acl
","text":" Bases: 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.
- skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/security.py
class VerifySSHIPv6Acl(AntaTest):\n\"\"\"\n Verifies if the SSHD agent has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SSHD agent has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if the SSHD agent has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySSHIPv6Acl\"\n description = \"Verifies if the SSHD agent has IPv6 ACL(s) configured.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySSHIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SSHD agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySSHIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SSHD agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SSH IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SSH IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHStatus","title":"VerifySSHStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifySSHStatus(AntaTest):\n\"\"\"\n Verifies if the SSHD agent is disabled in the default VRF.\n\n Expected Results:\n * success: The test will pass if the SSHD agent is disabled in the default VRF.\n * failure: The test will fail if the SSHD agent is NOT disabled in the default VRF.\n \"\"\"\n\n name = \"VerifySSHStatus\"\n description = \"Verifies if the SSHD agent is disabled in the default VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management ssh\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySSHStatus validation.\n \"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n line = [line for line in command_output.split(\"\\n\") if line.startswith(\"SSHD status\")][0]\n status = line.split(\"is \")[1]\n\n if status == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(line)\n
"},{"location":"api/tests.security/#anta.tests.security.VerifySSHStatus.test","title":"test()
","text":"Run VerifySSHStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySSHStatus validation.\n \"\"\"\n\n command_output = cast(str, self.instance_commands[0].output)\n\n line = [line for line in command_output.split(\"\\n\") if line.startswith(\"SSHD status\")][0]\n status = line.split(\"is \")[1]\n\n if status == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(line)\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyTelnetStatus","title":"VerifyTelnetStatus
","text":" Bases: 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.
Source code in anta/tests/security.py
class VerifyTelnetStatus(AntaTest):\n\"\"\"\n Verifies if Telnet is disabled in the default VRF.\n\n Expected Results:\n * success: The test will pass if Telnet is disabled in the default VRF.\n * failure: The test will fail if Telnet is NOT disabled in the default VRF.\n \"\"\"\n\n name = \"VerifyTelnetStatus\"\n description = \"Verifies if Telnet is disabled in the default VRF.\"\n categories = [\"security\"]\n commands = [AntaTestCommand(command=\"show management telnet\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyTelnetStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"serverState\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"Telnet status for Default VRF is enabled\")\n
"},{"location":"api/tests.security/#anta.tests.security.VerifyTelnetStatus.test","title":"test()
","text":"Run VerifyTelnetStatus validation.
Source code in anta/tests/security.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyTelnetStatus validation.\n \"\"\"\n\n command_output = cast(Dict[str, Any], self.instance_commands[0].output)\n\n if command_output[\"serverState\"] == \"disabled\":\n self.result.is_success()\n else:\n self.result.is_failure(\"Telnet status for Default VRF is enabled\")\n
"},{"location":"api/tests.snmp/","title":"SNMP","text":""},{"location":"api/tests.snmp/#anta-catalog-for-snmp-tests","title":"ANTA catalog for SNMP tests","text":"Test functions related to the EOS various SNMP settings
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv4Acl","title":"VerifySnmpIPv4Acl
","text":" Bases: AntaTest
Verifies if the SNMP agent has the right number IPv4 ACL(s) configured for a specified VRF.
Expected results - success: The test will pass if the SNMP agent has the provided number of IPv4 ACL(s) in the specified VRF.
- failure: The test will fail if the SNMP agent has not the right number of IPv4 ACL(s) in the specified VRF.
- skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.
Source code in anta/tests/snmp.py
class VerifySnmpIPv4Acl(AntaTest):\n\"\"\"\n Verifies if the SNMP agent has the right number IPv4 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SNMP agent has the provided number of IPv4 ACL(s) in the specified VRF.\n * failure: The test will fail if the SNMP agent has not the right number of IPv4 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv4 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpIPv4Acl\"\n description = \"Verifies if the SNMP agent has IPv4 ACL(s) configured.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp ipv4 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv4Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySnmpIPv4Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv4 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv4Acl validation.\n\n Args:\n number: The number of expected IPv4 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv4_acl_list = command_output[\"ipAclList\"][\"aclList\"]\n ipv4_acl_number = len(ipv4_acl_list)\n not_configured_acl_list = []\n\n if ipv4_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv4 ACL(s) in vrf {vrf} but got {ipv4_acl_number}\")\n return\n\n for ipv4_acl in ipv4_acl_list:\n if vrf not in ipv4_acl[\"configuredVrfs\"] or vrf not in ipv4_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv4_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv4 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv6Acl","title":"VerifySnmpIPv6Acl
","text":" Bases: AntaTest
Verifies if the SNMP agent has the right number IPv6 ACL(s) configured for a specified VRF.
Expected results - success: The test will pass if the SNMP agent has the provided number of IPv6 ACL(s) in the specified VRF.
- failure: The test will fail if the SNMP agent 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.
Source code in anta/tests/snmp.py
class VerifySnmpIPv6Acl(AntaTest):\n\"\"\"\n Verifies if the SNMP agent has the right number IPv6 ACL(s) configured for a specified VRF.\n\n Expected results:\n * success: The test will pass if the SNMP agent has the provided number of IPv6 ACL(s) in the specified VRF.\n * failure: The test will fail if the SNMP agent has not the right number of IPv6 ACL(s) in the specified VRF.\n * skipped: The test will be skipped if the number of IPv6 ACL(s) or VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpIPv6Acl\"\n description = \"Verifies if the SNMP agent has IPv6 ACL(s) configured.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp ipv6 access-list summary\")]\n\n @AntaTest.anta_test\n def test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpIPv6Acl.test","title":"test(number=None, vrf='default')
","text":"Run VerifySnmpIPv6Acl validation.
Parameters:
Name Type Description Default number
Optional[int]
The number of expected IPv6 ACL(s).
None
vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, number: Optional[int] = None, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpIPv6Acl validation.\n\n Args:\n number: The number of expected IPv6 ACL(s).\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not number or not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because number or vrf was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n ipv6_acl_list = command_output[\"ipv6AclList\"][\"aclList\"]\n ipv6_acl_number = len(ipv6_acl_list)\n not_configured_acl_list = []\n\n if ipv6_acl_number != number:\n self.result.is_failure(f\"Expected {number} SNMP IPv6 ACL(s) in vrf {vrf} but got {ipv6_acl_number}\")\n return\n\n for ipv6_acl in ipv6_acl_list:\n if vrf not in ipv6_acl[\"configuredVrfs\"] or vrf not in ipv6_acl[\"activeVrfs\"]:\n not_configured_acl_list.append(ipv6_acl[\"name\"])\n\n if not_configured_acl_list:\n self.result.is_failure(f\"SNMP IPv6 ACL(s) not configured or active in vrf {vrf}: {not_configured_acl_list}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpStatus","title":"VerifySnmpStatus
","text":" Bases: AntaTest
Verifies whether the SNMP agent is enabled in a specified VRF.
Expected Results - success: The test will pass if the SNMP agent is enabled in the specified VRF.
- failure: The test will fail if the SNMP agent is disabled in the specified VRF.
- skipped: The test will be skipped if the VRF parameter is not provided.
Source code in anta/tests/snmp.py
class VerifySnmpStatus(AntaTest):\n\"\"\"\n Verifies whether the SNMP agent is enabled in a specified VRF.\n\n Expected Results:\n * success: The test will pass if the SNMP agent is enabled in the specified VRF.\n * failure: The test will fail if the SNMP agent is disabled in the specified VRF.\n * skipped: The test will be skipped if the VRF parameter is not provided.\n \"\"\"\n\n name = \"VerifySnmpStatus\"\n description = \"Verifies if the SNMP agent is enabled.\"\n categories = [\"snmp\"]\n commands = [AntaTestCommand(command=\"show snmp\")]\n\n @AntaTest.anta_test\n def test(self, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpStatus validation.\n\n Args:\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because vrf was not supplied\")\n else:\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and vrf in command_output[\"vrfs\"][\"snmpVrfs\"]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"SNMP agent disabled in vrf {vrf}\")\n
"},{"location":"api/tests.snmp/#anta.tests.snmp.VerifySnmpStatus.test","title":"test(vrf='default')
","text":"Run VerifySnmpStatus validation.
Parameters:
Name Type Description Default vrf
str
The name of the VRF in which to check for the SNMP agent. Defaults to \u2018default\u2019.
'default'
Source code in anta/tests/snmp.py
@AntaTest.anta_test\ndef test(self, vrf: str = \"default\") -> None:\n\"\"\"\n Run VerifySnmpStatus validation.\n\n Args:\n vrf: The name of the VRF in which to check for the SNMP agent. Defaults to 'default'.\n \"\"\"\n if not vrf:\n self.result.is_skipped(f\"{self.__class__.name} did not run because vrf was not supplied\")\n else:\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if command_output[\"enabled\"] and vrf in command_output[\"vrfs\"][\"snmpVrfs\"]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"SNMP agent disabled in vrf {vrf}\")\n
"},{"location":"api/tests.software/","title":"Software","text":""},{"location":"api/tests.software/#anta-catalog-for-software-tests","title":"ANTA catalog for software tests","text":"Test functions related to the EOS software
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSExtensions","title":"VerifyEOSExtensions
","text":" Bases: AntaTest
Verifies all EOS extensions installed on the device are enabled for boot persistence.
Source code in anta/tests/software.py
class VerifyEOSExtensions(AntaTest):\n\"\"\"\n Verifies all EOS extensions installed on the device are enabled for boot persistence.\n \"\"\"\n\n name = \"VerifyEOSExtensions\"\n description = \"Verifies all EOS extensions installed on the device are enabled for boot persistence.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show extensions\"), AntaTestCommand(command=\"show boot-extensions\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyEOSExtensions validation\"\"\"\n\n boot_extensions = []\n\n show_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n show_boot_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[1].output)\n\n installed_extensions = [\n extension for extension, extension_data in show_extensions_command_output[\"extensions\"].items() if extension_data[\"status\"] == \"installed\"\n ]\n\n for extension in show_boot_extensions_command_output[\"extensions\"]:\n extension = extension.strip(\"\\n\")\n if extension != \"\":\n boot_extensions.append(extension)\n\n installed_extensions.sort()\n boot_extensions.sort()\n if installed_extensions == boot_extensions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Missing EOS extensions: installed {installed_extensions} / configured: {boot_extensions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSExtensions.test","title":"test()
","text":"Run VerifyEOSExtensions validation
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyEOSExtensions validation\"\"\"\n\n boot_extensions = []\n\n show_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n show_boot_extensions_command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[1].output)\n\n installed_extensions = [\n extension for extension, extension_data in show_extensions_command_output[\"extensions\"].items() if extension_data[\"status\"] == \"installed\"\n ]\n\n for extension in show_boot_extensions_command_output[\"extensions\"]:\n extension = extension.strip(\"\\n\")\n if extension != \"\":\n boot_extensions.append(extension)\n\n installed_extensions.sort()\n boot_extensions.sort()\n if installed_extensions == boot_extensions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Missing EOS extensions: installed {installed_extensions} / configured: {boot_extensions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSVersion","title":"VerifyEOSVersion
","text":" Bases: AntaTest
Verifies the device is running one of the allowed EOS version.
Source code in anta/tests/software.py
class VerifyEOSVersion(AntaTest):\n\"\"\"\n Verifies the device is running one of the allowed EOS version.\n \"\"\"\n\n name = \"VerifyEOSVersion\"\n description = \"Verifies the device is running one of the allowed EOS version.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show version\")]\n\n @AntaTest.anta_test\n def test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEOSVersion validation\n\n Args:\n versions: List of allowed EOS versions.\n \"\"\"\n if not versions:\n self.result.is_skipped(\"VerifyEOSVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if command_output[\"version\"] in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f'device is running version {command_output[\"version\"]} not in expected versions: {versions}')\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyEOSVersion.test","title":"test(versions=None)
","text":"Run VerifyEOSVersion validation
Parameters:
Name Type Description Default versions
Optional[List[str]]
List of allowed EOS versions.
None
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyEOSVersion validation\n\n Args:\n versions: List of allowed EOS versions.\n \"\"\"\n if not versions:\n self.result.is_skipped(\"VerifyEOSVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if command_output[\"version\"] in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f'device is running version {command_output[\"version\"]} not in expected versions: {versions}')\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyTerminAttrVersion","title":"VerifyTerminAttrVersion
","text":" Bases: AntaTest
Verifies the device is running one of the allowed TerminAttr version.
Source code in anta/tests/software.py
class VerifyTerminAttrVersion(AntaTest):\n\"\"\"\n Verifies the device is running one of the allowed TerminAttr version.\n \"\"\"\n\n name = \"VerifyTerminAttrVersion\"\n description = \"Verifies the device is running one of the allowed TerminAttr version.\"\n categories = [\"software\"]\n commands = [AntaTestCommand(command=\"show version detail\")]\n\n @AntaTest.anta_test\n def test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTerminAttrVersion validation\n\n Args:\n versions: List of allowed TerminAttr versions.\n \"\"\"\n\n if not versions:\n self.result.is_skipped(\"VerifyTerminAttrVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n command_output_data = command_output[\"details\"][\"packages\"][\"TerminAttr-core\"][\"version\"]\n if command_output_data in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device is running TerminAttr version {command_output_data} and is not in the allowed list: {versions}\")\n
"},{"location":"api/tests.software/#anta.tests.software.VerifyTerminAttrVersion.test","title":"test(versions=None)
","text":"Run VerifyTerminAttrVersion validation
Parameters:
Name Type Description Default versions
Optional[List[str]]
List of allowed TerminAttr versions.
None
Source code in anta/tests/software.py
@AntaTest.anta_test\ndef test(self, versions: Optional[List[str]] = None) -> None:\n\"\"\"\n Run VerifyTerminAttrVersion validation\n\n Args:\n versions: List of allowed TerminAttr versions.\n \"\"\"\n\n if not versions:\n self.result.is_skipped(\"VerifyTerminAttrVersion was not run as no versions were given\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n command_output_data = command_output[\"details\"][\"packages\"][\"TerminAttr-core\"][\"version\"]\n if command_output_data in versions:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device is running TerminAttr version {command_output_data} and is not in the allowed list: {versions}\")\n
"},{"location":"api/tests.stp/","title":"STP","text":""},{"location":"api/tests.stp/#anta-catalog-for-stp-tests","title":"ANTA catalog for STP tests","text":"Test functions related to various Spanning Tree Protocol (STP) settings
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPBlockedPorts","title":"VerifySTPBlockedPorts
","text":" Bases: AntaTest
Verifies there is no STP blocked ports.
Expected Results - success: The test will pass if there are NO ports blocked by STP.
- failure: The test will fail if there are ports blocked by STP.
Source code in anta/tests/stp.py
class VerifySTPBlockedPorts(AntaTest):\n\"\"\"\n Verifies there is no STP blocked ports.\n\n Expected Results:\n * success: The test will pass if there are NO ports blocked by STP.\n * failure: The test will fail if there are ports blocked by STP.\n \"\"\"\n\n name = \"VerifySTPBlockedPorts\"\n description = \"Verifies there is no STP blocked ports.\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree blockedports\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"spanningTreeInstances\"]):\n self.result.is_success()\n else:\n for key, value in stp_instances.items():\n stp_instances[key] = value.pop(\"spanningTreeBlockedPorts\")\n self.result.is_failure(f\"The following ports are blocked by STP: {stp_instances}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPBlockedPorts.test","title":"test()
","text":"Run VerifySTPBlockedPorts validation
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"spanningTreeInstances\"]):\n self.result.is_success()\n else:\n for key, value in stp_instances.items():\n stp_instances[key] = value.pop(\"spanningTreeBlockedPorts\")\n self.result.is_failure(f\"The following ports are blocked by STP: {stp_instances}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPCounters","title":"VerifySTPCounters
","text":" Bases: AntaTest
Verifies there is no errors in STP BPDU packets.
Expected Results - success: The test will pass if there are NO STP BPDU packet errors under all interfaces participating in STP.
- failure: The test will fail if there are STP BPDU packet errors on one or many interface(s).
Source code in anta/tests/stp.py
class VerifySTPCounters(AntaTest):\n\"\"\"\n Verifies there is no errors in STP BPDU packets.\n\n Expected Results:\n * success: The test will pass if there are NO STP BPDU packet errors under all interfaces participating in STP.\n * failure: The test will fail if there are STP BPDU packet errors on one or many interface(s).\n \"\"\"\n\n name = \"VerifySTPCounters\"\n description = \"Verifies there is no errors in STP BPDU packets.\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree counters\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n interfaces_with_errors = [\n interface for interface, counters in command_output[\"interfaces\"].items() if counters[\"bpduTaggedError\"] or counters[\"bpduOtherError\"] != 0\n ]\n\n if interfaces_with_errors:\n self.result.is_failure(f\"The following interfaces have STP BPDU packet errors: {interfaces_with_errors}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPCounters.test","title":"test()
","text":"Run VerifySTPBlockedPorts validation
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPBlockedPorts validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n interfaces_with_errors = [\n interface for interface, counters in command_output[\"interfaces\"].items() if counters[\"bpduTaggedError\"] or counters[\"bpduOtherError\"] != 0\n ]\n\n if interfaces_with_errors:\n self.result.is_failure(f\"The following interfaces have STP BPDU packet errors: {interfaces_with_errors}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPForwardingPorts","title":"VerifySTPForwardingPorts
","text":" Bases: AntaTest
Verifies that all interfaces are in a forwarding state for a provided list of VLAN(s).
Expected Results - success: The test will pass if all interfaces are in a forwarding state for the specified VLAN(s).
- failure: The test will fail if one or many interfaces are NOT in a forwarding state in the specified VLAN(s).
- error: The test will give an error if a list of VLAN(s) is not provided as template_params.
Source code in anta/tests/stp.py
class VerifySTPForwardingPorts(AntaTest):\n\"\"\"\n Verifies that all interfaces are in a forwarding state for a provided list of VLAN(s).\n\n Expected Results:\n * success: The test will pass if all interfaces are in a forwarding state for the specified VLAN(s).\n * failure: The test will fail if one or many interfaces are NOT in a forwarding state in the specified VLAN(s).\n * error: The test will give an error if a list of VLAN(s) is not provided as template_params.\n \"\"\"\n\n name = \"VerifySTPForwardingPorts\"\n description = \"Verifies that all interfaces are forwarding for a provided list of VLAN(s).\"\n categories = [\"stp\"]\n template = AntaTestTemplate(template=\"show spanning-tree topology vlan {vlan} status\")\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySTPForwardingPorts validation.\n \"\"\"\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params)[\"vlan\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (topologies := get_value(command_output, \"topologies\")):\n self.result.is_failure(f\"STP instance for VLAN {vlan_id} is not configured\")\n\n else:\n for value in topologies.values():\n if int(vlan_id) in value[\"vlans\"]:\n interfaces_not_forwarding = [interface for interface, state in value[\"interfaces\"].items() if state[\"state\"] != \"forwarding\"]\n\n if interfaces_not_forwarding:\n self.result.is_failure(f\"The following interface(s) are not in a forwarding state for VLAN {vlan_id}: {interfaces_not_forwarding}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPForwardingPorts.test","title":"test()
","text":"Run VerifySTPForwardingPorts validation.
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySTPForwardingPorts validation.\n \"\"\"\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params)[\"vlan\"]\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (topologies := get_value(command_output, \"topologies\")):\n self.result.is_failure(f\"STP instance for VLAN {vlan_id} is not configured\")\n\n else:\n for value in topologies.values():\n if int(vlan_id) in value[\"vlans\"]:\n interfaces_not_forwarding = [interface for interface, state in value[\"interfaces\"].items() if state[\"state\"] != \"forwarding\"]\n\n if interfaces_not_forwarding:\n self.result.is_failure(f\"The following interface(s) are not in a forwarding state for VLAN {vlan_id}: {interfaces_not_forwarding}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPMode","title":"VerifySTPMode
","text":" Bases: AntaTest
Verifies the configured STP mode for a provided list of VLAN(s).
Expected Results - success: The test will pass if the STP mode is configured properly in the specified VLAN(s).
- failure: The test will fail if the STP mode is NOT configured properly for one or more specified VLAN(s).
- skipped: The test will be skipped if the STP mode is not provided.
- error: The test will give an error if a list of VLAN(s) is not provided as template_params.
Source code in anta/tests/stp.py
class VerifySTPMode(AntaTest):\n\"\"\"\n Verifies the configured STP mode for a provided list of VLAN(s).\n\n Expected Results:\n * success: The test will pass if the STP mode is configured properly in the specified VLAN(s).\n * failure: The test will fail if the STP mode is NOT configured properly for one or more specified VLAN(s).\n * skipped: The test will be skipped if the STP mode is not provided.\n * error: The test will give an error if a list of VLAN(s) is not provided as template_params.\n \"\"\"\n\n name = \"VerifySTPMode\"\n description = \"Verifies the configured STP mode for a provided list of VLAN(s).\"\n categories = [\"stp\"]\n template = AntaTestTemplate(template=\"show spanning-tree vlan {vlan}\")\n\n @staticmethod\n def _check_stp_mode(mode: str) -> None:\n\"\"\"\n Verifies if the provided STP mode is compatible with Arista EOS devices.\n\n Args:\n mode: The STP mode to verify.\n \"\"\"\n stp_modes = [\"mstp\", \"rstp\", \"rapidPvst\"]\n\n if mode not in stp_modes:\n raise ValueError(f\"Wrong STP mode provided. Valid modes are: {stp_modes}\")\n\n @AntaTest.anta_test\n def test(self, mode: str = \"mstp\") -> None:\n\"\"\"\n Run VerifySTPVersion validation.\n\n Args:\n mode: STP mode to verify. Defaults to 'mstp'.\n \"\"\"\n if not mode:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mode was not supplied\")\n return\n\n self._check_stp_mode(mode)\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params).get(\"vlan\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (stp_mode := get_value(command_output, f\"spanningTreeVlanInstances.{vlan_id}.spanningTreeVlanInstance.protocol\")):\n self.result.is_failure(f\"STP mode '{mode}' not configured for VLAN {vlan_id}\")\n\n elif stp_mode != mode:\n self.result.is_failure(f\"Wrong STP mode configured for VLAN {vlan_id}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPMode.test","title":"test(mode='mstp')
","text":"Run VerifySTPVersion validation.
Parameters:
Name Type Description Default mode
str
STP mode to verify. Defaults to \u2018mstp\u2019.
'mstp'
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self, mode: str = \"mstp\") -> None:\n\"\"\"\n Run VerifySTPVersion validation.\n\n Args:\n mode: STP mode to verify. Defaults to 'mstp'.\n \"\"\"\n if not mode:\n self.result.is_skipped(f\"{self.__class__.name} did not run because mode was not supplied\")\n return\n\n self._check_stp_mode(mode)\n\n self.result.is_success()\n\n for index, command in enumerate(self.instance_commands):\n vlan_id = cast(Dict[str, str], command.template_params).get(\"vlan\")\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[index].output)\n\n if not (stp_mode := get_value(command_output, f\"spanningTreeVlanInstances.{vlan_id}.spanningTreeVlanInstance.protocol\")):\n self.result.is_failure(f\"STP mode '{mode}' not configured for VLAN {vlan_id}\")\n\n elif stp_mode != mode:\n self.result.is_failure(f\"Wrong STP mode configured for VLAN {vlan_id}\")\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPRootPriority","title":"VerifySTPRootPriority
","text":" Bases: AntaTest
Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).
Expected Results - success: The test will pass if the STP root priority is configured properly for the specified VLAN or MST instance ID(s).
- failure: The test will fail if the STP root priority is NOT configured properly for the specified VLAN or MST instance ID(s).
- skipped: The test will be skipped if the STP root priority is not provided.
Source code in anta/tests/stp.py
class VerifySTPRootPriority(AntaTest):\n\"\"\"\n Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).\n\n Expected Results:\n * success: The test will pass if the STP root priority is configured properly for the specified VLAN or MST instance ID(s).\n * failure: The test will fail if the STP root priority is NOT configured properly for the specified VLAN or MST instance ID(s).\n * skipped: The test will be skipped if the STP root priority is not provided.\n \"\"\"\n\n name = \"VerifySTPRootPriority\"\n description = \"Verifies the STP root priority for a provided list of VLAN or MST instance ID(s).\"\n categories = [\"stp\"]\n commands = [AntaTestCommand(command=\"show spanning-tree root detail\")]\n\n @AntaTest.anta_test\n def test(self, priority: Optional[int] = None, instances: Optional[List[int]] = None) -> None:\n\"\"\"\n Run VerifySTPRootPriority validation.\n\n Args:\n priority: STP root priority to verify.\n instances: List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.\n \"\"\"\n if not priority:\n self.result.is_skipped(f\"{self.__class__.name} did not run because priority was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"instances\"]):\n self.result.is_failure(\"No STP instances configured\")\n return\n\n for instance in stp_instances:\n if instance.startswith(\"MST\"):\n prefix = \"MST\"\n break\n if instance.startswith(\"VL\"):\n prefix = \"VL\"\n break\n\n check_instances = [f\"{prefix}{instance_id}\" for instance_id in instances] if instances else command_output[\"instances\"].keys()\n\n wrong_priority_instances = [instance for instance in check_instances if get_value(command_output, f\"instances.{instance}.rootBridge.priority\") != priority]\n\n if wrong_priority_instances:\n self.result.is_failure(f\"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.stp/#anta.tests.stp.VerifySTPRootPriority.test","title":"test(priority=None, instances=None)
","text":"Run VerifySTPRootPriority validation.
Parameters:
Name Type Description Default priority
Optional[int]
STP root priority to verify.
None
instances
Optional[List[int]]
List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.
None
Source code in anta/tests/stp.py
@AntaTest.anta_test\ndef test(self, priority: Optional[int] = None, instances: Optional[List[int]] = None) -> None:\n\"\"\"\n Run VerifySTPRootPriority validation.\n\n Args:\n priority: STP root priority to verify.\n instances: List of VLAN or MST instance ID(s). By default, ALL VLAN or MST instance ID(s) will be verified.\n \"\"\"\n if not priority:\n self.result.is_skipped(f\"{self.__class__.name} did not run because priority was not supplied\")\n return\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (stp_instances := command_output[\"instances\"]):\n self.result.is_failure(\"No STP instances configured\")\n return\n\n for instance in stp_instances:\n if instance.startswith(\"MST\"):\n prefix = \"MST\"\n break\n if instance.startswith(\"VL\"):\n prefix = \"VL\"\n break\n\n check_instances = [f\"{prefix}{instance_id}\" for instance_id in instances] if instances else command_output[\"instances\"].keys()\n\n wrong_priority_instances = [instance for instance in check_instances if get_value(command_output, f\"instances.{instance}.rootBridge.priority\") != priority]\n\n if wrong_priority_instances:\n self.result.is_failure(f\"The following instance(s) have the wrong STP root priority configured: {wrong_priority_instances}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.system/","title":"System","text":""},{"location":"api/tests.system/#anta-catalog-for-system-tests","title":"ANTA catalog for system tests","text":"Test functions related to system-level features and protocols
"},{"location":"api/tests.system/#anta.tests.system.VerifyAgentLogs","title":"VerifyAgentLogs
","text":" Bases: AntaTest
Verifies there is no agent crash reported on the device.
Source code in anta/tests/system.py
class VerifyAgentLogs(AntaTest):\n\"\"\"\n Verifies there is no agent crash reported on the device.\n \"\"\"\n\n name = \"VerifyAgentLogs\"\n description = \"Verifies there is no agent crash reported on the device.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show agent logs crash\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyAgentLogs validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported some agent crashes: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyAgentLogs.test","title":"test()
","text":"Run VerifyAgentLogs validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyAgentLogs validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported some agent crashes: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCPUUtilization","title":"VerifyCPUUtilization
","text":" Bases: AntaTest
Verifies the CPU utilization is less than 75%.
Source code in anta/tests/system.py
class VerifyCPUUtilization(AntaTest):\n\"\"\"\n Verifies the CPU utilization is less than 75%.\n \"\"\"\n\n name = \"VerifyCPUUtilization\"\n description = \"Verifies the CPU utilization is less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show processes top once\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyCPUUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n command_output_data = command_output[\"cpuInfo\"][\"%Cpu(s)\"][\"idle\"]\n\n if command_output_data > 25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported a high CPU utilization ({100 - command_output_data}%)\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCPUUtilization.test","title":"test()
","text":"Run VerifyCPUUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyCPUUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n command_output_data = command_output[\"cpuInfo\"][\"%Cpu(s)\"][\"idle\"]\n\n if command_output_data > 25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device reported a high CPU utilization ({100 - command_output_data}%)\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCoredump","title":"VerifyCoredump
","text":" Bases: AntaTest
Verifies there is no core file.
Source code in anta/tests/system.py
class VerifyCoredump(AntaTest):\n\"\"\"\n Verifies there is no core file.\n \"\"\"\n\n name = \"VerifyCoredump\"\n description = \"Verifies there is no core file.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"bash timeout 10 ls /var/core\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyCoredump validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Core-dump(s) have been found: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyCoredump.test","title":"test()
","text":"Run VerifyCoredump validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyCoredump validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Core-dump(s) have been found: {command_output}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyFileSystemUtilization","title":"VerifyFileSystemUtilization
","text":" Bases: AntaTest
Verifies each partition on the disk is used less than 75%.
Source code in anta/tests/system.py
class VerifyFileSystemUtilization(AntaTest):\n\"\"\"\n Verifies each partition on the disk is used less than 75%.\n \"\"\"\n\n name = \"VerifyFileSystemUtilization\"\n description = \"Verifies each partition on the disk is used less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"bash timeout 10 df -h\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyFileSystemUtilization validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n self.result.is_success()\n\n for line in command_output.split(\"\\n\")[1:]:\n if \"loop\" not in line and len(line) > 0 and (percentage := int(line.split()[4].replace(\"%\", \"\"))) > 75:\n self.result.is_failure(f\"mount point {line} is higher than 75% (reported {percentage})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyFileSystemUtilization.test","title":"test()
","text":"Run VerifyFileSystemUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyFileSystemUtilization validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n self.result.is_success()\n\n for line in command_output.split(\"\\n\")[1:]:\n if \"loop\" not in line and len(line) > 0 and (percentage := int(line.split()[4].replace(\"%\", \"\"))) > 75:\n self.result.is_failure(f\"mount point {line} is higher than 75% (reported {percentage})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyMemoryUtilization","title":"VerifyMemoryUtilization
","text":" Bases: AntaTest
Verifies the Memory utilization is less than 75%.
Source code in anta/tests/system.py
class VerifyMemoryUtilization(AntaTest):\n\"\"\"\n Verifies the Memory utilization is less than 75%.\n \"\"\"\n\n name = \"VerifyMemoryUtilization\"\n description = \"Verifies the Memory utilization is less than 75%.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show version\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyMemoryUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n memory_usage = float(cast(float, command_output[\"memFree\"])) / float(cast(float, command_output[\"memTotal\"]))\n if memory_usage > 0.25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device report a high memory usage: {(1 - memory_usage)*100:.2f}%\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyMemoryUtilization.test","title":"test()
","text":"Run VerifyMemoryUtilization validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyMemoryUtilization validation\n \"\"\"\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n memory_usage = float(cast(float, command_output[\"memFree\"])) / float(cast(float, command_output[\"memTotal\"]))\n if memory_usage > 0.25:\n self.result.is_success()\n else:\n self.result.is_failure(f\"device report a high memory usage: {(1 - memory_usage)*100:.2f}%\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyNTP","title":"VerifyNTP
","text":" Bases: AntaTest
Verifies NTP is synchronised.
Source code in anta/tests/system.py
class VerifyNTP(AntaTest):\n\"\"\"\n Verifies NTP is synchronised.\n \"\"\"\n\n name = \"VerifyNTP\"\n description = \"Verifies NTP is synchronised.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show ntp status\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyNTP validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if command_output.split(\"\\n\")[0].split(\" \")[0] == \"synchronised\":\n self.result.is_success()\n else:\n data = command_output.split(\"\\n\")[0]\n self.result.is_failure(f\"not sync with NTP server ({data})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyNTP.test","title":"test()
","text":"Run VerifyNTP validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyNTP validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if command_output.split(\"\\n\")[0].split(\" \")[0] == \"synchronised\":\n self.result.is_success()\n else:\n data = command_output.split(\"\\n\")[0]\n self.result.is_failure(f\"not sync with NTP server ({data})\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyReloadCause","title":"VerifyReloadCause
","text":" Bases: AntaTest
Verifies the last reload of the device was requested by a user.
Test considers the following messages as normal and will return success. Failure is for other messages * Reload requested by the user. * Reload requested after FPGA upgrade
Source code in anta/tests/system.py
class VerifyReloadCause(AntaTest):\n\"\"\"\n Verifies the last reload of the device was requested by a user.\n\n Test considers the following messages as normal and will return success. Failure is for other messages\n * Reload requested by the user.\n * Reload requested after FPGA upgrade\n \"\"\"\n\n name = \"VerifyReloadCause\"\n description = \"Verifies the device uptime is higher than a value.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show reload cause\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifyReloadCause validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if \"resetCauses\" not in command_output.keys():\n self.result.is_error(\"no reload cause available\")\n return\n\n if len(command_output[\"resetCauses\"]) == 0:\n # No reload causes\n self.result.is_success()\n return\n\n reset_causes = cast(List[Dict[str, Any]], command_output[\"resetCauses\"])\n command_output_data = reset_causes[0].get(\"description\")\n if command_output_data in [\n \"Reload requested by the user.\",\n \"Reload requested after FPGA upgrade\",\n ]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Reload cause is {command_output_data}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyReloadCause.test","title":"test()
","text":"Run VerifyReloadCause validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifyReloadCause validation\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if \"resetCauses\" not in command_output.keys():\n self.result.is_error(\"no reload cause available\")\n return\n\n if len(command_output[\"resetCauses\"]) == 0:\n # No reload causes\n self.result.is_success()\n return\n\n reset_causes = cast(List[Dict[str, Any]], command_output[\"resetCauses\"])\n command_output_data = reset_causes[0].get(\"description\")\n if command_output_data in [\n \"Reload requested by the user.\",\n \"Reload requested after FPGA upgrade\",\n ]:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Reload cause is {command_output_data}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifySyslog","title":"VerifySyslog
","text":" Bases: AntaTest
Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.
Source code in anta/tests/system.py
class VerifySyslog(AntaTest):\n\"\"\"\n Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.\n \"\"\"\n\n name = \"VerifySyslog\"\n description = \"Verifies the device had no syslog message with a severity of warning (or a more severe message) during the last 7 days.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show logging last 7 days threshold warnings\", ofmt=\"text\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"\n Run VerifySyslog validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(\"Device has some log messages with a severity WARNING or higher\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifySyslog.test","title":"test()
","text":"Run VerifySyslog validation
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"\n Run VerifySyslog validation\n \"\"\"\n command_output = cast(str, self.instance_commands[0].output)\n\n if len(command_output) == 0:\n self.result.is_success()\n else:\n self.result.is_failure(\"Device has some log messages with a severity WARNING or higher\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyUptime","title":"VerifyUptime
","text":" Bases: AntaTest
Verifies the device uptime is higher than a value.
Source code in anta/tests/system.py
class VerifyUptime(AntaTest):\n\"\"\"\n Verifies the device uptime is higher than a value.\n \"\"\"\n\n name = \"VerifyUptime\"\n description = \"Verifies the device uptime is higher than a value.\"\n categories = [\"system\"]\n commands = [AntaTestCommand(command=\"show uptime\")]\n\n @AntaTest.anta_test\n def test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyUptime validation\n\n Args:\n minimum: Minimum uptime in seconds.\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (isinstance(minimum, (int, float))) or minimum < 0:\n self.result.is_skipped(\"VerifyUptime was not run as incorrect minimum uptime was given\")\n return\n\n if cast(float, command_output[\"upTime\"]) > minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Uptime is {command_output['upTime']}\")\n
"},{"location":"api/tests.system/#anta.tests.system.VerifyUptime.test","title":"test(minimum=None)
","text":"Run VerifyUptime validation
Parameters:
Name Type Description Default minimum
Optional[int]
Minimum uptime in seconds.
None
Source code in anta/tests/system.py
@AntaTest.anta_test\ndef test(self, minimum: Optional[int] = None) -> None:\n\"\"\"\n Run VerifyUptime validation\n\n Args:\n minimum: Minimum uptime in seconds.\n \"\"\"\n\n command_output = cast(Dict[str, Dict[Any, Any]], self.instance_commands[0].output)\n\n if not (isinstance(minimum, (int, float))) or minimum < 0:\n self.result.is_skipped(\"VerifyUptime was not run as incorrect minimum uptime was given\")\n return\n\n if cast(float, command_output[\"upTime\"]) > minimum:\n self.result.is_success()\n else:\n self.result.is_failure(f\"Uptime is {command_output['upTime']}\")\n
"},{"location":"api/tests.vxlan/","title":"VxLAN","text":""},{"location":"api/tests.vxlan/#anta-catalog-for-vxlan-tests","title":"ANTA catalog for VxLAN tests","text":"Test functions related to VXLAN
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlan","title":"VerifyVxlan
","text":" Bases: AntaTest
Verifies if Vxlan1 interface is configured, and is up/up
Source code in anta/tests/vxlan.py
class VerifyVxlan(AntaTest):\n\"\"\"\n Verifies if Vxlan1 interface is configured, and is up/up\n \"\"\"\n\n name = \"VerifyVxlan\"\n description = \"Verifies Vxlan1 status\"\n categories = [\"vxlan\"]\n commands = [AntaTestCommand(command=\"show interfaces description\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyVxlan validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"Vxlan1\" not in command_output[\"interfaceDescriptions\"]:\n self.result.is_skipped(\"Vxlan1 interface is not configured\")\n elif (\n command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"lineProtocolStatus\"] == \"up\"\n and command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"interfaceStatus\"] == \"up\"\n ):\n self.result.is_success()\n else:\n self.result.is_failure(\n f\"Vxlan1 interface is {command_output['interfaceDescriptions']['Vxlan1']['lineProtocolStatus']}\"\n f\"/{command_output['interfaceDescriptions']['Vxlan1']['interfaceStatus']}\"\n )\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlan.test","title":"test()
","text":"Run VerifyVxlan validation
Source code in anta/tests/vxlan.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyVxlan validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"Vxlan1\" not in command_output[\"interfaceDescriptions\"]:\n self.result.is_skipped(\"Vxlan1 interface is not configured\")\n elif (\n command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"lineProtocolStatus\"] == \"up\"\n and command_output[\"interfaceDescriptions\"][\"Vxlan1\"][\"interfaceStatus\"] == \"up\"\n ):\n self.result.is_success()\n else:\n self.result.is_failure(\n f\"Vxlan1 interface is {command_output['interfaceDescriptions']['Vxlan1']['lineProtocolStatus']}\"\n f\"/{command_output['interfaceDescriptions']['Vxlan1']['interfaceStatus']}\"\n )\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlanConfigSanity","title":"VerifyVxlanConfigSanity
","text":" Bases: AntaTest
Verifies that there are no VXLAN config-sanity issues flagged
Source code in anta/tests/vxlan.py
class VerifyVxlanConfigSanity(AntaTest):\n\"\"\"\n Verifies that there are no VXLAN config-sanity issues flagged\n \"\"\"\n\n name = \"VerifyVxlanConfigSanity\"\n description = \"Verifies VXLAN config-sanity\"\n categories = [\"vxlan\"]\n commands = [AntaTestCommand(command=\"show vxlan config-sanity\", ofmt=\"json\")]\n\n @AntaTest.anta_test\n def test(self) -> None:\n\"\"\"Run VerifyVxlanConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"categories\" not in command_output or len(command_output[\"categories\"]) == 0:\n self.result.is_skipped(\"VXLAN is not configured on this device\")\n return\n\n failed_categories = {\n category: content\n for category, content in command_output[\"categories\"].items()\n if category in [\"localVtep\", \"mlag\", \"pd\"] and content[\"allCheckPass\"] is not True\n }\n\n if len(failed_categories) > 0:\n self.result.is_failure(f\"Vxlan config sanity check is not passing: {failed_categories}\")\n else:\n self.result.is_success()\n
"},{"location":"api/tests.vxlan/#anta.tests.vxlan.VerifyVxlanConfigSanity.test","title":"test()
","text":"Run VerifyVxlanConfigSanity validation
Source code in anta/tests/vxlan.py
@AntaTest.anta_test\ndef test(self) -> None:\n\"\"\"Run VerifyVxlanConfigSanity validation\"\"\"\n\n command_output = cast(Dict[str, Dict[str, Any]], self.instance_commands[0].output)\n\n if \"categories\" not in command_output or len(command_output[\"categories\"]) == 0:\n self.result.is_skipped(\"VXLAN is not configured on this device\")\n return\n\n failed_categories = {\n category: content\n for category, content in command_output[\"categories\"].items()\n if category in [\"localVtep\", \"mlag\", \"pd\"] and content[\"allCheckPass\"] is not True\n }\n\n if len(failed_categories) > 0:\n self.result.is_failure(f\"Vxlan config sanity check is not passing: {failed_categories}\")\n else:\n self.result.is_success()\n
"},{"location":"cli/debug/","title":"Helpers","text":""},{"location":"cli/debug/#anta-debug-commands","title":"ANTA debug commands","text":"ANTA CLI also provides a set of entrypoints to help building ANTA content. We call it debug and it provides different options:
- Run a command on a device from your inventory and expose a result from AntaTestCommand
- Run a templated command and expose the result
Both are extremly useful to build your test since you have a visual access to the output you have to test. It also helps to extract content to use for unit test as descirbed in our contribution guide.
Use your inventory
Because it is based on ANTA cli, all your commands use an ANTA inventory and require to get a valid one.
"},{"location":"cli/debug/#get-result-of-an-eos-command","title":"Get result of an EOS command","text":"To run a command, you can leverage run-cmd
entrypoint with following options:
$ anta debug run-cmd --help\nUsage: anta debug run-cmd [OPTIONS]\n\nRun arbitrary command to an EOS device and get result using eAPI\n\nOptions:\n -c, --command TEXT Command to run on EOS using eAPI [required]\n--ofmt [text|json] eAPI format to use. can be text or json\n --api-version, --version TEXT Version of the command through eAPI\n -d, --device TEXT Device from inventory to use [required]\n--log-level, --log TEXT Logging level of the command\n--help Show this message and exit.\n
In practice, this command is very simple to use. Here is an example using show interfaces description
with a JSON
format:
anta debug run-cmd -c \"show interfaces description\" --device ptt015\nrun command show interfaces description on ptt015\n{\n'interfaceDescriptions': {\n'Ethernet8': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n 'Ethernet9': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n 'Ethernet12': {'interfaceStatus': 'adminDown', 'description': '', 'lineProtocolStatus': 'down'},\n ...\n}\n
"},{"location":"cli/debug/#get-result-of-an-eos-command-using-templates","title":"Get result of an EOS command using templates","text":"This command allows user to provide an f-string
and a list of dictionary to run a command dynamically. Idea is to help building output for test using such approach.
$ anta debug run-template --help\nUsage: anta debug run-template [OPTIONS]\n\nRun arbitrary command to an EOS device and get result using eAPI\n\nOptions:\n -t, --template TEXT Command template to run on EOS using eAPI\n -p, --params TEXT Command parameters to use with template. Must\n be a JSON string for a list of dict\n [required]\n--ofmt [text|json] eAPI format to use. can be text or json\n --api-version, --version TEXT Version of the command through eAPI\n -d, --device TEXT Device from inventory to use [required]\n--log-level, --log TEXT Logging level of the command\n--help Show this message and exit.\n
In practice, this command is very simple to use. Here is an example using show lldp neighbors
with a JSON
format for only 2 interfaces: Ethernet1 and Ethernet2
anta debug run-template \\\n--params \"[{\"ifd\": \"Ethernet1\"}, {\"ifd\":\"Ethernet2\"}]\" \\\n--template \"show lldp neighbors {ifd}\" \\\n--device ptt015\n\nrun dynmic command show lldp neighbors {ifd} with [{\"ifd\": \"Ethernet1\"}, {\"ifd\":\"Ethernet2\"}] on ptt015\nrun_command = show lldp neighbors Ethernet1 ptt015\n{\n\"tablesLastChangeTime\": 1682498936.0082116,\n \"tablesAgeOuts\": 0,\n \"tablesInserts\": 1,\n \"lldpNeighbors\": [],\n \"tablesDeletes\": 0,\n \"tablesDrops\": 0\n}\nrun_command = show lldp neighbors Ethernet2 ptt015\n{\n\"tablesLastChangeTime\": 1682498936.008321,\n \"tablesAgeOuts\": 0,\n \"tablesInserts\": 1,\n \"lldpNeighbors\": [],\n \"tablesDeletes\": 0,\n \"tablesDrops\": 0\n}\n
"},{"location":"cli/exec/","title":"Execute commands","text":""},{"location":"cli/exec/#execute-commands-on-devices","title":"Execute commands on devices","text":"ANTA CLI also provides a set of entrypoints to execute commands remotely on EOS devices.
"},{"location":"cli/exec/#clear-interfaces-counters","title":"Clear interfaces counters","text":"This command clear interfaces counters on EOS devices defined in your inventory
$ anta exec clear-counters --help\nUsage: anta exec clear-counters [OPTIONS]\n\nClear counter statistics on EOS devices\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
"},{"location":"cli/exec/#collect-a-set-of-commands","title":"Collect a set of commands","text":"This command collects all commands you defined in a catalog. It can be either json
or text
.
$ anta exec snapshot --help\nUsage: anta exec snapshot [OPTIONS]\n\nCollect commands output from devices in inventory\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n -c, --commands-list PATH File with list of commands to grab [env\n var: ANTA_EXEC_SNAPSHOT_COMMANDS_LIST]\n-outut, -o, --output-directory PATH\n Path where to save commands output [env\n var: ANTA_EXEC_SNAPSHOT_OUTPUT_DIRECTORY]\n--log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
And structure of your catalog file should be:
json_format:\n- show version\ntext_format:\n- show agent logs crash\n- show bfd peers\n- show bgp evpn\n
"},{"location":"cli/exec/#get-scheduled-tech-support","title":"Get Scheduled tech-support","text":"EOS comes with a feature that generates tech-support archive every 1 hour by default and save this archive under /mnt/flash/schedule/tech-support
leaf1#show schedule summary\nMaximum concurrent jobs 1\nPrepend host name to logfile: Yes\nName At Time Last Interval Timeout Max Max Logfile Location Status\n Time (mins) (mins) Log Logs\n Files Size\n----------------- ------------- ----------- -------------- ------------- ----------- ---------- --------------------------------- ------\ntech-support now 08:37 60 30 100 - flash:schedule/tech-support/ Success\n\n\nleaf1#bash ls /mnt/flash/schedule/tech-support\nleaf1_tech-support_2023-03-09.1337.log.gz leaf1_tech-support_2023-03-10.0837.log.gz leaf1_tech-support_2023-03-11.0337.log.gz ...\n
As it can be useful for an NRFU to save a very complete state report before a go live, ANTA has implemented a CLI that retrieves these files very easily:
\u276f anta exec collect-tech-support --help\n\nUsage: anta exec collect-tech-support [OPTIONS]\n\nCollect scheduled tech-support from eos devices.\n\nOptions:\n -o, --output PATH Path for tests catalog [default: ./tech-\n support]\n-ssh, --ssh-port INTEGER SSH port to use for connection [default:\n 22]\n--insecure / --secure Disable SSH Host Key validation [default:\n secure]\n--latest INTEGER Number of scheduled show-tech to retrieve\n --configure / --not-configure Ensure device has 'aaa authorization exec\n default local' configured (required for SCP)\n[default: not-configure]\n-t, --tags TEXT List of tags using coma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command [default:\n info]\n--help Show this message and exit.\n
When you run this command, it will retrieve tech-support files and download it locally in a folder and a subfolder per device. You can change the default output folder with the --output
option. ANTA download files from the devices using SCP, all SSH Host Key devices must be trusted prior to run the command, otherwise use the --insecure
option. In order to use SCP with EOS, the configuration aaa authorization exec default local
must be present on the devices. By default, ANTA will not configure this automatically, unless --configure
is specified. It is possible to retrieve only the latest tech-support files using the --latest
option.
\u276f anta exec collect-tech-support --insecure\n[15:27:19] INFO Connecting to devices...\nINFO Copying '/mnt/flash/schedule/tech-support/spine1_tech-support_2023-06-09.1315.log.gz' from device spine1 to 'tech-support/spine1' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf3_tech-support_2023-06-09.1315.log.gz' from device leaf3 to 'tech-support/leaf3' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf1_tech-support_2023-06-09.1315.log.gz' from device leaf1 to 'tech-support/leaf1' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf2_tech-support_2023-06-09.1315.log.gz' from device leaf2 to 'tech-support/leaf2' locally\nINFO Copying '/mnt/flash/schedule/tech-support/spine2_tech-support_2023-06-09.1315.log.gz' from device spine2 to 'tech-support/spine2' locally\nINFO Copying '/mnt/flash/schedule/tech-support/leaf4_tech-support_2023-06-09.1315.log.gz' from device leaf4 to 'tech-support/leaf4' locally\nINFO Collected 1 scheduled tech-support from leaf2\nINFO Collected 1 scheduled tech-support from spine2\nINFO Collected 1 scheduled tech-support from leaf3\nINFO Collected 1 scheduled tech-support from spine1\nINFO Collected 1 scheduled tech-support from leaf1\nINFO Collected 1 scheduled tech-support from leaf4\n
The output folder will look like this:
\u276f tree tech-support/\ntech-support/\n\u251c\u2500\u2500 leaf1\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf1_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf2\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf2_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf3\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf3_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 leaf4\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 leaf4_tech-support_2023-06-09.1315.log.gz\n\u251c\u2500\u2500 spine1\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 spine1_tech-support_2023-06-09.1315.log.gz\n\u2514\u2500\u2500 spine2\n \u2514\u2500\u2500 spine2_tech-support_2023-06-09.1315.log.gz\n\n6 directories, 6 files\n
"},{"location":"cli/get-inventory-information/","title":"Get Inventory Information","text":""},{"location":"cli/get-inventory-information/#get-inventory-information","title":"Get Inventory Information","text":"ANTA CLI provides a set of entrypoints to get data from your local inventory.
"},{"location":"cli/get-inventory-information/#get-all-configured-tags","title":"Get all configured tags","text":"Since most commands in anta support tags filtering, this command helps you list all available tags configured in your inventory.
"},{"location":"cli/get-inventory-information/#command-overview","title":"Command overview","text":"anta get tags --help\nUsage: anta get tags [OPTIONS]\n\nGet list of configured tags in user inventory.\n\nOptions:\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
"},{"location":"cli/get-inventory-information/#example","title":"Example","text":"Let\u2019s consider the following inventory:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
To get the list of all configured tags in your CLI, run the following command:
$ anta get tags\nTags found:\n[\n\"all\",\n \"fabric\",\n \"leaf\",\n \"spine\"\n]\nNone\n\n* note that tag all has been added by anta\n
Tip
As you can see, the tag all
has been added even if not explicitely configued in your inventory. This tag is the default tag added to all your devices to run commands against your inventory when you do not provide any specific tag.
"},{"location":"cli/get-inventory-information/#list-devices-in-inventory","title":"List devices in inventory","text":""},{"location":"cli/get-inventory-information/#command-overview_1","title":"Command overview","text":"To get a list of all devices available in your inventory with ANTA, use the following command
anta get inventory --help\nUsage: anta get inventory [OPTIONS]\n\nShow inventory loaded in ANTA.\n\nOptions:\n -t, --tags TEXT List of tags using comma as separator:\n tag1,tag2,tag3\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--connected / --not-connected Display inventory after connection has been\n created\n --help Show this message and exit.\n
It will give you all information loaded in ANTA inventory from your inventory file.
Tip
By default only information not based on device connection is available. If you want to get information based on connection such as hardware model, you should use the --connected
option.
"},{"location":"cli/get-inventory-information/#example_1","title":"Example","text":"Considering the following inventory file:
anta_inventory:\nhosts:\n- host: 192.168.0.10\nname: spine01\ntags: ['fabric', 'spine']\n- host: 192.168.0.11\nname: spine02\ntags: ['fabric', 'spine']\n- host: 192.168.0.12\nname: leaf01\ntags: ['fabric', 'leaf']\n- host: 192.168.0.13\nname: leaf02\ntags: ['fabric', 'leaf']\n- host: 192.168.0.14\nname: leaf03\ntags: ['fabric', 'leaf']\n- host: 192.168.0.15\nname: leaf04\ntags: ['fabric', 'leaf']\n
You can get ANTA inventory with the command:
$ anta --username ansible --password ansible get inventory --tags spine\nCurrent inventory content is:\n[\n{\n\"name\": \"spine01\",\n \"host\": \"192.168.0.10\",\n \"username\": \"ansible\",\n \"password\": \"ansible\",\n \"port\": \"443\",\n \"enable_password\": \"None\",\n \"session\": \"<aioeapi.device.Device object at 0x7fa98d0a2d30>\",\n \"hw_model\": \"unset\",\n \"tags\": \"['fabric', 'spine', 'all']\",\n \"timeout\": \"10.0\",\n \"established\": \"False\",\n \"is_online\": \"False\"\n},\n {\n\"name\": \"spine02\",\n \"host\": \"192.168.0.11\",\n \"username\": \"ansible\",\n \"password\": \"ansible\",\n \"port\": \"443\",\n \"enable_password\": \"None\",\n \"session\": \"<aioeapi.device.Device object at 0x7fa98d0a2ac0>\",\n \"hw_model\": \"unset\",\n \"tags\": \"['fabric', 'spine', 'all']\",\n \"timeout\": \"10.0\",\n \"established\": \"False\",\n \"is_online\": \"False\"\n}\n]\nNone\n
"},{"location":"cli/inv-from-cvp/","title":"Inventory from CVP","text":""},{"location":"cli/inv-from-cvp/#create-inventory-from-cloudvision","title":"Create inventory from CloudVision","text":"In a large setup, it can be useful to create your inventory based on CloudVision inventory.
$ anta get from-cvp\nUsage: anta get from-cvp [OPTIONS]\n\nBuild ANTA inventory from Cloudvision\n\nOptions:\n -ip, --cvp-ip TEXT CVP IP Address\n -u, --cvp-username TEXT CVP Username\n -p, --cvp-password TEXT CVP Password / token\n -c, --cvp-container TEXT Container where devices are configured\n -d, --inventory-directory PATH Path to save inventory file\n --log-level, --log [debug|info|warning|critical]\nLogging level of the command\n--help Show this message and exit.\n
Output is an inventory with the name of the container added as a tag for the host:
anta_inventory:\nhosts:\n- host: 192.168.0.13\nname: leaf2\ntags:\n- pod1\n- host: 192.168.0.15\nname: leaf4\ntags:\n- pod2\n
Warning
Current implementation only takes devices directly attached to a specific container when using cli with --cvp-container
option.
If you want to build an inventory based on multiple containers, you can use a bash command as shown below and then manually concatenate files to create a single inventory file.
$ for container in pod01 pod02 spines; do anta get from-cvp -ip <cvp-ip> -u cvpadmin -p cvpadmin -c $container -d test-inventory; done\n\n[12:25:35] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:36] INFO Creating inventory folder /home/tom/Projects/arista/network-test-automation/test-inventory\n WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n\n[12:25:37] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:38] WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n\n[12:25:38] INFO Getting auth token from cvp.as73.inetsix.net for user tom\n[12:25:39] WARNING Using the new api_token parameter. This will override usage of the cvaas_token parameter if both are provided. This is because api_token and cvaas_token parameters\n are for the same use case and api_token is more generic\n INFO Connected to CVP cvp.as73.inetsix.net\n\n INFO Inventory file has been created in /home/tom/Projects/arista/network-test-automation/test-inventory/inventory-spines.yml\n
"},{"location":"cli/nrfu/","title":"NRFU","text":""},{"location":"cli/nrfu/#execute-nrfu-testing","title":"Execute NRFU testing","text":"All the NRFU testing commands are placed under anta nrfu
and provide different rendering options:
- Table view
- JSON view
- Text view
- Custom template view
anta nrfu\nUsage: anta nrfu [OPTIONS] COMMAND [ARGS]...\n\n Run NRFU against inventory devices\n\nOptions:\n --help Show this message and exit.\n\nCommands:\n json ANTA command to check network state with JSON result\n table ANTA command to check network states with table result\n text ANTA command to check network states with text result\n tpl-report ANTA command to check network state with templated report\n
All of these commands require the following input:
- A path to a catalog of tests to execute (
--catalog
) - A list of tags if they are part of your inventory (
--tags
). List is comma separated
"},{"location":"cli/nrfu/#nrfu-with-text-rendering","title":"NRFU with text rendering","text":"This rendering is a pure text report for every test run on all devices. It comes with some options:
- Search (
--search
) for a regexp pattern in hostname and test name - Option to skip (
--skip-error
) tests in error (not failure) because of a connectivity issue or unsupported command
Example output
$ anta nrfu text --tags pod1 --catalog nrfu/leaf.yml\nleaf2 :: VerifyMlagStatus :: SUCCESS\nleaf2 :: VerifyMlagInterface :: SUCCESS\nleaf2 :: VerifyMlagConfigSanity :: SUCCESS\nleaf2 :: VerifyInterfaceUtilization :: SUCCESS\nleaf2 :: VerifyInterfaceErrors :: SUCCESS\nleaf2 :: VerifyInterfaceDiscards :: SUCCESS\nleaf2 :: VerifyInterfaceErrDisabled :: SUCCESS\nleaf2 :: VerifyInterfaceStatus :: SUCCESS\nleaf2 :: VerifyStormControlDrop :: SKIPPED (VerifyStormControlDrop test is not supported on cEOSLab.)\nleaf2 :: VerifyPortChannel :: SUCCESS\nleaf2 :: VerifyIllegalLacp :: SUCCESS\nleaf2 :: VerifyLoopbackCount :: FAILURE (Found 3 Loopbacks when expecting 2)\nleaf2 :: VerifySvi :: SUCCESS\n[...]\n
"},{"location":"cli/nrfu/#nrfu-with-table-report","title":"NRFU with table report","text":"This rendering prints results in a nice table supporting grep filtering. It comes with its own set of options:
- Search (
--search
) for a pattern in hostname and test name. - Option to group (
--group-by
) and summarize results. You can group by host
or test
.
$ anta check table -t pod1 -c nrfu/cudi.yml\n
You can also group per host or per test to get a summary view in case of large setup
"},{"location":"cli/nrfu/#nrfu-with-json-output","title":"NRFU with JSON output","text":"This command is helpful to generate a JSON and then pass it to another tool for reporting for instance. Only one option is available to save output to a file (--output
)
$ anta check json -t pod1 -c nrfu/leaf.yml\n[\n{\n\"name\": \"leaf01\",\n \"test\": \"VerifyZeroTouch\",\n \"test_category\": [\n\"configuration\"\n],\n \"test_description\": \"Verifies ZeroTouch is disabled.\",\n \"result\": \"success\",\n \"messages\": []\n},\n {\n\"name\": \"leaf01\",\n \"test\": \"VerifyRunningConfigDiffs\",\n \"test_category\": [\n\"configuration\"\n],\n \"test_description\": \"\",\n \"result\": \"success\",\n \"messages\": []\n},\n]\n
"},{"location":"cli/nrfu/#nrfu-with-your-own-report","title":"NRFU with your own report","text":"Because you may want to have a specific report format, ANTA provides a CLI option to build report based on Jinja2 template.
$ anta nrfu tpl-report -c .personal/catalog-class.yml -tpl .personal/test_template.j2\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/catalog-class.yml \u2502\n\u2502 - Template: .personal/test_template.j2 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n* VerifyZeroTouch is SUCCESS for spine01\n* VerifyRunningConfigDiffs is SUCCESS for spine01\n* VerifyInterfaceUtilization is SUCCESS for spine01\n
And the template .personal/test_template.j2
is a pure Jinja2 template:
$ cat .personal/test_template.j2\n{% for d in data %}\n* {{ d.test }} is [green]{{ d.result | upper}}[/green] for {{ d.name }}\n{% endfor %}\n
In this context, Jinja2 template can access to all TestResult
elements with their values as described in this documentation.
An option is available to save the generated report into a text file:
# Run ANTA\n$ anta nrfu tpl-report -c .personal/catalog-class.yml -tpl .personal/test_template.j2 -o .personal/demo.txt\n\u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Settings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 Running check-devices with: \u2502\n\u2502 - Inventory: .personal/inventory_atd.yml \u2502\n\u2502 - Tests catalog: .personal/catalog-class.yml \u2502\n\u2502 - Template: .personal/test_template.j2 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n* VerifyZeroTouch is SUCCESS for spine01\n* VerifyRunningConfigDiffs is SUCCESS for spine01\n* VerifyInterfaceUtilization is SUCCESS for spine01\n\n# Display saved report\n$ cat .personal/demo.txt\n* VerifyZeroTouch is [green]SUCCESS[/green] for spine01\n* VerifyRunningConfigDiffs is [green]SUCCESS[/green] for spine01\n* VerifyInterfaceUtilization is [green]SUCCESS[/green] for spine01\n
"},{"location":"cli/overview/","title":"Overview","text":""},{"location":"cli/overview/#cli-overview","title":"CLI overview","text":"ANTA comes with a CLI to execute all the supported actions. If you want to build your own tool, you should visit this page where we describe how to use ANTA as a python library
To invoke anta, open a shell window and then enter anta
Usage: anta [OPTIONS] COMMAND [ARGS]...\n\n Arista Network Test CLI\n\nOptions:\n --version Show the version and exit.\n --username TEXT Username to connect to EOS [env var:\n ANTA_USERNAME; required]\n--password TEXT Password to connect to EOS [env var:\n ANTA_PASSWORD; required]\n--timeout INTEGER Global connection timeout [env var:\n ANTA_TIMEOUT; default: 5]\n--insecure / --secure Disable SSH Host Key validation [env var:\n ANTA_INSECURE; default: secure]\n--enable-password TEXT Enable password if required to connect [env\n var: ANTA_ENABLE_PASSWORD]\n-i, --inventory FILE Path to the inventory YAML file [env var:\n ANTA_INVENTORY; required]\n--log-level, --log [CRITICAL|ERROR|WARNING|INFO|DEBUG]\nANTA logging level [env var:\n ANTA_LOG_LEVEL; default: INFO]\n--ignore-status Always exit with success [env var:\n ANTA_IGNORE_STATUS]\n--ignore-error Only report failures and not errors [env\n var: ANTA_IGNORE_ERROR]\n--help Show this message and exit.\n\nCommands:\n debug Debug commands for building ANTA\n exec Execute commands to inventory devices\n get Get data from/to ANTA\n nrfu Run NRFU against inventory devices\n
"},{"location":"cli/overview/#anta-parameters","title":"ANTA parameters","text":"Some parameters are globally required and can be passed to anta via cli or via ENV VAR:
$ anta --username tom --password arista123 --inventory inventory.yml <anta cli>\n
Or if you prefer to set ENV VAR:
# Save information for anta cli\n$ export ANTA_USERNAME=tom\n$ export ANTA_PASSWORD=arista123\n$ export ANTA_INVENTORY=inventory.yml\n\n# Run cli\n$ anta <anta cli>\n
"},{"location":"cli/overview/#anta-exitcodes","title":"ANTA ExitCodes","text":"For all subcommands except nrfu, anta
wil return with the exit code 0.
For the nrfu commands, anta
is using the following exit codes:
- Exit code 0 - All tests passed successfully.
- Exit code 1 - Tests were run but at least one test returned a failure.
- Exit code 2 - Tests were run but at least one test returned an error.
- Exit code 3 - Internal error happened while executing tests (not used today).
It is possible to ignore the status when running the nrfu command by using anta --ignore-status nrfu
and in that case the exit code will always be 0.
It is possible to ignore errors when running the nrfu command by using anta --ignore-error nrfu
and in that case the exit code will either be 0 if all tests succeeded or 1 if any test failed.
"},{"location":"cli/overview/#shell-completion","title":"Shell Completion","text":"You can enable shell completion for the anta cli:
ZSHBASH If you use ZSH shell, add the following line in your ~/.zshrc
:
eval \"$(_ANTA_COMPLETE=zsh_source anta)\" > /dev/null\n
With bash, add the following line in your ~/.bashrc
:
eval \"$(_ANTA_COMPLETE=bash_source anta)\" > /dev/null\n
"}]}
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 100c1e8a1..2d8774cf1 100644
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ