Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anta): Add Markdown report option to ANTA #740

Merged
merged 29 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c5c517a
feat(anta): Add markdown report option to ANTA CLI
carl-baillargeon Jul 4, 2024
ef3a942
Update MDReportFactory
carl-baillargeon Jul 4, 2024
7920718
Make Py3.11 happy with f-string
carl-baillargeon Jul 4, 2024
0c1e957
Remove unnecessary only_failed_tests from MDReportBase
carl-baillargeon Jul 4, 2024
cb35a83
Added TestStats
carl-baillargeon Jul 8, 2024
733c159
Fix typos
carl-baillargeon Jul 8, 2024
aa2168f
Fix pytest
carl-baillargeon Jul 8, 2024
e72b22a
Added unit tests for reporter and result_manager
carl-baillargeon Jul 9, 2024
874467b
Added unit tests for md_reporter
carl-baillargeon Jul 9, 2024
650b8e2
Fix unit tests for md_reporter
carl-baillargeon Jul 9, 2024
230bd0a
Remove license from test md reports
carl-baillargeon Jul 9, 2024
8410064
Fix line too long
carl-baillargeon Jul 9, 2024
1747500
Added more unit tests
carl-baillargeon Jul 9, 2024
f34b1fe
Added documentation
carl-baillargeon Jul 9, 2024
596d029
Remove test.md
carl-baillargeon Jul 9, 2024
30c8d7e
Address review comments
carl-baillargeon Jul 11, 2024
73208a5
Update unit tests for ResultManager
carl-baillargeon Jul 11, 2024
8d14307
More unit tests
carl-baillargeon Jul 11, 2024
57f2021
Merge branch 'main' into feat/md_report
gmuloc Jul 12, 2024
fb2139a
Reduce number of tests in fake reports
carl-baillargeon Jul 18, 2024
d217ac3
Fix per review comments
carl-baillargeon Jul 18, 2024
5c44994
Rebase with latest main
carl-baillargeon Jul 18, 2024
16ae28c
Remove unrelated changes
carl-baillargeon Jul 18, 2024
dc23513
Merge branch 'main' into feat/md_report
carl-baillargeon Aug 19, 2024
8918cd0
Fix per review comments
carl-baillargeon Aug 19, 2024
7abf73a
Merge branch 'main' into feat/md_report
carl-baillargeon Aug 19, 2024
83d8602
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 19, 2024
21cb559
Merge branch 'main' into feat/md_report
gmuloc Aug 29, 2024
8372826
Added unit test for --hide
carl-baillargeon Aug 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ repos:
- name: Check and insert license on Markdown files
id: insert-license
files: .*\.md$
# exclude:
exclude: ^tests/data/.*\.md$
args:
- --license-filepath
- .github/license-short.txt
Expand Down
1 change: 1 addition & 0 deletions anta/cli/nrfu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,4 @@ def nrfu(
nrfu.add_command(commands.json)
nrfu.add_command(commands.text)
nrfu.add_command(commands.tpl_report)
nrfu.add_command(commands.md_report)
31 changes: 27 additions & 4 deletions anta/cli/nrfu/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from anta.cli.utils import exit_with_code

from .utils import print_jinja, print_json, print_table, print_text, run_tests
from .utils import print_jinja, print_json, print_table, print_text, run_tests, save_markdown_report

logger = logging.getLogger(__name__)

Expand All @@ -31,7 +31,7 @@ def table(
ctx: click.Context,
group_by: Literal["device", "test"] | None,
) -> None:
"""ANTA command to check network states with table result."""
"""ANTA command to check network state with table results."""
run_tests(ctx)
print_table(ctx, group_by=group_by)
exit_with_code(ctx)
Expand All @@ -48,7 +48,7 @@ def table(
help="Path to save report as a file",
)
def json(ctx: click.Context, output: pathlib.Path | None) -> None:
"""ANTA command to check network state with JSON result."""
"""ANTA command to check network state with JSON results."""
run_tests(ctx)
print_json(ctx, output=output)
exit_with_code(ctx)
Expand All @@ -57,7 +57,7 @@ def json(ctx: click.Context, output: pathlib.Path | None) -> None:
@click.command()
@click.pass_context
def text(ctx: click.Context) -> None:
"""ANTA command to check network states with text result."""
"""ANTA command to check network state with text results."""
run_tests(ctx)
print_text(ctx)
exit_with_code(ctx)
Expand Down Expand Up @@ -86,3 +86,26 @@ def tpl_report(ctx: click.Context, template: pathlib.Path, output: pathlib.Path
run_tests(ctx)
print_jinja(results=ctx.obj["result_manager"], template=template, output=output)
exit_with_code(ctx)


@click.command()
@click.pass_context
@click.option(
"--md-output",
type=click.Path(file_okay=True, dir_okay=False, exists=False, writable=True, path_type=pathlib.Path),
show_envvar=True,
required=True,
help="Path to save the report as a Markdown file",
)
@click.option(
"--only-failed-tests",
is_flag=True,
default=False,
show_envvar=True,
help="Only include failed tests in the report.",
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this new option or could we not use the nrfu option

  --hide [success|failure|error|skipped]
                                  Hide results by type: success / failure /
                                  error / skipped'.

instead and pass it down the line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Removed in 8918cd0

def md_report(ctx: click.Context, md_output: pathlib.Path, *, only_failed_tests: bool = False) -> None:
"""ANTA command to check network state with Markdown report. It only saves test results and not the output from the --group-by option."""
run_tests(ctx)
save_markdown_report(ctx, md_output=md_output, only_failed_tests=only_failed_tests)
exit_with_code(ctx)
17 changes: 17 additions & 0 deletions anta/cli/nrfu/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
from typing import TYPE_CHECKING, Literal

import rich
from rich.emoji import Emoji
from rich.panel import Panel
from rich.progress import BarColumn, MofNCompleteColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn, TimeRemainingColumn

from anta.cli.console import console
from anta.models import AntaTest
from anta.reporter import ReportJinja, ReportTable
from anta.reporter.md_reporter import MDReportGenerator
from anta.runner import main

if TYPE_CHECKING:
Expand Down Expand Up @@ -122,6 +124,21 @@ def print_jinja(results: ResultManager, template: pathlib.Path, output: pathlib.
file.write(report)


def save_markdown_report(ctx: click.Context, md_output: pathlib.Path, *, only_failed_tests: bool = False) -> None:
"""Save the markdown report to a file.

Parameters
----------
ctx: Click context containing the result manager.
md_output: Path to save the markdown report.
only_failed_tests: If True, only failed tests will be included in the report. Default is False.
"""
console.print()
MDReportGenerator.generate(results=_get_result_manager(ctx), md_filename=md_output, only_failed_tests=only_failed_tests)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could fail and raise an OSError, in that case we would need to catch it cleanly and not print "Markdown report saved"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5c44994

checkmark = Emoji("white_check_mark")
console.print(f"Markdown report saved to {md_output} {checkmark}", style="cyan")


# Adding our own ANTA spinner - overriding rich SPINNERS for our own
# so ignore warning for redefinition
rich.spinner.SPINNERS = { # type: ignore[attr-defined]
Expand Down
5 changes: 5 additions & 0 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
# that can be found in the LICENSE file.
"""Module that provides predefined types for AntaTest.Input instances."""

from __future__ import annotations

import re
from typing import Annotated, Literal

from pydantic import Field
from pydantic.functional_validators import AfterValidator, BeforeValidator

ACRONYM_CATEGORIES: set[str] = {"aaa", "mlag", "snmp", "bgp", "ospf", "vxlan", "stp", "igmp", "ip", "lldp", "ntp", "bfd", "ptp", "lanz", "stun", "vlan"}
"""A set of network protocol or feature acronyms that should be represented in uppercase."""

# Regular Expression definition
# TODO: make this configurable - with an env var maybe?
REGEXP_EOS_BLACKLIST_CMDS = [r"^reload.*", r"^conf\w*\s*(terminal|session)*", r"^wr\w*\s*\w+"]
Expand Down
40 changes: 14 additions & 26 deletions anta/reporter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,24 +140,18 @@ def report_summary_tests(
"# of skipped",
"# of failure",
"# of errors",
"List of failed or error nodes",
"List of failed or errored devices",
]
table = self._build_headers(headers=headers, table=table)
for test in manager.get_tests():
for test, stats in sorted(manager.test_stats.items()):
if tests is None or test in tests:
results = manager.filter_by_tests({test}).results
nb_failure = len([result for result in results if result.result == "failure"])
nb_error = len([result for result in results if result.result == "error"])
list_failure = [result.name for result in results if result.result in ["failure", "error"]]
nb_success = len([result for result in results if result.result == "success"])
nb_skipped = len([result for result in results if result.result == "skipped"])
table.add_row(
test,
str(nb_success),
str(nb_skipped),
str(nb_failure),
str(nb_error),
str(list_failure),
str(stats.devices_success_count),
str(stats.devices_skipped_count),
str(stats.devices_failure_count),
str(stats.devices_error_count),
", ".join(stats.devices_failure),
)
return table

Expand Down Expand Up @@ -188,24 +182,18 @@ def report_summary_devices(
"# of skipped",
"# of failure",
"# of errors",
"List of failed or error test cases",
"List of failed or errored test cases",
]
table = self._build_headers(headers=headers, table=table)
for device in manager.get_devices():
for device, stats in sorted(manager.device_stats.items()):
if devices is None or device in devices:
results = manager.filter_by_devices({device}).results
nb_failure = len([result for result in results if result.result == "failure"])
nb_error = len([result for result in results if result.result == "error"])
list_failure = [result.test for result in results if result.result in ["failure", "error"]]
nb_success = len([result for result in results if result.result == "success"])
nb_skipped = len([result for result in results if result.result == "skipped"])
table.add_row(
device,
str(nb_success),
str(nb_skipped),
str(nb_failure),
str(nb_error),
str(list_failure),
str(stats.tests_success_count),
str(stats.tests_skipped_count),
str(stats.tests_failure_count),
str(stats.tests_error_count),
", ".join(stats.tests_failure),
)
return table

Expand Down
Loading
Loading