Skip to content

Commit 35f3dfe

Browse files
Rebase with latest main
2 parents a650846 + f525ffb commit 35f3dfe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1574
-243
lines changed

.github/workflows/code-testing.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ jobs:
122122
test-documentation:
123123
name: Build offline documentation for testing
124124
runs-on: ubuntu-20.04
125-
needs: [lint-python, type-python, test-python]
125+
needs: [test-python]
126126
steps:
127127
- uses: actions/checkout@v4
128128
- name: Setup Python

.github/workflows/on-demand.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
password: ${{ secrets.GITHUB_TOKEN }}
4040

4141
- name: Build and push
42-
uses: docker/build-push-action@v5
42+
uses: docker/build-push-action@v6
4343
with:
4444
context: .
4545
file: Dockerfile

.github/workflows/pr-triage.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
# https://github.com/marketplace/actions/auto-author-assign
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: toshimaru/[email protected].0
16+
- uses: toshimaru/[email protected].1
1717
with:
1818
repo-token: "${{ secrets.GITHUB_TOKEN }}"
1919

@@ -22,7 +22,7 @@ jobs:
2222
steps:
2323
# Please look up the latest version from
2424
# https://github.com/amannn/action-semantic-pull-request/releases
25-
- uses: amannn/[email protected].2
25+
- uses: amannn/[email protected].3
2626
env:
2727
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2828
with:

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ jobs:
100100
password: ${{ secrets.GITHUB_TOKEN }}
101101

102102
- name: Build and push
103-
uses: docker/build-push-action@v5
103+
uses: docker/build-push-action@v6
104104
with:
105105
context: .
106106
file: Dockerfile

.github/workflows/secret-scanner.yml

+2-17
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,6 @@ jobs:
1010
scan_secret:
1111
name: Scan incoming changes
1212
runs-on: ubuntu-latest
13-
container:
14-
image: ghcr.io/aristanetworks/secret-scanner-service:main
15-
options: --name sss-scanner
16-
steps:
17-
- name: Checkout ${{ github.ref }}
18-
# Hitting https://github.com/actions/checkout/issues/334 so trying v1
19-
uses: actions/checkout@v1
20-
with:
21-
fetch-depth: 0
13+
steps:
2214
- name: Run scanner
23-
run: |
24-
git config --global --add safe.directory $GITHUB_WORKSPACE
25-
scanner commit . github ${{ github.repository }} \
26-
--markdown-file job_summary.md \
27-
${{ github.event_name == 'pull_request' && format('--since-commit {0}', github.event.pull_request.base.sha) || ''}}
28-
- name: Write result to summary
29-
run: cat ./job_summary.md >> $GITHUB_STEP_SUMMARY
30-
if: ${{ always() }}
15+
uses: aristanetworks/secret-scanner-service-public@main

.github/workflows/sonar.yml

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
name: Analysis with Sonarlint and publish to SonarCloud
3+
on:
4+
push:
5+
branches:
6+
- main
7+
# Need to do this to be able to have coverage on PR across forks.
8+
pull_request_target:
9+
10+
# TODO this can be made better by running only coverage, it happens that today
11+
# in tox gh-actions we have configured 3.11 to run the report side in
12+
# pyproject.toml
13+
14+
jobs:
15+
sonarcloud:
16+
name: Run Sonarlint analysis and upload to SonarCloud.
17+
if: github.repository == 'aristanetworks/anta'
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
ref: ${{ github.event.pull_request.head.sha }}
23+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
24+
- name: Setup Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: 3.11
28+
- name: Install dependencies
29+
run: pip install tox tox-gh-actions
30+
- name: "Run pytest via tox for ${{ matrix.python }}"
31+
run: tox
32+
- name: SonarCloud Scan
33+
uses: SonarSource/sonarcloud-github-action@master
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
37+
with:
38+
# Using ACTION_STEP_DEBUG to trigger verbose when debugging in Github Action
39+
args: >
40+
-Dsonar.scm.revision=${{ github.event.pull_request.head.sha }}
41+
-Dsonar.pullrequest.key=${{ github.event.number }}
42+
-Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }}
43+
-Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }}
44+
-Dsonar.verbose=${{ secrets.ACTIONS_STEP_DEBUG }}

.pre-commit-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ repos:
4343
- '<!--| ~| -->'
4444

4545
- repo: https://github.com/astral-sh/ruff-pre-commit
46-
rev: v0.4.8
46+
rev: v0.5.5
4747
hooks:
4848
- id: ruff
4949
name: Run Ruff linter
@@ -52,7 +52,7 @@ repos:
5252
name: Run Ruff formatter
5353

5454
- repo: https://github.com/pycqa/pylint
55-
rev: "v3.2.3"
55+
rev: "v3.2.6"
5656
hooks:
5757
- id: pylint
5858
name: Check code style with pylint
@@ -80,7 +80,7 @@ repos:
8080
types: [text]
8181

8282
- repo: https://github.com/pre-commit/mirrors-mypy
83-
rev: v1.10.0
83+
rev: v1.11.0
8484
hooks:
8585
- id: mypy
8686
name: Check typing with mypy

anta/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
__copyright__ = "Copyright 2022-2024, Arista Networks, Inc."
2121

2222
# ANTA Debug Mode environment variable
23-
__DEBUG__ = bool(os.environ.get("ANTA_DEBUG", "").lower() == "true")
23+
__DEBUG__ = os.environ.get("ANTA_DEBUG", "").lower() == "true"
24+
if __DEBUG__:
25+
# enable asyncio DEBUG mode when __DEBUG__ is enabled
26+
os.environ["PYTHONASYNCIODEBUG"] = "1"
2427

2528

2629
# Source: https://rich.readthedocs.io/en/stable/appendix/colors.html

anta/catalog.py

+34-20
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
from collections import defaultdict
1212
from inspect import isclass
1313
from itertools import chain
14+
from json import load as json_load
1415
from pathlib import Path
15-
from typing import TYPE_CHECKING, Any, Optional, Union
16+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
1617

17-
import yaml
1818
from pydantic import BaseModel, ConfigDict, RootModel, ValidationError, ValidationInfo, field_validator, model_serializer, model_validator
1919
from pydantic.types import ImportString
2020
from pydantic_core import PydanticCustomError
21-
from yaml import YAMLError, safe_load
21+
from yaml import YAMLError, safe_dump, safe_load
2222

2323
from anta.logger import anta_log_exception
2424
from anta.models import AntaTest
@@ -239,7 +239,16 @@ def yaml(self) -> str:
239239
# This could be improved.
240240
# https://github.com/pydantic/pydantic/issues/1043
241241
# Explore if this worth using this: https://github.com/NowanIlfideme/pydantic-yaml
242-
return yaml.safe_dump(yaml.safe_load(self.model_dump_json(serialize_as_any=True, exclude_unset=True)), indent=2, width=math.inf)
242+
return safe_dump(safe_load(self.model_dump_json(serialize_as_any=True, exclude_unset=True)), indent=2, width=math.inf)
243+
244+
def to_json(self) -> str:
245+
"""Return a JSON representation string of this model.
246+
247+
Returns
248+
-------
249+
The JSON representation string of this model.
250+
"""
251+
return self.model_dump_json(serialize_as_any=True, exclude_unset=True, indent=2)
243252

244253

245254
class AntaCatalog:
@@ -255,8 +264,8 @@ def __init__(
255264
) -> None:
256265
"""Instantiate an AntaCatalog instance.
257266
258-
Args:
259-
----
267+
Parameters
268+
----------
260269
tests: A list of AntaTestDefinition instances.
261270
filename: The path from which the catalog is loaded.
262271
@@ -299,19 +308,24 @@ def tests(self, value: list[AntaTestDefinition]) -> None:
299308
self._tests = value
300309

301310
@staticmethod
302-
def parse(filename: str | Path) -> AntaCatalog:
311+
def parse(filename: str | Path, file_format: Literal["yaml", "json"] = "yaml") -> AntaCatalog:
303312
"""Create an AntaCatalog instance from a test catalog file.
304313
305-
Args:
306-
----
307-
filename: Path to test catalog YAML file
314+
Parameters
315+
----------
316+
filename: Path to test catalog YAML or JSON fil
317+
file_format: Format of the file, either 'yaml' or 'json'
308318
309319
"""
320+
if file_format not in ["yaml", "json"]:
321+
message = f"'{file_format}' is not a valid format for an AntaCatalog file. Only 'yaml' and 'json' are supported."
322+
raise ValueError(message)
323+
310324
try:
311325
file: Path = filename if isinstance(filename, Path) else Path(filename)
312326
with file.open(encoding="UTF-8") as f:
313-
data = safe_load(f)
314-
except (TypeError, YAMLError, OSError) as e:
327+
data = safe_load(f) if file_format == "yaml" else json_load(f)
328+
except (TypeError, YAMLError, OSError, ValueError) as e:
315329
message = f"Unable to parse ANTA Test Catalog file '{filename}'"
316330
anta_log_exception(e, message, logger)
317331
raise
@@ -326,8 +340,8 @@ def from_dict(data: RawCatalogInput, filename: str | Path | None = None) -> Anta
326340
It is the data structure returned by `yaml.load()` function of a valid
327341
YAML Test Catalog file.
328342
329-
Args:
330-
----
343+
Parameters
344+
----------
331345
data: Python dictionary used to instantiate the AntaCatalog instance
332346
filename: value to be set as AntaCatalog instance attribute
333347
@@ -360,8 +374,8 @@ def from_list(data: ListAntaTestTuples) -> AntaCatalog:
360374
361375
See ListAntaTestTuples type alias for details.
362376
363-
Args:
364-
----
377+
Parameters
378+
----------
365379
data: Python list used to instantiate the AntaCatalog instance
366380
367381
"""
@@ -378,8 +392,8 @@ def from_list(data: ListAntaTestTuples) -> AntaCatalog:
378392
def merge(catalogs: list[AntaCatalog]) -> AntaCatalog:
379393
"""Merge multiple AntaCatalog instances.
380394
381-
Args:
382-
----
395+
Parameters
396+
----------
383397
catalogs: List of AntaCatalog instances to merge.
384398
385399
Returns
@@ -431,8 +445,8 @@ def build_indexes(self, filtered_tests: set[str] | None = None) -> None:
431445
def get_tests_by_tags(self, tags: set[str], *, strict: bool = False) -> set[AntaTestDefinition]:
432446
"""Return all tests that match a given set of tags, according to the specified strictness.
433447
434-
Args:
435-
----
448+
Parameters
449+
----------
436450
tags: The tags to filter tests by. If empty, return all tests without tags.
437451
strict: If True, returns only tests that contain all specified tags (intersection).
438452
If False, returns tests that contain any of the specified tags (union).

anta/cli/get/utils.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ def get_cv_token(cvp_ip: str, cvp_username: str, cvp_password: str, *, verify_ce
8282
8383
TODO: need to handle requests error
8484
85-
Args:
86-
----
85+
Parameters
86+
----------
8787
cvp_ip: IP address of CloudVision.
8888
cvp_username: Username to connect to CloudVision.
8989
cvp_password: Password to connect to CloudVision.
@@ -161,8 +161,8 @@ def deep_yaml_parsing(data: dict[str, Any], hosts: list[AntaInventoryHost] | Non
161161
def create_inventory_from_ansible(inventory: Path, output: Path, ansible_group: str = "all") -> None:
162162
"""Create an ANTA inventory from an Ansible inventory YAML file.
163163
164-
Args:
165-
----
164+
Parameters
165+
----------
166166
inventory: Ansible Inventory file to read
167167
output: ANTA inventory file to generate.
168168
ansible_group: Ansible group from where to extract data.

anta/cli/nrfu/__init__.py

+14-22
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,14 @@
55

66
from __future__ import annotations
77

8-
import asyncio
98
from typing import TYPE_CHECKING, get_args
109

1110
import click
1211

1312
from anta.cli.nrfu import commands
1413
from anta.cli.utils import AliasedGroup, catalog_options, inventory_options
1514
from anta.custom_types import TestStatus
16-
from anta.models import AntaTest
1715
from anta.result_manager import ResultManager
18-
from anta.runner import main
19-
20-
from .utils import anta_progress_bar, print_settings
2116

2217
if TYPE_CHECKING:
2318
from anta.catalog import AntaCatalog
@@ -37,6 +32,7 @@ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
3732
"""Ignore MissingParameter exception when parsing arguments if `--help` is present for a subcommand."""
3833
# Adding a flag for potential callbacks
3934
ctx.ensure_object(dict)
35+
ctx.obj["args"] = args
4036
if "--help" in args:
4137
ctx.obj["_anta_help"] = True
4238

@@ -96,7 +92,7 @@ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
9692
default=None,
9793
type=click.Choice(HIDE_STATUS, case_sensitive=False),
9894
multiple=True,
99-
help="Group result by test or device.",
95+
help="Hide results by type: success / failure / error / skipped'.",
10096
required=False,
10197
)
10298
@click.option(
@@ -120,38 +116,34 @@ def nrfu(
120116
ignore_status: bool,
121117
ignore_error: bool,
122118
dry_run: bool,
119+
catalog_format: str = "yaml",
123120
) -> None:
124121
"""Run ANTA tests on selected inventory devices."""
125122
# If help is invoke somewhere, skip the command
126123
if ctx.obj.get("_anta_help"):
127124
return
125+
128126
# We use ctx.obj to pass stuff to the next Click functions
129127
ctx.ensure_object(dict)
130128
ctx.obj["result_manager"] = ResultManager()
131129
ctx.obj["ignore_status"] = ignore_status
132130
ctx.obj["ignore_error"] = ignore_error
133131
ctx.obj["hide"] = set(hide) if hide else None
134-
print_settings(inventory, catalog)
135-
with anta_progress_bar() as AntaTest.progress:
136-
asyncio.run(
137-
main(
138-
ctx.obj["result_manager"],
139-
inventory,
140-
catalog,
141-
tags=tags,
142-
devices=set(device) if device else None,
143-
tests=set(test) if test else None,
144-
dry_run=dry_run,
145-
)
146-
)
147-
if dry_run:
148-
return
132+
ctx.obj["catalog"] = catalog
133+
ctx.obj["catalog_format"] = catalog_format
134+
ctx.obj["inventory"] = inventory
135+
ctx.obj["tags"] = tags
136+
ctx.obj["device"] = device
137+
ctx.obj["test"] = test
138+
ctx.obj["dry_run"] = dry_run
139+
149140
# Invoke `anta nrfu table` if no command is passed
150-
if ctx.invoked_subcommand is None:
141+
if not ctx.invoked_subcommand:
151142
ctx.invoke(commands.table)
152143

153144

154145
nrfu.add_command(commands.table)
146+
nrfu.add_command(commands.csv)
155147
nrfu.add_command(commands.json)
156148
nrfu.add_command(commands.text)
157149
nrfu.add_command(commands.tpl_report)

0 commit comments

Comments
 (0)