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

fix(anta.cli)!: Avoid requiring username, password, inventory for the get commands #447

Merged
merged 53 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0bf600e
WIP
gmuloc Nov 8, 2023
8f57ac9
WIP
gmuloc Nov 9, 2023
7599dfb
Test: Add tests for get from-ansible
gmuloc Nov 24, 2023
621c8dd
Test: Fix now broken test
gmuloc Nov 24, 2023
c014248
CI: Mypy fun
gmuloc Nov 24, 2023
9c85a73
CI: Update pre-commit
gmuloc Nov 24, 2023
c807c7d
CI: Welcome typing my old friend
gmuloc Nov 24, 2023
50361a0
WIP
gmuloc Nov 24, 2023
b23aee3
add custom type for loglevel
mtache Nov 27, 2023
7eb2a0d
add error handling for None arguments
mtache Nov 27, 2023
27cb962
add logs to anta.device
mtache Nov 29, 2023
16f925a
Cleanup anta.cli
mtache Nov 29, 2023
ece8eed
add logs to anta.inventory
mtache Nov 29, 2023
9a15671
update unit tests for anta.cli
mtache Nov 29, 2023
a98721d
fix https://github.com/pallets/click/issues/824
mtache Nov 30, 2023
1f41916
update unit tests for anta.cli.nrfu
mtache Nov 30, 2023
d6577e5
update unit tests for anta.cli.nrfu
mtache Nov 30, 2023
20722e6
linting
mtache Nov 30, 2023
c4e65a4
refactor cli modules
mtache Nov 30, 2023
1434479
refactor stuff
mtache Nov 30, 2023
84e1b1c
add simple unit tests
mtache Nov 30, 2023
58827cd
update anta.cli.check and anta.cli.debug
mtache Nov 30, 2023
7d6df42
refactor stuff
mtache Nov 30, 2023
7299374
update cli unit tests
mtache Dec 1, 2023
95f3c5a
linting
mtache Dec 1, 2023
03ef76e
update unit tests for anta.cli
mtache Dec 4, 2023
94c2c69
small fix for anta get from-ansible
mtache Dec 4, 2023
049e80d
refactor wrapper
mtache Dec 4, 2023
e58720b
refactor wrappers
mtache Dec 4, 2023
9f1d208
refactor wrapper AGAIN
mtache Dec 4, 2023
583e3c6
consume ignore_status and ignore_error
mtache Dec 4, 2023
57e1b72
Update tests/lib/fixture.py
mtache Dec 4, 2023
2de4bca
Update anta/cli/debug/utils.py
mtache Dec 4, 2023
79880b0
Update tests/lib/fixture.py
mtache Dec 4, 2023
5964427
Update tests/lib/fixture.py
mtache Dec 4, 2023
dcc82af
Update anta/cli/debug/utils.py
mtache Dec 4, 2023
4fbd051
remove shebangs
mtache Dec 4, 2023
2ea91e3
test revision and version in unit tests
mtache Dec 4, 2023
6f59697
fix anta nrfu --help
mtache Dec 4, 2023
152fb17
fix cli
mtache Dec 4, 2023
4c1f248
fix ci
mtache Dec 4, 2023
8a32bd4
Doc: Update CLI doc
gmuloc Dec 6, 2023
12172e6
Test: Fix tox coloring issues
gmuloc Dec 6, 2023
69af63d
CI: Isort
gmuloc Dec 6, 2023
c689ac1
CI: Pylint
gmuloc Dec 6, 2023
117af2c
Test: Adjust pytest default logging level
gmuloc Dec 6, 2023
77a5044
Refactor(anta): Make sure test name is not truncated in table
gmuloc Dec 6, 2023
c12aee6
Test: Set width of CliRunner
gmuloc Dec 6, 2023
72a14f2
Remove uncessary os.environ.pop. This is cleared by CliRunner
mtache Dec 7, 2023
63cd7b3
Update doc
mtache Dec 7, 2023
1bf37f0
Add warning
mtache Dec 7, 2023
99c8565
Update warning
mtache Dec 7, 2023
584cc0c
change -log option to -l
mtache Dec 7, 2023
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 @@ -82,7 +82,7 @@ repos:
# - id: ruff

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.7.1
hooks:
- id: mypy
args:
Expand Down
4 changes: 2 additions & 2 deletions anta/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from pydantic.types import ImportString
from yaml import YAMLError, safe_load

from anta.logger import anta_log_exception
from anta.models import AntaTest
from anta.tools.misc import anta_log_exception

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -224,7 +224,7 @@ def parse(filename: str | Path) -> AntaCatalog:
try:
with open(file=filename, mode="r", encoding="UTF-8") as file:
data = safe_load(file)
except (YAMLError, OSError) as e:
except (TypeError, YAMLError, OSError) as e:
message = f"Unable to parse ANTA Test Catalog file '{filename}'"
anta_log_exception(e, message, logger)
raise
Expand Down
190 changes: 26 additions & 164 deletions anta/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,32 @@
# Copyright (c) 2023 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
# coding: utf-8 -*-
"""
ANTA CLI
"""
from __future__ import annotations

import logging
import pathlib
from typing import Any, Literal
import sys

import click

from anta import __version__
from anta.catalog import AntaCatalog
from anta.cli.check import commands as check_commands
from anta.cli.debug import commands as debug_commands
from anta.cli.exec import commands as exec_commands
from anta.cli.get import commands as get_commands
from anta.cli.nrfu import commands as nrfu_commands
from anta.cli.utils import AliasedGroup, IgnoreRequiredWithHelp, parse_catalog, parse_inventory
from anta.logger import setup_logging
from anta.result_manager import ResultManager
from anta import GITHUB_SUGGESTION, __version__
from anta.cli.check import check as check_command
from anta.cli.debug import debug as debug_command
from anta.cli.exec import exec as exec_command
from anta.cli.get import get as get_command
from anta.cli.nrfu import nrfu as nrfu_command
from anta.cli.utils import AliasedGroup, ExitCode
from anta.logger import Log, LogLevel, anta_log_exception, setup_logging

logger = logging.getLogger(__name__)

@click.group(cls=IgnoreRequiredWithHelp)

@click.group(cls=AliasedGroup)
@click.pass_context
@click.version_option(__version__)
@click.option(
"--username",
help="Username to connect to EOS",
show_envvar=True,
required=True,
)
@click.option("--password", help="Password to connect to EOS that must be provided. It can be prompted using '--prompt' option.", show_envvar=True)
@click.option(
"--enable-password",
help="Password to access EOS Privileged EXEC mode. It can be prompted using '--prompt' option. Requires '--enable' option.",
show_envvar=True,
)
@click.option(
"--enable",
help="Some commands may require EOS Privileged EXEC mode. This option tries to access this mode before sending a command to the device.",
default=False,
show_envvar=True,
is_flag=True,
show_default=True,
)
@click.option(
"--prompt",
"-P",
help="Prompt for passwords if they are not provided.",
default=False,
is_flag=True,
show_default=True,
)
@click.option(
"--timeout",
help="Global connection timeout",
default=30,
show_envvar=True,
show_default=True,
)
@click.option(
"--insecure",
help="Disable SSH Host Key validation",
default=False,
show_envvar=True,
is_flag=True,
show_default=True,
)
@click.option(
"--inventory",
"-i",
help="Path to the inventory YAML file",
show_envvar=True,
required=True,
type=click.Path(file_okay=True, dir_okay=False, exists=True, readable=True, path_type=pathlib.Path),
)
@click.option(
"--log-file",
help="Send the logs to a file. If logging level is DEBUG, only INFO or higher will be sent to stdout.",
Expand All @@ -88,122 +36,36 @@
)
@click.option(
"--log-level",
"--log",
"-log",
help="ANTA logging level",
default=logging.getLevelName(logging.INFO),
show_envvar=True,
show_default=True,
type=click.Choice(
[
logging.getLevelName(logging.CRITICAL),
logging.getLevelName(logging.ERROR),
logging.getLevelName(logging.WARNING),
logging.getLevelName(logging.INFO),
logging.getLevelName(logging.DEBUG),
],
[Log.CRITICAL, Log.ERROR, Log.WARNING, Log.INFO, Log.DEBUG],
case_sensitive=False,
),
)
@click.option("--ignore-status", help="Always exit with success", show_envvar=True, is_flag=True, default=False)
@click.option("--ignore-error", help="Only report failures and not errors", show_envvar=True, is_flag=True, default=False)
@click.option("--disable-cache", help="Disable cache globally", show_envvar=True, show_default=True, is_flag=True, default=False)
def anta(
ctx: click.Context, inventory: pathlib.Path, log_level: Literal["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], log_file: pathlib.Path, **kwargs: Any
) -> None:
# pylint: disable=unused-argument
def anta(ctx: click.Context, log_level: LogLevel, log_file: pathlib.Path) -> None:
"""Arista Network Test Automation (ANTA) CLI"""
setup_logging(log_level, log_file)

if not ctx.obj.get("_anta_help"):
if ctx.params.get("prompt"):
# User asked for a password prompt
if ctx.params.get("password") is None:
ctx.params["password"] = click.prompt("Please enter a password to connect to EOS", type=str, hide_input=True, confirmation_prompt=True)
if ctx.params.get("enable"):
if ctx.params.get("enable_password") is None:
if click.confirm("Is a password required to enter EOS privileged EXEC mode?"):
ctx.params["enable_password"] = click.prompt(
"Please enter a password to enter EOS privileged EXEC mode", type=str, hide_input=True, confirmation_prompt=True
)
if ctx.params.get("password") is None:
raise click.BadParameter(
f"EOS password needs to be provided by using either the '{anta.params[2].opts[0]}' option or the '{anta.params[5].opts[0]}' option."
)
if not ctx.params.get("enable") and ctx.params.get("enable_password"):
raise click.BadParameter(f"Providing a password to access EOS Privileged EXEC mode requires '{anta.params[4].opts[0]}' option.")

ctx.ensure_object(dict)
ctx.obj["inventory"] = parse_inventory(ctx, inventory)
ctx.obj["inventory_path"] = ctx.params["inventory"]


@anta.group("nrfu", cls=IgnoreRequiredWithHelp)
@click.pass_context
@click.option(
"--catalog",
"-c",
envvar="ANTA_CATALOG",
show_envvar=True,
help="Path to the test catalog YAML file",
type=click.Path(file_okay=True, dir_okay=False, exists=True, readable=True),
required=True,
callback=parse_catalog,
)
def _nrfu(ctx: click.Context, catalog: AntaCatalog) -> None:
"""Run NRFU against inventory devices"""
ctx.obj["catalog"] = catalog
ctx.obj["result_manager"] = ResultManager()


@anta.group("check", cls=AliasedGroup)
def _check() -> None:
"""Check commands for building ANTA"""


@anta.group("exec", cls=AliasedGroup)
def _exec() -> None:
"""Execute commands to inventory devices"""


@anta.group("get", cls=AliasedGroup)
def _get() -> None:
"""Get data from/to ANTA"""


@anta.group("debug", cls=AliasedGroup)
def _debug() -> None:
"""Debug commands for building ANTA"""


# Load group commands
# Prefixing with `_` for avoiding the confusion when importing anta.cli.debug.commands as otherwise the debug group has
# a commands attribute.
_check.add_command(check_commands.catalog)
# Inventory cannot be implemented for now as main 'anta' CLI is already parsing it
# _check.add_command(check_commands.inventory)

_exec.add_command(exec_commands.clear_counters)
_exec.add_command(exec_commands.snapshot)
_exec.add_command(exec_commands.collect_tech_support)

_get.add_command(get_commands.from_cvp)
_get.add_command(get_commands.from_ansible)
_get.add_command(get_commands.inventory)
_get.add_command(get_commands.tags)
setup_logging(log_level, log_file)

_debug.add_command(debug_commands.run_cmd)
_debug.add_command(debug_commands.run_template)

_nrfu.add_command(nrfu_commands.table)
_nrfu.add_command(nrfu_commands.json)
_nrfu.add_command(nrfu_commands.text)
_nrfu.add_command(nrfu_commands.tpl_report)
anta.add_command(nrfu_command)
anta.add_command(check_command)
anta.add_command(exec_command)
anta.add_command(get_command)
anta.add_command(debug_command)


# ANTA CLI Execution
def cli() -> None:
"""Entrypoint for pyproject.toml"""
anta(obj={}, auto_envvar_prefix="ANTA") # pragma: no cover
try:
anta(obj={}, auto_envvar_prefix="ANTA")
except Exception as e: # pylint: disable=broad-exception-caught
anta_log_exception(e, f"Uncaught Exception when running ANTA CLI\n{GITHUB_SUGGESTION}", logger)
sys.exit(ExitCode.INTERNAL_ERROR)


if __name__ == "__main__":
Expand Down
14 changes: 14 additions & 0 deletions anta/cli/check/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Copyright (c) 2023 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""
Click commands to validate configuration files
"""
import click

from anta.cli.check import commands


@click.group
def check() -> None:
"""Commands to validate configuration files"""


check.add_command(commands.catalog)
19 changes: 5 additions & 14 deletions anta/cli/check/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# that can be found in the LICENSE file.
# pylint: disable = redefined-outer-name
"""
Commands for Anta CLI to run check commands.
Click commands to validate configuration files
"""
from __future__ import annotations

Expand All @@ -14,25 +14,16 @@

from anta.catalog import AntaCatalog
from anta.cli.console import console
from anta.cli.utils import parse_catalog
from anta.cli.utils import catalog_options

logger = logging.getLogger(__name__)


@click.command()
@click.option(
"--catalog",
"-c",
envvar="ANTA_CATALOG",
show_envvar=True,
help="Path to the test catalog YAML file",
type=click.Path(file_okay=True, dir_okay=False, exists=True, readable=True, resolve_path=True),
required=True,
callback=parse_catalog,
)
@click.command
@catalog_options
def catalog(catalog: AntaCatalog) -> None:
"""
Check that the catalog is valid
"""
console.print(f"[bold][green]Catalog {catalog.filename} is valid")
console.print(f"[bold][green]Catalog is valid: {catalog.filename}")
console.print(pretty_repr(catalog.tests))
2 changes: 0 additions & 2 deletions anta/cli/console.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env python
# Copyright (c) 2023 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
# coding: utf-8 -*-
"""
ANTA Top-level Console
https://rich.readthedocs.io/en/stable/console.html#console-api
Expand Down
15 changes: 15 additions & 0 deletions anta/cli/debug/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# Copyright (c) 2023 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""
Click commands to execute EOS commands on remote devices
"""
import click

from anta.cli.debug import commands


@click.group
def debug() -> None:
"""Commands to execute EOS commands on remote devices"""


debug.add_command(commands.run_cmd)
debug.add_command(commands.run_template)
Loading