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

Conversation

carl-baillargeon
Copy link
Contributor

@carl-baillargeon carl-baillargeon commented Jul 4, 2024

Description

Added _results_per_status as a cached property to fetch results faster.

anta nrfu md-report --md-output ./anta_md_report.md
anta nrfu md-report --md-output ./anta_md_report.md --only_failed_tests

image

Fixes # (issue id)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have run pre-commit for code linting and typing (pre-commit run)
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes (tox -e testenv)

@titom73
Copy link
Collaborator

titom73 commented Jul 5, 2024

We should discuss how we want to add new outputs to make it consistent across multiple PRs

Linked to PR #672

@mtache
Copy link
Collaborator

mtache commented Jul 5, 2024

I don't think this PR requires #649 since it is not about refactoring the ANTA Runner.
You can still benchmark CLI output options if you want to improve their performances but this PR introduces a new format so nothing to be compared with.

Copy link
Contributor

github-actions bot commented Jul 8, 2024

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Copy link
Contributor

github-actions bot commented Jul 8, 2024

Conflicts have been resolved. A maintainer will review the pull request shortly.

@titom73 titom73 self-requested a review July 9, 2024 15:25
@titom73 titom73 added framework-enhancement New feature or request Anta CLI All things around CLI labels Jul 9, 2024
anta/cli/nrfu/commands.py Outdated Show resolved Hide resolved
@@ -122,6 +124,14 @@ 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."""
Copy link
Collaborator

Choose a reason for hiding this comment

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

docstring need more info (Parameters)

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 30c8d7e

anta/reporter/md_reporter.py Outdated Show resolved Hide resolved
anta/reporter/md_reporter.py Outdated Show resolved Hide resolved
anta/result_manager/__init__.py Outdated Show resolved Hide resolved
)
def test_md_report_generate(tmp_path: Path, expected_report_name: str, *, only_failed_tests: bool) -> None:
"""Test the MDReportGenerator class."""
# Create a temporary Markdown file
Copy link
Collaborator

Choose a reason for hiding this comment

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

it does not create the file right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. tmp_path is a pytest fixture --> https://docs.pytest.org/en/stable/how-to/tmp_path.html

Copy link
Collaborator

Choose a reason for hiding this comment

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

so let's remove this comment

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 fb2139a

tests/units/reporter/test_md_reporter.py Outdated Show resolved Hide resolved
Comment on lines 46 to 48
# Load the existing Markdown report to compare with the generated one
with (DATA_DIR / expected_report_name).open("r", encoding="utf-8") as f:
expected_content = f.read()
Copy link
Collaborator

Choose a reason for hiding this comment

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

lets make a smaller test set

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean? Creating multiple tests for each section (class) of 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.

less tests I was thinking

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reduced the amount of tests in fb2139a

tests/units/result_manager/test__init__.py Outdated Show resolved Hide resolved
tests/units/result_manager/test__init__.py Outdated Show resolved Hide resolved
----------
results: The ResultsManager instance containing all test results.
md_filename: The path to the markdown file to write the report into.
only_failed_tests: Flag to generate a report with only failed tests. Defaults to False.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
only_failed_tests: Flag to generate a report with only failed tests. Defaults to False.
only_failed_tests: Flag to generate a report with only failed tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you suggesting to remove the default in the function signature as well?

Copy link
Collaborator

Choose a reason for hiding this comment

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

no only in the docstring

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed only_failed_tests in 8918cd0

Comment on lines 182 to 189
toc = """**Table of Contents:**

- [ANTA Report](#anta-report)
- [Test Results Summary](#test-results-summary)
- [Summary Totals](#summary-totals)
- [Summary Totals Device Under Test](#summary-totals-device-under-test)
- [Summary Totals Per Category](#summary-totals-per-category)
- [Failed Test Results Summary](#failed-test-results-summary)"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's move this to a constant outside of here so that it does not look weird to look at with indent

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 d217ac3

# Every time a new result is added, we need to clear the cached property
self.__dict__.pop("results_by_status", None)

def get_results(self, status: set[TestStatus] | TestStatus | None = None, sort_by: list[str] | None = None) -> list[TestResult]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

would it not be simpler for us to just support set[TestStatus] | None - I mean - who is the convenience method for? to support single TestStatus ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

get_results and get_total_results have the same signature and I think it's cleaner when we need a single status in md_reporter --> self.results.get_total_results('success')

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would change also get_total_results - I disagree it is cleaner to have signature with unions when we can avoid them as this means we need to handle the types in the code, though I agree it looks nicer to write.

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 8918cd0

anta/result_manager/__init__.py Show resolved Hide resolved
Comment on lines +97 to +109
# pylint: disable=too-many-instance-attributes
@dataclass
class DeviceStats:
"""Device statistics for a run of tests."""

tests_success_count: int = 0
tests_skipped_count: int = 0
tests_failure_count: int = 0
tests_error_count: int = 0
tests_unset_count: int = 0
tests_failure: set[str] = field(default_factory=set)
categories_failed: set[str] = field(default_factory=set)
categories_skipped: set[str] = field(default_factory=set)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is a benefit of us using a mix of Pydantic BaseModel and datalclasses? (Not asking to change it just wondering if we are not kind of mix and matching things when we should 'stick to one')?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pydantic will validate the fields, dataclasses will not by default. Since these classes are basically "storing" internal data (not user facing), we don't need to validate anything.

docs/cli/nrfu.md Outdated
Usage: anta nrfu md-report [OPTIONS]

ANTA command to check network state with Markdown report. It only saves test
results and not the output from the --group-by option.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
results and not the output from the --group-by option.
results and not the output from the `anta nrfu --group-by` option.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I am confused now. --group-by is an option for the table command so why do we need to specify this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed in 5c44994

Comment on lines 52 to 56
| DC1-LEAF1A | BFD | VerifyBFDPeersIntervals | Verifies the timers of the IPv4 BFD peers in the specified VRF. | - | failure | Following BFD peers are not configured or timers are not correct: {'192.0.255.8': {'default': 'Not Configured'}, '192.0.255.7': {'default': 'Not Configured'}} |
| DC1-LEAF1A | BFD | VerifyBFDSpecificPeers | Verifies the IPv4 BFD peer's sessions and remote disc in the specified VRF. | - | failure | Following BFD peers are not configured, status is not up or remote disc is zero: {'192.0.255.8': {'default': 'Not Configured'}, '192.0.255.7': {'default': 'Not Configured'}} |
| DC1-LEAF1A | BGP | VerifyBGPAdvCommunities | Verifies the advertised communities of a BGP peer. | - | failure | Following BGP peers are not configured or advertised communities are not standard, extended, and large: {'bgp_peers': {'172.30.11.17': {'default': {'status': 'Not configured'}}, '172.30.11.21': {'default': {'status': 'Not configured'}}}} |
| DC1-LEAF1A | BGP | VerifyBGPExchangedRoutes | Verifies the advertised and received routes of BGP peers. | - | failure | Following BGP peers are not found or routes are not exchanged properly: {'bgp_peers': {'172.30.255.5': {'default': 'Not configured'}, '172.30.255.1': {'default': 'Not configured'}}} |
| DC1-LEAF1A | BGP | VerifyBGPPeerASNCap | Verifies the four octet asn capabilities of a BGP peer. | - | failure | Following BGP peer four octet asn capabilities are not found or not ok: {'bgp_peers': {'172.30.11.1': {'default': {'status': 'Not configured'}}}} |
Copy link
Collaborator

Choose a reason for hiding this comment

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

well .. what it impacts is maintainability - if we deprecate tests for instance we need to come back here and modify this file - which is big and which we will probably forget

)
def test_md_report_generate(tmp_path: Path, expected_report_name: str, *, only_failed_tests: bool) -> None:
"""Test the MDReportGenerator class."""
# Create a temporary Markdown file
Copy link
Collaborator

Choose a reason for hiding this comment

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

so let's remove this comment

Comment on lines 46 to 48
# Load the existing Markdown report to compare with the generated one
with (DATA_DIR / expected_report_name).open("r", encoding="utf-8") as f:
expected_content = f.read()
Copy link
Collaborator

Choose a reason for hiding this comment

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

less tests I was thinking

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

"""
MDReportBase.ONLY_FAILED_TESTS = only_failed_tests

with md_filename.open("w", encoding="utf-8") as mdfile:
Copy link
Collaborator

Choose a reason for hiding this comment

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

need to catch OSError here if anything goes wrong - cf #672

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

Copy link
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Copy link
Contributor

Conflicts have been resolved. A maintainer will review the pull request shortly.

Comment on lines 119 to 125
@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

Comment on lines 36 to 38
The class method also accepts an optional `only_failed_tests` flag to generate a report with only failed tests.

By default, the report will include all test results.
Copy link
Collaborator

Choose a reason for hiding this comment

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

lets use the --hide content instead for greater versatility

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 8918cd0

----------
results: The ResultsManager instance containing all test results.
md_filename: The path to the markdown file to write the report into.
only_failed_tests: Flag to generate a report with only failed tests. Defaults to False.
Copy link
Collaborator

Choose a reason for hiding this comment

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

no only in the docstring

-------
str: Formatted header name.

Example:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Example:
Example

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 8918cd0

----------
heading_level: The level of the heading (1-6).

Example:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Example:
Example

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 8918cd0

# Every time a new result is added, we need to clear the cached property
self.__dict__.pop("results_by_status", None)

def get_results(self, status: set[TestStatus] | TestStatus | None = None, sort_by: list[str] | None = None) -> list[TestResult]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would change also get_total_results - I disagree it is cleaner to have signature with unions when we can avoid them as this means we need to handle the types in the code, though I agree it looks nicer to write.

@@ -162,15 +162,6 @@ def test_anta_nrfu_md_report_all_tests(click_runner: CliRunner, tmp_path: Path)
assert md_output.exists()


def test_anta_nrfu_md_report_only_failed_tests(click_runner: CliRunner, tmp_path: Path) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

should you not keep a test with --hide <> whatever?

Copy link
Collaborator

@gmuloc gmuloc left a comment

Choose a reason for hiding this comment

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

just one comment on the tests otherwise LGTM

Copy link

sonarcloud bot commented Aug 29, 2024

Copy link
Collaborator

@gmuloc gmuloc left a comment

Choose a reason for hiding this comment

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

tested in a lab

@gmuloc gmuloc merged commit 7bb4560 into aristanetworks:main Aug 29, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Anta CLI All things around CLI framework-enhancement New feature or request rn: feat(anta)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants