Skip to content

Commit

Permalink
refactor: Fix FAQ and other pieces of code (#679)
Browse files Browse the repository at this point in the history
  • Loading branch information
gmuloc authored May 16, 2024
1 parent 7336299 commit 28ba8e1
Show file tree
Hide file tree
Showing 10 changed files with 40 additions and 55 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ARG IMG_OPTION=alpine

### BUILDER

FROM python:${PYTHON_VER}-${IMG_OPTION} as BUILDER
FROM python:${PYTHON_VER}-${IMG_OPTION} AS BUILDER

RUN pip install --upgrade pip

Expand All @@ -19,7 +19,7 @@ RUN pip --no-cache-dir install --user .

### BASE

FROM python:${PYTHON_VER}-${IMG_OPTION} as BASE
FROM python:${PYTHON_VER}-${IMG_OPTION} AS BASE

# Opencontainer labels
# Labels version and revision will be updating
Expand Down
8 changes: 4 additions & 4 deletions anta/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ def flatten_modules(data: dict[str, Any], package: str | None = None) -> dict[Mo
if isinstance(tests, dict):
# This is an inner Python module
modules.update(AntaCatalogFile.flatten_modules(data=tests, package=module.__name__))
else:
if not isinstance(tests, list):
msg = f"Syntax error when parsing: {tests}\nIt must be a list of ANTA tests. Check the test catalog."
raise ValueError(msg) # noqa: TRY004 pydantic catches ValueError or AssertionError, no TypeError
elif isinstance(tests, list):
# This is a list of AntaTestDefinition
modules[module] = tests
else:
msg = f"Syntax error when parsing: {tests}\nIt must be a list of ANTA tests. Check the test catalog."
raise ValueError(msg) # noqa: TRY004 pydantic catches ValueError or AssertionError, no TypeError
return modules

# ANN401 - Any ok for this validator as we are validating the received data
Expand Down
53 changes: 27 additions & 26 deletions anta/cli/exec/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ async def collect(device: AntaDevice) -> None:
cmd += f" | head -{latest}"
command = AntaCommand(command=cmd, ofmt="text")
await device.collect(command=command)
if command.collected and command.text_output:
filenames = [Path(f"{EOS_SCHEDULED_TECH_SUPPORT}/{f}") for f in command.text_output.splitlines()]
else:
if not (command.collected and command.text_output):
logger.error("Unable to get tech-support filenames on %s: verify that %s is not empty", device.name, EOS_SCHEDULED_TECH_SUPPORT)
return

filenames = [Path(f"{EOS_SCHEDULED_TECH_SUPPORT}/{f}") for f in command.text_output.splitlines()]

# Create directories
outdir = Path() / root_dir / f"{device.name.lower()}"
outdir.mkdir(parents=True, exist_ok=True)
Expand All @@ -122,31 +122,32 @@ async def collect(device: AntaDevice) -> None:

if command.collected and not command.text_output:
logger.debug("'aaa authorization exec default local' is not configured on device %s", device.name)
if configure:
commands = []
# TODO: @mtache - add `config` field to `AntaCommand` object to handle this use case.
# Otherwise mypy complains about enable as it is only implemented for AsyncEOSDevice
# TODO: Should enable be also included in AntaDevice?
if not isinstance(device, AsyncEOSDevice):
msg = "anta exec collect-tech-support is only supported with AsyncEOSDevice for now."
raise UsageError(msg)
if device.enable and device._enable_password is not None: # pylint: disable=protected-access
commands.append({"cmd": "enable", "input": device._enable_password}) # pylint: disable=protected-access
elif device.enable:
commands.append({"cmd": "enable"})
commands.extend(
[
{"cmd": "configure terminal"},
{"cmd": "aaa authorization exec default local"},
],
)
logger.warning("Configuring 'aaa authorization exec default local' on device %s", device.name)
command = AntaCommand(command="show running-config | include aaa authorization exec default local", ofmt="text")
await device._session.cli(commands=commands) # pylint: disable=protected-access
logger.info("Configured 'aaa authorization exec default local' on device %s", device.name)
else:
if not configure:
logger.error("Unable to collect tech-support on %s: configuration 'aaa authorization exec default local' is not present", device.name)
return

commands = []
# TODO: @mtache - add `config` field to `AntaCommand` object to handle this use case.
# Otherwise mypy complains about enable as it is only implemented for AsyncEOSDevice
# TODO: Should enable be also included in AntaDevice?
if not isinstance(device, AsyncEOSDevice):
msg = "anta exec collect-tech-support is only supported with AsyncEOSDevice for now."
raise UsageError(msg)
if device.enable and device._enable_password is not None: # pylint: disable=protected-access
commands.append({"cmd": "enable", "input": device._enable_password}) # pylint: disable=protected-access
elif device.enable:
commands.append({"cmd": "enable"})
commands.extend(
[
{"cmd": "configure terminal"},
{"cmd": "aaa authorization exec default local"},
],
)
logger.warning("Configuring 'aaa authorization exec default local' on device %s", device.name)
command = AntaCommand(command="show running-config | include aaa authorization exec default local", ofmt="text")
await device._session.cli(commands=commands) # pylint: disable=protected-access
logger.info("Configured 'aaa authorization exec default local' on device %s", device.name)

logger.debug("'aaa authorization exec default local' is already configured on device %s", device.name)

await device.copy(sources=filenames, destination=outdir, direction="from")
Expand Down
5 changes: 2 additions & 3 deletions anta/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from typing import TYPE_CHECKING, Any, Callable

import click
from pydantic import ValidationError
from yaml import YAMLError

from anta.catalog import AntaCatalog
Expand Down Expand Up @@ -254,7 +253,7 @@ def wrapper(
insecure=insecure,
disable_cache=disable_cache,
)
except (ValidationError, TypeError, ValueError, YAMLError, OSError, InventoryIncorrectSchemaError, InventoryRootKeyError):
except (TypeError, ValueError, YAMLError, OSError, InventoryIncorrectSchemaError, InventoryRootKeyError):
ctx.exit(ExitCode.USAGE_ERROR)
return f(*args, inventory=i, tags=tags, **kwargs)

Expand Down Expand Up @@ -292,7 +291,7 @@ def wrapper(
return f(*args, catalog=None, **kwargs)
try:
c = AntaCatalog.parse(catalog)
except (ValidationError, TypeError, ValueError, YAMLError, OSError):
except (TypeError, ValueError, YAMLError, OSError):
ctx.exit(ExitCode.USAGE_ERROR)
return f(*args, catalog=c, **kwargs)

Expand Down
8 changes: 2 additions & 6 deletions anta/custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def interface_autocomplete(v: str) -> str:
- `po` will be changed to `Port-Channel`
- `lo` will be changed to `Loopback`
"""
intf_id_re = re.compile(r"[0-9]+(\/[0-9]+)*(\.[0-9]+)?")
intf_id_re = re.compile(r"\d+(\/\d+)*(\.\d+)?")
m = intf_id_re.search(v)
if m is None:
msg = f"Could not parse interface ID in interface '{v}'"
Expand All @@ -33,11 +33,7 @@ def interface_autocomplete(v: str) -> str:

alias_map = {"et": "Ethernet", "eth": "Ethernet", "po": "Port-Channel", "lo": "Loopback"}

for alias, full_name in alias_map.items():
if v.lower().startswith(alias):
return f"{full_name}{intf_id}"

return v
return next((f"{full_name}{intf_id}" for alias, full_name in alias_map.items() if v.lower().startswith(alias)), v)


def interface_case_sensitivity(v: str) -> str:
Expand Down
1 change: 0 additions & 1 deletion anta/tests/field_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,3 @@ def test(self) -> None:
return
# We should never hit this point
self.result.is_error("Error in running test - FixedSystemvrm1 not found")
return
4 changes: 2 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
toc_depth: 4
toc_depth: 2
---
<!--
~ Copyright (c) 2023-2024 Arista Networks, Inc.
~ Use of this source code is governed by the Apache License 2.0
~ that can be found in the LICENSE file.
-->
<style>
h4 {
.md-typeset h2 {
visibility: hidden;
font-size: 0em;
height: 0em;
Expand Down
9 changes: 0 additions & 9 deletions docs/stylesheets/extra.material.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@
--md-code-bg-color: #E6E6E6;
--md-code-border-color: #0000004f;
--block-code-bg-color: #e4e4e4;
/* --md-code-fg-color: ...; */

font-size: 1.1rem;
/* min-height: 100%;
position: relative;
width: 100%; */
font-feature-settings: "kern","liga";
font-family: var(--md-text-font-family,_),-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;
-webkit-font-smoothing: antialiased;
Expand All @@ -62,9 +58,7 @@
--md-typeset-a-color-bg: #27569B;

/* Code block color shades */
/* --md-code-bg-color: #E6E6E6; */
--md-code-border-color: #aec6db4f;
/* --block-code-bg-color: #e4e4e4; */
}

@media only screen and (min-width: 76.25em) {
Expand Down Expand Up @@ -230,8 +224,6 @@
.md-typeset table:not([class]) th {
min-width: 5rem;
padding: .6rem .8rem;
/* color: var(--md-primary-fg-color--light); */
bg: var(--md-footer-fg-color--lighter);
}

.md-footer-copyright {
Expand All @@ -247,7 +239,6 @@
margin-left: auto;
margin-right: auto;
border-radius: 1%;
/* width: 50%; */
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/units/cli/nrfu/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def test_anta_nrfu_json(click_runner: CliRunner) -> None:
result = click_runner.invoke(anta, ["nrfu", "json"])
assert result.exit_code == ExitCode.OK
assert "JSON results" in result.output
match = re.search(r"\[\n {[\s\S]+ }\n\]", result.output)
match = re.search(r"\[\n {2}{[\s\S]+ {2}}\n\]", result.output)
assert match is not None
result_list = json.loads(match.group())
for res in result_list:
Expand Down
1 change: 0 additions & 1 deletion tests/units/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,6 @@ def test_wrong_format_output_access(self) -> None:
text_cmd = AntaCommand(command="show dummy", ofmt="text", output="blah")
text_cmd_2 = AntaCommand(command="show dummy", ofmt="text", output={"not_a": "string"})
msg = "Output of command 'show dummy' is invalid"
msg = "Output of command 'show dummy' is invalid"
with pytest.raises(RuntimeError, match=msg):
json_cmd.text_output
with pytest.raises(RuntimeError, match=msg):
Expand Down

0 comments on commit 28ba8e1

Please sign in to comment.