1
0
Fork 0

Merging upstream version 0.9.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 13:50:07 +01:00
parent 857951d9ac
commit 161de8690e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
28 changed files with 1073 additions and 859 deletions

View file

@ -11,49 +11,51 @@ ARDL CLI Baseline.
"""
import click
from rich.console import Console
import eos_downloader
from eos_downloader.cli.get import commands as get_commands
from eos_downloader import __version__
from eos_downloader.cli.debug import commands as debug_commands
from eos_downloader.cli.get import commands as get_commands
from eos_downloader.cli.info import commands as info_commands
from eos_downloader.cli.utils import AliasedGroup
@click.group()
@click.group(cls=AliasedGroup)
@click.version_option(__version__)
@click.pass_context
@click.option('--token', show_envvar=True, default=None, help='Arista Token from your customer account')
@click.option(
"--token",
show_envvar=True,
default=None,
help="Arista Token from your customer account",
)
def ardl(ctx: click.Context, token: str) -> None:
"""Arista Network Download CLI"""
ctx.ensure_object(dict)
ctx.obj['token'] = token
ctx.obj["token"] = token
@click.command()
def version() -> None:
"""Display version of ardl"""
console = Console()
console.print(f'ardl is running version {eos_downloader.__version__}')
@ardl.group(no_args_is_help=True)
@ardl.group(cls=AliasedGroup, no_args_is_help=True)
@click.pass_context
def get(ctx: click.Context) -> None:
def get(ctx: click.Context, cls: click.Group = AliasedGroup) -> None:
# pylint: disable=redefined-builtin
"""Download Arista from Arista website"""
@ardl.group(no_args_is_help=True)
@ardl.group(cls=AliasedGroup, no_args_is_help=True)
@click.pass_context
def info(ctx: click.Context) -> None:
def info(ctx: click.Context, cls: click.Group = AliasedGroup) -> None:
# pylint: disable=redefined-builtin
"""List information from Arista website"""
@ardl.group(no_args_is_help=True)
@ardl.group(cls=AliasedGroup, no_args_is_help=True)
@click.pass_context
def debug(ctx: click.Context) -> None:
def debug(ctx: click.Context, cls: click.Group = AliasedGroup) -> None:
# pylint: disable=redefined-builtin
"""Debug commands to work with ardl"""
# ANTA CLI Execution
@ -64,13 +66,9 @@ def cli() -> None:
get.add_command(get_commands.cvp)
info.add_command(info_commands.eos_versions)
debug.add_command(debug_commands.xml)
ardl.add_command(version)
# Load CLI
ardl(
obj={},
auto_envvar_prefix='arista'
)
ardl(obj={}, auto_envvar_prefix="arista")
if __name__ == '__main__':
if __name__ == "__main__":
cli()

View file

@ -22,32 +22,51 @@ import eos_downloader.eos
@click.command()
@click.pass_context
@click.option('--output', default=str('arista.xml'), help='Path to save XML file', type=click.Path(), show_default=True)
@click.option('--log-level', '--log', help='Logging level of the command', default=None, type=click.Choice(['debug', 'info', 'warning', 'error', 'critical'], case_sensitive=False))
@click.option(
"--output",
default=str("arista.xml"),
help="Path to save XML file",
type=click.Path(),
show_default=True,
)
@click.option(
"--log-level",
"--log",
help="Logging level of the command",
default=None,
type=click.Choice(
["debug", "info", "warning", "error", "critical"], case_sensitive=False
),
)
def xml(ctx: click.Context, output: str, log_level: str) -> None:
# sourcery skip: remove-unnecessary-cast
"""Extract XML directory structure"""
console = Console()
# Get from Context
token = ctx.obj['token']
token = ctx.obj["token"]
logger.remove()
if log_level is not None:
logger.add("eos-downloader.log", rotation="10 MB", level=log_level.upper())
my_download = eos_downloader.eos.EOSDownloader(
image='unset',
software='EOS',
version='unset',
image="unset",
software="EOS",
version="unset",
token=token,
hash_method='sha512sum')
hash_method="sha512sum",
)
my_download.authenticate()
xml_object: ET.ElementTree = my_download._get_folder_tree() # pylint: disable=protected-access
xml_object: ET.ElementTree = (
my_download.get_folder_tree()
) # pylint: disable=protected-access
xml_content = xml_object.getroot()
xmlstr = minidom.parseString(ET.tostring(xml_content)).toprettyxml(indent=" ", newl='')
with open(output, "w", encoding='utf-8') as f:
xmlstr = minidom.parseString(ET.tostring(xml_content)).toprettyxml(
indent=" ", newl=""
)
with open(output, "w", encoding="utf-8") as f:
f.write(str(xmlstr))
console.print(f'XML file saved in: { output }')
console.print(f"XML file saved in: { output }")

View file

@ -21,68 +21,156 @@ from rich.console import Console
import eos_downloader.eos
from eos_downloader.models.version import BASE_VERSION_STR, RTYPE_FEATURE, RTYPES
EOS_IMAGE_TYPE = ['64', 'INT', '2GB-INT', 'cEOS', 'cEOS64', 'vEOS', 'vEOS-lab', 'EOS-2GB', 'default']
CVP_IMAGE_TYPE = ['ova', 'rpm', 'kvm', 'upgrade']
EOS_IMAGE_TYPE = [
"64",
"INT",
"2GB-INT",
"cEOS",
"cEOS64",
"vEOS",
"vEOS-lab",
"EOS-2GB",
"default",
]
CVP_IMAGE_TYPE = ["ova", "rpm", "kvm", "upgrade"]
@click.command(no_args_is_help=True)
@click.pass_context
@click.option('--image-type', default='default', help='EOS Image type', type=click.Choice(EOS_IMAGE_TYPE), required=True)
@click.option('--version', default=None, help='EOS version', type=str, required=False)
@click.option('--latest', '-l', is_flag=True, type=click.BOOL, default=False, help='Get latest version in given branch. If --branch is not use, get the latest branch with specific release type')
@click.option('--release-type', '-rtype', type=click.Choice(RTYPES, case_sensitive=False), default=RTYPE_FEATURE, help='EOS release type to search')
@click.option('--branch', '-b', type=click.STRING, default=None, help='EOS Branch to list releases')
@click.option('--docker-name', default='arista/ceos', help='Docker image name (default: arista/ceos)', type=str, show_default=True)
@click.option('--output', default=str(os.path.relpath(os.getcwd(), start=os.curdir)), help='Path to save image', type=click.Path(),show_default=True)
@click.option(
"--image-type",
default="default",
help="EOS Image type",
type=click.Choice(EOS_IMAGE_TYPE),
required=True,
)
@click.option("--version", default=None, help="EOS version", type=str, required=False)
@click.option(
"--latest",
"-l",
is_flag=True,
type=click.BOOL,
default=False,
help="Get latest version in given branch. If --branch is not use, get the latest branch with specific release type",
)
@click.option(
"--release-type",
"-rtype",
type=click.Choice(RTYPES, case_sensitive=False),
default=RTYPE_FEATURE,
help="EOS release type to search",
)
@click.option(
"--branch",
"-b",
type=click.STRING,
default=None,
help="EOS Branch to list releases",
)
@click.option(
"--docker-name",
default="arista/ceos",
help="Docker image name (default: arista/ceos)",
type=str,
show_default=True,
)
@click.option(
"--output",
default=str(os.path.relpath(os.getcwd(), start=os.curdir)),
help="Path to save image",
type=click.Path(),
show_default=True,
)
# Debugging
@click.option('--log-level', '--log', help='Logging level of the command', default=None, type=click.Choice(['debug', 'info', 'warning', 'error', 'critical'], case_sensitive=False))
@click.option(
"--log-level",
"--log",
help="Logging level of the command",
default=None,
type=click.Choice(
["debug", "info", "warning", "error", "critical"], case_sensitive=False
),
)
# Boolean triggers
@click.option('--eve-ng', is_flag=True, help='Run EVE-NG vEOS provisioning (only if CLI runs on an EVE-NG server)', default=False)
@click.option('--disable-ztp', is_flag=True, help='Disable ZTP process in vEOS image (only available with --eve-ng)', default=False)
@click.option('--import-docker', is_flag=True, help='Import docker image (only available with --image_type cEOSlab)', default=False)
@click.option(
"--eve-ng",
is_flag=True,
help="Run EVE-NG vEOS provisioning (only if CLI runs on an EVE-NG server)",
default=False,
)
@click.option(
"--disable-ztp",
is_flag=True,
help="Disable ZTP process in vEOS image (only available with --eve-ng)",
default=False,
)
@click.option(
"--import-docker",
is_flag=True,
help="Import docker image (only available with --image_type cEOSlab)",
default=False,
)
def eos(
ctx: click.Context, image_type: str, output: str, log_level: str, eve_ng: bool, disable_ztp: bool,
import_docker: bool, docker_name: str, version: Union[str, None] = None, release_type: str = RTYPE_FEATURE,
latest: bool = False, branch: Union[str,None] = None
) -> int:
ctx: click.Context,
image_type: str,
output: str,
log_level: str,
eve_ng: bool,
disable_ztp: bool,
import_docker: bool,
docker_name: str,
version: Union[str, None] = None,
release_type: str = RTYPE_FEATURE,
latest: bool = False,
branch: Union[str, None] = None,
) -> int:
"""Download EOS image from Arista website"""
console = Console()
# Get from Context
token = ctx.obj['token']
if token is None or token == '':
console.print('❗ Token is unset ! Please configure ARISTA_TOKEN or use --token option', style="bold red")
token = ctx.obj["token"]
if token is None or token == "":
console.print(
"❗ Token is unset ! Please configure ARISTA_TOKEN or use --token option",
style="bold red",
)
sys.exit(1)
logger.remove()
if log_level is not None:
logger.add("eos-downloader.log", rotation="10 MB", level=log_level.upper())
console.print("🪐 [bold blue]eos-downloader[/bold blue] is starting...", )
console.print(f' - Image Type: {image_type}')
console.print(f' - Version: {version}')
console.print(
"🪐 [bold blue]eos-downloader[/bold blue] is starting...",
)
console.print(f" - Image Type: {image_type}")
console.print(f" - Version: {version}")
if version is not None:
my_download = eos_downloader.eos.EOSDownloader(
image=image_type,
software='EOS',
software="EOS",
version=version,
token=token,
hash_method='sha512sum')
hash_method="sha512sum",
)
my_download.authenticate()
elif latest:
my_download = eos_downloader.eos.EOSDownloader(
image=image_type,
software='EOS',
version='unset',
software="EOS",
version="unset",
token=token,
hash_method='sha512sum')
hash_method="sha512sum",
)
my_download.authenticate()
if branch is None:
branch = str(my_download.latest_branch(rtype=release_type).branch)
latest_version = my_download.latest_eos(branch, rtype=release_type)
if str(latest_version) == BASE_VERSION_STR:
console.print(f'[red]Error[/red], cannot find any version in {branch} for {release_type} release type')
console.print(
f"[red]Error[/red], cannot find any version in {branch} for {release_type} release type"
)
sys.exit(1)
my_download.version = str(latest_version)
@ -92,46 +180,71 @@ def eos(
my_download.download_local(file_path=output, checksum=True)
if import_docker:
my_download.docker_import(
image_name=docker_name
)
console.print('✅ processing done !')
my_download.docker_import(image_name=docker_name)
console.print("✅ processing done !")
sys.exit(0)
@click.command(no_args_is_help=True)
@click.pass_context
@click.option('--format', default='upgrade', help='CVP Image type', type=click.Choice(CVP_IMAGE_TYPE), required=True)
@click.option('--version', default=None, help='CVP version', type=str, required=True)
@click.option('--output', default=str(os.path.relpath(os.getcwd(), start=os.curdir)), help='Path to save image', type=click.Path(),show_default=True)
@click.option('--log-level', '--log', help='Logging level of the command', default=None, type=click.Choice(['debug', 'info', 'warning', 'error', 'critical'], case_sensitive=False))
def cvp(ctx: click.Context, version: str, format: str, output: str, log_level: str) -> int:
@click.option(
"--format",
default="upgrade",
help="CVP Image type",
type=click.Choice(CVP_IMAGE_TYPE),
required=True,
)
@click.option("--version", default=None, help="CVP version", type=str, required=True)
@click.option(
"--output",
default=str(os.path.relpath(os.getcwd(), start=os.curdir)),
help="Path to save image",
type=click.Path(),
show_default=True,
)
@click.option(
"--log-level",
"--log",
help="Logging level of the command",
default=None,
type=click.Choice(
["debug", "info", "warning", "error", "critical"], case_sensitive=False
),
)
def cvp(
ctx: click.Context, version: str, format: str, output: str, log_level: str
) -> int:
"""Download CVP image from Arista website"""
console = Console()
# Get from Context
token = ctx.obj['token']
if token is None or token == '':
console.print('❗ Token is unset ! Please configure ARISTA_TOKEN or use --token option', style="bold red")
token = ctx.obj["token"]
if token is None or token == "":
console.print(
"❗ Token is unset ! Please configure ARISTA_TOKEN or use --token option",
style="bold red",
)
sys.exit(1)
logger.remove()
if log_level is not None:
logger.add("eos-downloader.log", rotation="10 MB", level=log_level.upper())
console.print("🪐 [bold blue]eos-downloader[/bold blue] is starting...", )
console.print(f' - Image Type: {format}')
console.print(f' - Version: {version}')
console.print(
"🪐 [bold blue]eos-downloader[/bold blue] is starting...",
)
console.print(f" - Image Type: {format}")
console.print(f" - Version: {version}")
my_download = eos_downloader.eos.EOSDownloader(
image=format,
software='CloudVision',
software="CloudVision",
version=version,
token=token,
hash_method='md5sum')
hash_method="md5sum",
)
my_download.authenticate()
my_download.download_local(file_path=output, checksum=False)
console.print('✅ processing done !')
console.print("✅ processing done !")
sys.exit(0)

View file

@ -24,12 +24,53 @@ from eos_downloader.models.version import BASE_VERSION_STR, RTYPE_FEATURE, RTYPE
@click.command(no_args_is_help=True)
@click.pass_context
@click.option('--latest', '-l', is_flag=True, type=click.BOOL, default=False, help='Get latest version in given branch. If --branch is not use, get the latest branch with specific release type')
@click.option('--release-type', '-rtype', type=click.Choice(RTYPES, case_sensitive=False), default=RTYPE_FEATURE, help='EOS release type to search')
@click.option('--branch', '-b', type=click.STRING, default=None, help='EOS Branch to list releases')
@click.option('--verbose', '-v', is_flag=True, type=click.BOOL, default=False, help='Human readable output. Default is none to use output in script)')
@click.option('--log-level', '--log', help='Logging level of the command', default='warning', type=click.Choice(['debug', 'info', 'warning', 'error', 'critical'], case_sensitive=False))
def eos_versions(ctx: click.Context, log_level: str, branch: Union[str,None] = None, release_type: str = RTYPE_FEATURE, latest: bool = False, verbose: bool = False) -> None:
@click.option(
"--latest",
"-l",
is_flag=True,
type=click.BOOL,
default=False,
help="Get latest version in given branch. If --branch is not use, get the latest branch with specific release type",
)
@click.option(
"--release-type",
"-rtype",
type=click.Choice(RTYPES, case_sensitive=False),
default=RTYPE_FEATURE,
help="EOS release type to search",
)
@click.option(
"--branch",
"-b",
type=click.STRING,
default=None,
help="EOS Branch to list releases",
)
@click.option(
"--verbose",
"-v",
is_flag=True,
type=click.BOOL,
default=False,
help="Human readable output. Default is none to use output in script)",
)
@click.option(
"--log-level",
"--log",
help="Logging level of the command",
default="warning",
type=click.Choice(
["debug", "info", "warning", "error", "critical"], case_sensitive=False
),
)
def eos_versions(
ctx: click.Context,
log_level: str,
branch: Union[str, None] = None,
release_type: str = RTYPE_FEATURE,
latest: bool = False,
verbose: bool = False,
) -> None:
# pylint: disable = too-many-branches
"""
List Available EOS version on Arista.com website.
@ -42,22 +83,23 @@ def eos_versions(ctx: click.Context, log_level: str, branch: Union[str,None] = N
"""
console = Console()
# Get from Context
token = ctx.obj['token']
token = ctx.obj["token"]
logger.remove()
if log_level is not None:
logger.add("eos-downloader.log", rotation="10 MB", level=log_level.upper())
my_download = eos_downloader.eos.EOSDownloader(
image='unset',
software='EOS',
version='unset',
image="unset",
software="EOS",
version="unset",
token=token,
hash_method='sha512sum')
hash_method="sha512sum",
)
auth = my_download.authenticate()
if verbose and auth:
console.print('✅ Authenticated on arista.com')
console.print("✅ Authenticated on arista.com")
if release_type is not None:
release_type = release_type.upper()
@ -67,21 +109,27 @@ def eos_versions(ctx: click.Context, log_level: str, branch: Union[str,None] = N
branch = str(my_download.latest_branch(rtype=release_type).branch)
latest_version = my_download.latest_eos(branch, rtype=release_type)
if str(latest_version) == BASE_VERSION_STR:
console.print(f'[red]Error[/red], cannot find any version in {branch} for {release_type} release type')
console.print(
f"[red]Error[/red], cannot find any version in {branch} for {release_type} release type"
)
sys.exit(1)
if verbose:
console.print(f'Branch {branch} has been selected with release type {release_type}')
console.print(
f"Branch {branch} has been selected with release type {release_type}"
)
if branch is not None:
console.print(f'Latest release for {branch}: {latest_version}')
console.print(f"Latest release for {branch}: {latest_version}")
else:
console.print(f'Latest EOS release: {latest_version}')
console.print(f"Latest EOS release: {latest_version}")
else:
console.print(f'{ latest_version }')
console.print(f"{ latest_version }")
else:
versions = my_download.get_eos_versions(branch=branch, rtype=release_type)
if verbose:
console.print(f'List of available versions for {branch if branch is not None else "all branches"}')
console.print(
f'List of available versions for {branch if branch is not None else "all branches"}'
)
for version in versions:
console.print(f'{str(version)}')
console.print(f"{str(version)}")
else:
pprint([str(version) for version in versions])

View file

@ -0,0 +1,38 @@
#!/usr/bin/python
# coding: utf-8 -*-
# pylint: disable=inconsistent-return-statements
"""
Extension for the python ``click`` module
to provide a group or command with aliases.
"""
from typing import Any
import click
class AliasedGroup(click.Group):
"""
Implements a subclass of Group that accepts a prefix for a command.
If there were a command called push, it would accept pus as an alias (so long as it was unique)
"""
def get_command(self, ctx: click.Context, cmd_name: str) -> Any:
"""Documentation to build"""
rv = click.Group.get_command(self, ctx, cmd_name)
if rv is not None:
return rv
matches = [x for x in self.list_commands(ctx)
if x.startswith(cmd_name)]
if not matches:
return None
if len(matches) == 1:
return click.Group.get_command(self, ctx, matches[0])
ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")
def resolve_command(self, ctx: click.Context, args: Any) -> Any:
"""Documentation to build"""
# always return the full command name
_, cmd, args = super().resolve_command(ctx, args)
return cmd.name, cmd, args