-
Notifications
You must be signed in to change notification settings - Fork 31
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
Conversation
We should discuss how we want to add new outputs to make it consistent across multiple PRs Linked to PR #672 |
I don't think this PR requires #649 since it is not about refactoring the ANTA Runner. |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
504cff4
to
733c159
Compare
Conflicts have been resolved. A maintainer will review the pull request shortly. |
anta/cli/nrfu/utils.py
Outdated
@@ -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.""" |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 30c8d7e
) | ||
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in fb2139a
# 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() |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
anta/reporter/md_reporter.py
Outdated
---------- | ||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
anta/reporter/md_reporter.py
Outdated
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)""" |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d217ac3
anta/result_manager/__init__.py
Outdated
# 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]: |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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')
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 8918cd0
# 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) |
There was a problem hiding this comment.
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')?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
results and not the output from the --group-by option. | |
results and not the output from the `anta nrfu --group-by` option. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in 5c44994
| 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'}}}} | |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
# 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() |
There was a problem hiding this comment.
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
anta/cli/nrfu/utils.py
Outdated
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) |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5c44994
anta/reporter/md_reporter.py
Outdated
""" | ||
MDReportBase.ONLY_FAILED_TESTS = only_failed_tests | ||
|
||
with md_filename.open("w", encoding="utf-8") as mdfile: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 5c44994
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Conflicts have been resolved. A maintainer will review the pull request shortly. |
anta/cli/nrfu/commands.py
Outdated
@click.option( | ||
"--only-failed-tests", | ||
is_flag=True, | ||
default=False, | ||
show_envvar=True, | ||
help="Only include failed tests in the report.", | ||
) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Removed in 8918cd0
anta/reporter/md_reporter.py
Outdated
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. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 8918cd0
anta/reporter/md_reporter.py
Outdated
---------- | ||
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. |
There was a problem hiding this comment.
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
anta/reporter/md_reporter.py
Outdated
------- | ||
str: Formatted header name. | ||
|
||
Example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example: | |
Example |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 8918cd0
anta/reporter/md_reporter.py
Outdated
---------- | ||
heading_level: The level of the heading (1-6). | ||
|
||
Example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example: | |
Example |
There was a problem hiding this comment.
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
Outdated
# 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]: |
There was a problem hiding this comment.
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.
7cb3f82
to
dc23513
Compare
@@ -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: |
There was a problem hiding this comment.
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?
There was a problem hiding this 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
|
There was a problem hiding this 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
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
Fixes # (issue id)
Checklist:
pre-commit run
)tox -e testenv
)