From 6e083d67869a53167e2a11fab4fb2320b790649a Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 19:41:38 +0000 Subject: [PATCH 01/14] [sonic-installer] Hyphens instead of underscores in commands/subcommands --- setup.py | 3 +- sonic_installer/aliases.ini | 6 ++ sonic_installer/main.py | 126 ++++++++++++++++++++++++++---------- 3 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 sonic_installer/aliases.ini diff --git a/setup.py b/setup.py index 38090071b0..a023457f5c 100644 --- a/setup.py +++ b/setup.py @@ -135,7 +135,8 @@ 'pddf_ledutil = pddf_ledutil.main:cli', 'show = show.main:cli', 'sonic-clear = clear.main:cli', - 'sonic_installer = sonic_installer.main:cli', + 'sonic-installer = sonic_installer.main:sonic_installer', + 'sonic_installer = sonic_installer.main:sonic_installer', 'undebug = undebug.main:cli', 'watchdogutil = watchdogutil.main:watchdogutil', ] diff --git a/sonic_installer/aliases.ini b/sonic_installer/aliases.ini new file mode 100644 index 0000000000..ebe917057b --- /dev/null +++ b/sonic_installer/aliases.ini @@ -0,0 +1,6 @@ +[aliases] +set_default=set-default +set_next_boot=set-next-boot +binary_version=binary-version +upgrade_docker=upgrade-docker +rollback_docker=rollback-docker diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 3c68bcd843..a2c32c97fc 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -12,6 +12,50 @@ from .bootloader import get_bootloader from .common import run_command + +# Global Config object +_config = None + + +class AliasedGroup(click.Group): + """This subclass of click.Group supports abbreviations and + looking up aliases in a config file with a bit of magic. + """ + + def get_command(self, ctx, cmd_name): + global _config + + # If we haven't instantiated our global config, do it now and load current config + if _config is None: + _config = Config() + + # Load our config file + cfg_file = os.path.join(os.path.dirname(__file__), 'aliases.ini') + _config.read_config(cfg_file) + + # Try to get builtin commands as normal + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + + # No builtin found. Look up an explicit command alias in the config + if cmd_name in _config.aliases: + actual_cmd = _config.aliases[cmd_name] + return click.Group.get_command(self, ctx, actual_cmd) + + # Alternative option: if we did not find an explicit alias we + # allow automatic abbreviation of the command. "status" for + # instance will match "st". We only allow that however if + # there is only one command. + matches = [x for x in self.list_commands(ctx) + if x.lower().startswith(cmd_name.lower())] + if not matches: + return None + elif len(matches) == 1: + return click.Group.get_command(self, ctx, matches[0]) + ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + + # # Helper functions # @@ -37,9 +81,10 @@ def reporthook(count, block_size, total_size): percent = int(count * block_size * 100 / total_size) time_left = (total_size - progress_size) / speed / 1024 sys.stdout.write("\r...%d%%, %d MB, %d KB/s, %d seconds left... " % - (percent, progress_size / (1024 * 1024), speed, time_left)) + (percent, progress_size / (1024 * 1024), speed, time_left)) sys.stdout.flush() + # TODO: Embed tag name info into docker image meta data at build time, # and extract tag name from docker image file. def get_docker_tag_name(image): @@ -54,6 +99,7 @@ def get_docker_tag_name(image): return "unknown" return tag + # Function which validates whether a given URL specifies an existent file # on a reachable remote machine. Will abort the current operation if not def validate_url_or_abort(url): @@ -74,11 +120,13 @@ def validate_url_or_abort(url): click.echo("Image file not found on remote machine. Aborting...") raise click.Abort() + # Callback for confirmation prompt. Aborts if user enters "n" def abort_if_false(ctx, param, value): if not value: ctx.abort() + def get_container_image_name(container_name): # example image: docker-lldp-sv2:latest cmd = "docker inspect --format '{{.Config.Image}}' " + container_name @@ -94,6 +142,7 @@ def get_container_image_name(container_name): image_name = proc.stdout.read().rstrip() return image_name + def get_container_image_id(image_tag): # TODO: extract commond docker info fetching functions # this is image_id for image with tag, like 'docker-teamd:latest' @@ -102,6 +151,7 @@ def get_container_image_id(image_tag): image_id = proc.stdout.read().rstrip() return image_id + def get_container_image_id_all(image_name): # All images id under the image name like 'docker-teamd' cmd = "docker images --format '{{.ID}}' " + image_name @@ -111,6 +161,7 @@ def get_container_image_id_all(image_name): image_id_all = set(image_id_all) return image_id_all + def hget_warm_restart_table(db_name, table_name, warm_app_name, key): db = SonicV2Connector() db.connect(db_name, False) @@ -118,29 +169,31 @@ def hget_warm_restart_table(db_name, table_name, warm_app_name, key): client = db.get_redis_client(db_name) return client.hget(_hash, key) + def hdel_warm_restart_table(db_name, table_name, warm_app_name, key): db = SonicV2Connector() db.connect(db_name, False) _hash = table_name + db.get_db_separator(db_name) + warm_app_name client = db.get_redis_client(db_name) - return client.hdel(_hash, key) + return client.hdel(_hash, key) + # Main entrypoint -@click.group() -def cli(): +@click.group(cls=AliasedGroup) +def sonic_installer(): """ SONiC image installation manager """ if os.geteuid() != 0: exit("Root privileges required for this operation") # Install image -@cli.command() +@sonic_installer.command('install') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, - expose_value=False, prompt='New image will be installed, continue?') + expose_value=False, prompt='New image will be installed, continue?') @click.option('-f', '--force', is_flag=True, - help="Force installation of an image of a type which differs from that of the current running image") + help="Force installation of an image of a type which differs from that of the current running image") @click.option('--skip_migration', is_flag=True, - help="Do not migrate current configuration to the newly installed image") + help="Do not migrate current configuration to the newly installed image") @click.argument('url') def install(url, force, skip_migration=False): """ Install image from local binary or URL""" @@ -188,12 +241,12 @@ def install(url, force, skip_migration=False): # Finally, sync filesystem run_command("sync;sync;sync") - run_command("sleep 3") # wait 3 seconds after sync + run_command("sleep 3") # wait 3 seconds after sync click.echo('Done') # List installed images -@cli.command('list') +@sonic_installer.command('list') def list_command(): """ Print installed images """ bootloader = get_bootloader() @@ -206,8 +259,9 @@ def list_command(): for image in images: click.echo(image) + # Set default image for boot -@cli.command('set_default') +@sonic_installer.command('set-default') @click.argument('image') def set_default(image): """ Choose image to boot from by default """ @@ -217,8 +271,9 @@ def set_default(image): raise click.Abort() bootloader.set_default_image(image) + # Set image for next boot -@cli.command('set_next_boot') +@sonic_installer.command('set-next-boot') @click.argument('image') def set_next_boot(image): """ Choose image for next reboot (one time action) """ @@ -228,10 +283,11 @@ def set_next_boot(image): sys.exit(1) bootloader.set_next_image(image) + # Uninstall image -@cli.command() +@sonic_installer.command('remove') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, - expose_value=False, prompt='Image will be removed, continue?') + expose_value=False, prompt='Image will be removed, continue?') @click.argument('image') def remove(image): """ Uninstall image """ @@ -247,8 +303,9 @@ def remove(image): # TODO: check if image is next boot or default boot and fix these bootloader.remove_image(image) + # Retrieve version from binary image file and print to screen -@cli.command('binary_version') +@sonic_installer.command('binary-version') @click.argument('binary_image_path') def binary_version(binary_image_path): """ Get version from local binary image file """ @@ -260,10 +317,11 @@ def binary_version(binary_image_path): else: click.echo(version) + # Remove installed images which are not current and next -@cli.command() +@sonic_installer.command('cleanup') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, - expose_value=False, prompt='Remove images which are not current and next, continue?') + expose_value=False, prompt='Remove images which are not current and next, continue?') def cleanup(): """ Remove installed images which are not current and next """ bootloader = get_bootloader() @@ -280,16 +338,17 @@ def cleanup(): if image_removed == 0: click.echo("No image(s) to remove") + # Upgrade docker image -@cli.command('upgrade_docker') +@sonic_installer.command('upgrade-docker') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, - expose_value=False, prompt='New docker image will be installed, continue?') + expose_value=False, prompt='New docker image will be installed, continue?') @click.option('--cleanup_image', is_flag=True, help="Clean up old docker image") @click.option('--skip_check', is_flag=True, help="Skip task check for docker upgrade") @click.option('--tag', type=str, help="Tag for the new docker image") @click.option('--warm', is_flag=True, help="Perform warm upgrade") @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) + type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) @click.argument('url') def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): """ Upgrade docker image from local binary or URL""" @@ -329,8 +388,8 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): state_db.close(state_db.STATE_DB) if container_name == "swss" or container_name == "bgp" or container_name == "teamd": - if warm_configured == False and warm: - run_command("config warm_restart enable %s" % container_name) + if warm_configured is False and warm: + run_command("config warm_restart enable %s" % container_name) # Fetch tag of current running image tag_previous = get_docker_tag_name(image_latest) @@ -338,7 +397,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): run_command("docker load < %s" % image_path) warm_app_names = [] # warm restart specific procssing for swss, bgp and teamd dockers. - if warm_configured == True or warm: + if warm_configured is True or warm: # make sure orchagent is in clean state if swss is to be upgraded if container_name == "swss": skipPendingTaskCheck = "" @@ -353,7 +412,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): if not skip_check: click.echo("Orchagent is not in clean state, RESTARTCHECK failed") # Restore orignal config before exit - if warm_configured == False and warm: + if warm_configured is False and warm: run_command("config warm_restart disable %s" % container_name) # Clean the image loaded earlier image_id_latest = get_container_image_id(image_latest) @@ -413,7 +472,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): exp_state = "reconciled" state = "" # post warm restart specific procssing for swss, bgp and teamd dockers, wait for reconciliation state. - if warm_configured == True or warm: + if warm_configured is True or warm: count = 0 for warm_app_name in warm_app_names: state = "" @@ -425,16 +484,16 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): count += 1 time.sleep(2) state = hget_warm_restart_table("STATE_DB", "WARM_RESTART_TABLE", warm_app_name, "state") - syslog.syslog("%s reached %s state"%(warm_app_name, state)) + syslog.syslog("%s reached %s state" % (warm_app_name, state)) sys.stdout.write("]\n\r") if state != exp_state: - click.echo("%s failed to reach %s state"%(warm_app_name, exp_state)) - syslog.syslog(syslog.LOG_ERR, "%s failed to reach %s state"%(warm_app_name, exp_state)) + click.echo("%s failed to reach %s state" % (warm_app_name, exp_state)) + syslog.syslog(syslog.LOG_ERR, "%s failed to reach %s state" % (warm_app_name, exp_state)) else: exp_state = "" # this is cold upgrade # Restore to previous cold restart setting - if warm_configured == False and warm: + if warm_configured is False and warm: if container_name == "swss" or container_name == "bgp" or container_name == "teamd": run_command("config warm_restart disable %s" % container_name) @@ -444,12 +503,13 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): click.echo('Failed') sys.exit(1) + # rollback docker image -@cli.command('rollback_docker') +@sonic_installer.command('rollback-docker') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, - expose_value=False, prompt='Docker image will be rolled back, continue?') + expose_value=False, prompt='Docker image will be rolled back, continue?') @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) + type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) def rollback_docker(container_name): """ Rollback docker image to previous version""" image_name = get_container_image_name(container_name) @@ -477,4 +537,4 @@ def rollback_docker(container_name): click.echo('Done') if __name__ == '__main__': - cli() + sonic_installer() From 9e601620c5ff1cb3a40c01fd2443ab419d9db3da Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 20:39:32 +0000 Subject: [PATCH 02/14] Add deprecation warning if the user uses an underscore --- sonic_installer/main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index a2c32c97fc..afd19b99c3 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -185,6 +185,11 @@ def sonic_installer(): if os.geteuid() != 0: exit("Root privileges required for this operation") + # Warn the user if they are calling the deprecated version of the command (with an underscore instead of a hyphen) + if os.path.basename(sys.argv[0]) == "sonic_installer": + click.secho("Warning: 'sonic_installer' is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'sonic-installer' instead", fg="red", err=True) + # Install image @sonic_installer.command('install') From 3bacbfe617dfbecb8f4ab4454c802f73158035bc Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 20:57:05 +0000 Subject: [PATCH 03/14] Add missing config parsing --- sonic_installer/main.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index afd19b99c3..4e6e5ac47f 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -12,11 +12,34 @@ from .bootloader import get_bootloader from .common import run_command +try: + import ConfigParser as configparser +except ImportError: + import configparser + # Global Config object _config = None +# This is from the aliases example: +# https://github.com/pallets/click/blob/57c6f09611fc47ca80db0bd010f05998b3c0aa95/examples/aliases/aliases.py +class Config(object): + """Object to hold CLI config""" + + def __init__(self): + self.path = os.getcwd() + self.aliases = {} + + def read_config(self, filename): + parser = configparser.RawConfigParser() + parser.read([filename]) + try: + self.aliases.update(parser.items('aliases')) + except configparser.NoSectionError: + pass + + class AliasedGroup(click.Group): """This subclass of click.Group supports abbreviations and looking up aliases in a config file with a bit of magic. From 4041af365713957b2361caa682087ca22c69915a Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 21:14:06 +0000 Subject: [PATCH 04/14] Add deprecation warnings for subcommands with underscores --- sonic_installer/main.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 4e6e5ac47f..16059ecc6a 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -210,7 +210,7 @@ def sonic_installer(): # Warn the user if they are calling the deprecated version of the command (with an underscore instead of a hyphen) if os.path.basename(sys.argv[0]) == "sonic_installer": - click.secho("Warning: 'sonic_installer' is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Warning: 'sonic_installer' command is deprecated and will be removed in the future", fg="red", err=True) click.secho("Please use 'sonic-installer' instead", fg="red", err=True) @@ -293,6 +293,11 @@ def list_command(): @click.argument('image') def set_default(image): """ Choose image to boot from by default """ + # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) + if "set_default" in sys.argv: + click.secho("Warning: 'set_default' subcommand is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'set-default' instead", fg="red", err=True) + bootloader = get_bootloader() if image not in bootloader.get_installed_images(): click.echo('Error: Image does not exist') @@ -305,6 +310,11 @@ def set_default(image): @click.argument('image') def set_next_boot(image): """ Choose image for next reboot (one time action) """ + # Warn the user if they are calling the deprecated version of the subcommand (with underscores instead of hyphens) + if "set_next_boot" in sys.argv: + click.secho("Warning: 'set_next_boot' subcommand is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'set-next-boot' instead", fg="red", err=True) + bootloader = get_bootloader() if image not in bootloader.get_installed_images(): click.echo('Error: Image does not exist') @@ -337,6 +347,11 @@ def remove(image): @click.argument('binary_image_path') def binary_version(binary_image_path): """ Get version from local binary image file """ + # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) + if "binary_version" in sys.argv: + click.secho("Warning: 'binary_version' subcommand is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'binary-version' instead", fg="red", err=True) + bootloader = get_bootloader() version = bootloader.get_binary_image_version(binary_image_path) if not version: @@ -380,6 +395,10 @@ def cleanup(): @click.argument('url') def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): """ Upgrade docker image from local binary or URL""" + # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) + if "upgrade_docker" in sys.argv: + click.secho("Warning: 'upgrade_docker' subcommand is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'upgrade-docker' instead", fg="red", err=True) image_name = get_container_image_name(container_name) image_latest = image_name + ":latest" @@ -540,6 +559,11 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) def rollback_docker(container_name): """ Rollback docker image to previous version""" + # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) + if "rollback_docker" in sys.argv: + click.secho("Warning: 'rollback_docker' subcommand is deprecated and will be removed in the future", fg="red", err=True) + click.secho("Please use 'rollback-docker' instead", fg="red", err=True) + image_name = get_container_image_name(container_name) # All images id under the image name image_id_all = get_container_image_id_all(image_name) From d25594ccb2805e96962e8830280568b025487991 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 21:31:13 +0000 Subject: [PATCH 05/14] Remove try/catch which is no longer needed --- sonic_installer/main.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 16059ecc6a..c19bbf0367 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -1,5 +1,6 @@ #! /usr/bin/python -u +import configparser import os import sys import time @@ -12,11 +13,6 @@ from .bootloader import get_bootloader from .common import run_command -try: - import ConfigParser as configparser -except ImportError: - import configparser - # Global Config object _config = None From f30e7b99eef176515616ab5c358b0629e3df1c06 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 21:34:01 +0000 Subject: [PATCH 06/14] Organize imports --- sonic_installer/main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index c19bbf0367..2b86bdd680 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -2,12 +2,13 @@ import configparser import os +import subprocess import sys +import syslog import time -import click import urllib -import syslog -import subprocess + +import click from swsssdk import SonicV2Connector from .bootloader import get_bootloader From 7f31a3c44ef7bb6d5a378f3f9946d040eaab92aa Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 21:44:33 +0000 Subject: [PATCH 07/14] Eliminate duplicate code by adding DOCKER_CONTAINER_LIST; Remove unknown 'amon' docker --- sonic_installer/main.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 2b86bdd680..21461c0d47 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -379,6 +379,22 @@ def cleanup(): click.echo("No image(s) to remove") +DOCKER_CONTAINER_LIST = [ + "bgp", + "dhcp_relay", + "lldp", + "nat", + "pmon", + "radv", + "restapi", + "sflow" + "snmp", + "swss", + "syncd", + "teamd", + "telemetry" +] + # Upgrade docker image @sonic_installer.command('upgrade-docker') @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, @@ -388,7 +404,7 @@ def cleanup(): @click.option('--tag', type=str, help="Tag for the new docker image") @click.option('--warm', is_flag=True, help="Perform warm upgrade") @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) + type=click.Choice(DOCKER_CONTAINER_LIST)) @click.argument('url') def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): """ Upgrade docker image from local binary or URL""" @@ -553,7 +569,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, expose_value=False, prompt='Docker image will be rolled back, continue?') @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) + type=click.Choice(DOCKER_CONTAINER_LIST)) def rollback_docker(container_name): """ Rollback docker image to previous version""" # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) From 6a66fd2081369ce19c9042b63727c75bc25d30b1 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 22:14:38 +0000 Subject: [PATCH 08/14] Add try/catch back in, as it is still needed for Python 2 --- sonic_installer/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 21461c0d47..a5265e2c6b 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -1,6 +1,10 @@ #! /usr/bin/python -u -import configparser +try: + import ConfigParser as configparser +except ImportError: + import configparser + import os import subprocess import sys From 1920f47924b88a27e1c7c161a67ece3515c6eff4 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sat, 11 Jul 2020 22:18:34 +0000 Subject: [PATCH 09/14] Add missing comma --- sonic_installer/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index a5265e2c6b..b69c44b036 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -391,7 +391,7 @@ def cleanup(): "pmon", "radv", "restapi", - "sflow" + "sflow", "snmp", "swss", "syncd", From b40dfe8055c953bdff41a09dae9624acac78bac8 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sun, 12 Jul 2020 21:10:44 +0000 Subject: [PATCH 10/14] Add deprecation comment --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a023457f5c..b593eedd48 100644 --- a/setup.py +++ b/setup.py @@ -136,7 +136,7 @@ 'show = show.main:cli', 'sonic-clear = clear.main:cli', 'sonic-installer = sonic_installer.main:sonic_installer', - 'sonic_installer = sonic_installer.main:sonic_installer', + 'sonic_installer = sonic_installer.main:sonic_installer', # Deprecated 'undebug = undebug.main:cli', 'watchdogutil = watchdogutil.main:watchdogutil', ] From ca471a53c95ee534c0cba1357e6c93d0f3de57d4 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sun, 12 Jul 2020 21:13:43 +0000 Subject: [PATCH 11/14] Add bash completion file for sonic-installer --- data/etc/bash_completion.d/sonic-installer | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/etc/bash_completion.d/sonic-installer diff --git a/data/etc/bash_completion.d/sonic-installer b/data/etc/bash_completion.d/sonic-installer new file mode 100644 index 0000000000..f1e9be7a01 --- /dev/null +++ b/data/etc/bash_completion.d/sonic-installer @@ -0,0 +1,8 @@ +_sonic_installer_completion() { + COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \ + COMP_CWORD=$COMP_CWORD \ + _SONIC_INSTALLER_COMPLETE=complete $1 ) ) + return 0 +} + +complete -F _sonic_installer_completion -o default sonic-installer; From 965429ae8818f142d23330f4bd8d4c8d1a0c683f Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sun, 12 Jul 2020 21:16:09 +0000 Subject: [PATCH 12/14] Update command reference to refer to hyphenated version --- doc/Command-Reference.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 8971445418..30df5c57bb 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -4645,7 +4645,7 @@ Supported options: 2. -f|--force - install FW regardless the current version 3. -i|--image - update FW using current/next SONiC image -Note: the default option is --image=current (current/next values are taken from `sonic_installer list`) +Note: the default option is --image=current (current/next values are taken from `sonic-installer list`) ### Platform Component Firmware vendor specific behaviour @@ -6604,7 +6604,7 @@ Go Back To [Beginning of the document](#) or [Beginning of this section](#waterm ## Software Installation and Management -SONiC software can be installed in two methods, viz, "using sonic_installer tool", "ONIE Installer". +SONiC software can be installed in two methods, viz, "using sonic-installer tool", "ONIE Installer". ### SONiC Installer @@ -6612,18 +6612,18 @@ This is a command line tool available as part of the SONiC software; If the devi This tool has facility to install an alternate image, list the available images and to set the next reboot image. This command requires elevated (root) privileges to run. -**sonic_installer list** +**sonic-installer list** This command displays information about currently installed images. It displays a list of installed images, currently running image and image set to be loaded in next reboot. - Usage: ``` - sonic_installer list + sonic-installer list ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer list + admin@sonic:~$ sudo sonic-installer list Current: SONiC-OS-HEAD.XXXX Next: SONiC-OS-HEAD.XXXX Available: @@ -6633,18 +6633,18 @@ This command displays information about currently installed images. It displays TIP: This output can be obtained without evelated privileges by running the `show boot` command. See [here](#show-system-status) for details. -**sonic_installer install** +**sonic-installer install** This command is used to install a new image on the alternate image partition. This command takes a path to an installable SONiC image or URL and installs the image. - Usage: ``` - sonic_installer install + sonic-installer install ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer install https://sonic-jenkins.westus.cloudapp.azure.com/job/xxxx/job/buildimage-xxxx-all/xxx/artifact/target/sonic-xxxx.bin + admin@sonic:~$ sudo sonic-installer install https://sonic-jenkins.westus.cloudapp.azure.com/job/xxxx/job/buildimage-xxxx-all/xxx/artifact/target/sonic-xxxx.bin New image will be installed, continue? [y/N]: y Downloading image... ...100%, 480 MB, 3357 KB/s, 146 seconds passed @@ -6676,46 +6676,46 @@ This command is used to install a new image on the alternate image partition. T Done ``` -**sonic_installer set_default** +**sonic-installer set_default** This command is be used to change the image which can be loaded by default in all the subsequent reboots. - Usage: ``` - sonic_installer set_default + sonic-installer set_default ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer set_default SONiC-OS-HEAD.XXXX + admin@sonic:~$ sudo sonic-installer set_default SONiC-OS-HEAD.XXXX ``` -**sonic_installer set_next_boot** +**sonic-installer set_next_boot** This command is used to change the image that can be loaded in the *next* reboot only. Note that it will fallback to current image in all other subsequent reboots after the next reboot. - Usage: ``` - sonic_installer set_next_boot + sonic-installer set_next_boot ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer set_next_boot SONiC-OS-HEAD.XXXX + admin@sonic:~$ sudo sonic-installer set_next_boot SONiC-OS-HEAD.XXXX ``` -**sonic_installer remove** +**sonic-installer remove** This command is used to remove the unused SONiC image from the disk. Note that it's *not* allowed to remove currently running image. - Usage: ``` - sonic_installer remove [-y|--yes] + sonic-installer remove [-y|--yes] ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer remove SONiC-OS-HEAD.YYYY + admin@sonic:~$ sudo sonic-installer remove SONiC-OS-HEAD.YYYY Image will be removed, continue? [y/N]: y Updating GRUB... Done @@ -6726,18 +6726,18 @@ This command is used to remove the unused SONiC image from the disk. Note that i Image removed ``` -**sonic_installer cleanup** +**sonic-installer cleanup** This command removes all unused images from the device, leaving only the currently active image and the image which will be booted into next (if different) installed. If there are no images which can be removed, the command will output `No image(s) to remove` - Usage: ``` - sonic_installer cleanup [-y|--yes] + sonic-installer cleanup [-y|--yes] ``` - Example: ``` - admin@sonic:~$ sudo sonic_installer cleanup + admin@sonic:~$ sudo sonic-installer cleanup Remove images which are not current and next, continue? [y/N]: y No image(s) to remove ``` From ea339f15d8e18cd8df2248bbdf848650bbefe32b Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Sun, 12 Jul 2020 21:18:57 +0000 Subject: [PATCH 13/14] Update references in all other utilities to call hyphenated version --- fwutil/lib.py | 4 ++-- scripts/asic_config_check | 4 ++-- scripts/fast-reboot | 2 +- scripts/reboot | 2 +- scripts/sonic-kdump-config | 4 ++-- show/main.py | 2 +- sonic_installer/common.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fwutil/lib.py b/fwutil/lib.py index 9bfc229276..eff432c630 100755 --- a/fwutil/lib.py +++ b/fwutil/lib.py @@ -221,13 +221,13 @@ def __init__(self): self.overlay_mountpoint = self.OVERLAY_MOUNTPOINT_TEMPLATE.format(image_stem) def get_current_image(self): - cmd = "sonic_installer list | grep 'Current: ' | cut -f2 -d' '" + cmd = "sonic-installer list | grep 'Current: ' | cut -f2 -d' '" output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) return output.rstrip(NEWLINE) def get_next_image(self): - cmd = "sonic_installer list | grep 'Next: ' | cut -f2 -d' '" + cmd = "sonic-installer list | grep 'Next: ' | cut -f2 -d' '" output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) return output.rstrip(NEWLINE) diff --git a/scripts/asic_config_check b/scripts/asic_config_check index b2b156b80f..06988da2d7 100755 --- a/scripts/asic_config_check +++ b/scripts/asic_config_check @@ -73,8 +73,8 @@ function ConfirmASICConfigChecksumsMatch() # Main starts here debug "Checking that ASIC configuration has not changed" -CURR_SONIC_IMAGE="$(sonic_installer list | grep "Current: " | cut -f2 -d' ')" -DST_SONIC_IMAGE="$(sonic_installer list | grep "Next: " | cut -f2 -d' ')" +CURR_SONIC_IMAGE="$(sonic-installer list | grep "Current: " | cut -f2 -d' ')" +DST_SONIC_IMAGE="$(sonic-installer list | grep "Next: " | cut -f2 -d' ')" if [[ "${CURR_SONIC_IMAGE}" == "${DST_SONIC_IMAGE}" ]]; then debug "ASIC config unchanged, current and destination SONiC version are the same" exit "${ASIC_CONFIG_UNCHANGED}" diff --git a/scripts/fast-reboot b/scripts/fast-reboot index 644955533b..85ee0aeba4 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -273,7 +273,7 @@ function teardown_control_plane_assistant() function setup_reboot_variables() { # Kernel and initrd image - NEXT_SONIC_IMAGE=$(sonic_installer list | grep "Next: " | cut -d ' ' -f 2) + NEXT_SONIC_IMAGE=$(sonic-installer list | grep "Next: " | cut -d ' ' -f 2) IMAGE_PATH="/host/image-${NEXT_SONIC_IMAGE#SONiC-OS-}" if grep -q aboot_platform= /host/machine.conf; then KERNEL_IMAGE="$(ls $IMAGE_PATH/boot/vmlinuz-*)" diff --git a/scripts/reboot b/scripts/reboot index 6a030c15c9..b2f2954e73 100755 --- a/scripts/reboot +++ b/scripts/reboot @@ -63,7 +63,7 @@ function show_help_and_exit() function setup_reboot_variables() { - NEXT_SONIC_IMAGE=$(sonic_installer list | grep "Next: " | cut -d ' ' -f 2) + NEXT_SONIC_IMAGE=$(sonic-installer list | grep "Next: " | cut -d ' ' -f 2) IMAGE_PATH="/host/image-${NEXT_SONIC_IMAGE#SONiC-OS-}" } diff --git a/scripts/sonic-kdump-config b/scripts/sonic-kdump-config index 5f91979834..4a0bba6273 100755 --- a/scripts/sonic-kdump-config +++ b/scripts/sonic-kdump-config @@ -77,7 +77,7 @@ def run_command(cmd, use_shell=False): ## Search which SONiC image is the Current image def get_current_image(): - (rc, img, err_str) = run_command("sonic_installer list | grep 'Current: ' | cut -d '-' -f 3-", use_shell=True); + (rc, img, err_str) = run_command("sonic-installer list | grep 'Current: ' | cut -d '-' -f 3-", use_shell=True); if type(img) == list and len(img) == 1: return img[0] print_err("Unable to locate current SONiC image") @@ -85,7 +85,7 @@ def get_current_image(): ## Search which SONiC image is the Next image def get_next_image(): - (rc, img, err_str) = run_command("sonic_installer list | grep 'Next: ' | cut -d '-' -f 3-", use_shell=True); + (rc, img, err_str) = run_command("sonic-installer list | grep 'Next: ' | cut -d '-' -f 3-", use_shell=True); if type(img) == list and len(img) == 1: return img[0] print_err("Unable to locate current SONiC image") diff --git a/show/main.py b/show/main.py index 95993aca8c..b670596466 100755 --- a/show/main.py +++ b/show/main.py @@ -2722,7 +2722,7 @@ def ecn(): @cli.command('boot') def boot(): """Show boot configuration""" - cmd = "sudo sonic_installer list" + cmd = "sudo sonic-installer list" proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) click.echo(proc.stdout.read()) diff --git a/sonic_installer/common.py b/sonic_installer/common.py index f12454042a..e9bfeac04f 100644 --- a/sonic_installer/common.py +++ b/sonic_installer/common.py @@ -1,5 +1,5 @@ """ -Module holding common functions and constants used by sonic_installer and its +Module holding common functions and constants used by sonic-installer and its subpackages. """ From 0d0c019191b7ca0fe83282480f880d64cf43dbf6 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Mon, 13 Jul 2020 20:26:23 +0000 Subject: [PATCH 14/14] Add print_deprecation_warning() function --- sonic_installer/main.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index b69c44b036..6f07f8045e 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -202,6 +202,13 @@ def hdel_warm_restart_table(db_name, table_name, warm_app_name, key): return client.hdel(_hash, key) +def print_deprecation_warning(deprecated_cmd_or_subcmd, new_cmd_or_subcmd): + click.secho("Warning: '{}' {}command is deprecated and will be removed in the future" + .format(deprecated_cmd_or_subcmd, "" if deprecated_cmd_or_subcmd == "sonic_installer" else "sub"), + fg="red", err=True) + click.secho("Please use '{}' instead".format(new_cmd_or_subcmd), fg="red", err=True) + + # Main entrypoint @click.group(cls=AliasedGroup) def sonic_installer(): @@ -211,8 +218,7 @@ def sonic_installer(): # Warn the user if they are calling the deprecated version of the command (with an underscore instead of a hyphen) if os.path.basename(sys.argv[0]) == "sonic_installer": - click.secho("Warning: 'sonic_installer' command is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'sonic-installer' instead", fg="red", err=True) + print_deprecation_warning("sonic_installer", "sonic-installer") # Install image @@ -296,8 +302,7 @@ def set_default(image): """ Choose image to boot from by default """ # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) if "set_default" in sys.argv: - click.secho("Warning: 'set_default' subcommand is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'set-default' instead", fg="red", err=True) + print_deprecation_warning("set_default", "set-default") bootloader = get_bootloader() if image not in bootloader.get_installed_images(): @@ -313,8 +318,7 @@ def set_next_boot(image): """ Choose image for next reboot (one time action) """ # Warn the user if they are calling the deprecated version of the subcommand (with underscores instead of hyphens) if "set_next_boot" in sys.argv: - click.secho("Warning: 'set_next_boot' subcommand is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'set-next-boot' instead", fg="red", err=True) + print_deprecation_warning("set_next_boot", "set-next-boot") bootloader = get_bootloader() if image not in bootloader.get_installed_images(): @@ -350,8 +354,7 @@ def binary_version(binary_image_path): """ Get version from local binary image file """ # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) if "binary_version" in sys.argv: - click.secho("Warning: 'binary_version' subcommand is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'binary-version' instead", fg="red", err=True) + print_deprecation_warning("binary_version", "binary-version") bootloader = get_bootloader() version = bootloader.get_binary_image_version(binary_image_path) @@ -414,8 +417,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): """ Upgrade docker image from local binary or URL""" # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) if "upgrade_docker" in sys.argv: - click.secho("Warning: 'upgrade_docker' subcommand is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'upgrade-docker' instead", fg="red", err=True) + print_deprecation_warning("upgrade_docker", "upgrade-docker") image_name = get_container_image_name(container_name) image_latest = image_name + ":latest" @@ -578,8 +580,7 @@ def rollback_docker(container_name): """ Rollback docker image to previous version""" # Warn the user if they are calling the deprecated version of the subcommand (with an underscore instead of a hyphen) if "rollback_docker" in sys.argv: - click.secho("Warning: 'rollback_docker' subcommand is deprecated and will be removed in the future", fg="red", err=True) - click.secho("Please use 'rollback-docker' instead", fg="red", err=True) + print_deprecation_warning("rollback_docker", "rollback-docker") image_name = get_container_image_name(container_name) # All images id under the image name