diff --git a/data/etc/bash_completion.d/sonic-installer b/data/etc/bash_completion.d/sonic-installer new file mode 100644 index 000000000000..f1e9be7a0142 --- /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; diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 89714454184b..30df5c57bb11 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 ``` diff --git a/fwutil/lib.py b/fwutil/lib.py index 9bfc2292766d..eff432c6305a 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 b2b156b80fcc..06988da2d720 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 bfce5add2979..e18af200c3ed 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -274,7 +274,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 889634bbf40f..0ad25836da05 100755 --- a/scripts/reboot +++ b/scripts/reboot @@ -64,7 +64,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 5f91979834ef..4a0bba627326 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/setup.py b/setup.py index 38090071b073..b593eedd48c6 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', # Deprecated 'undebug = undebug.main:cli', 'watchdogutil = watchdogutil.main:watchdogutil', ] diff --git a/show/main.py b/show/main.py index 95993aca8cd5..b6705964663c 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/aliases.ini b/sonic_installer/aliases.ini new file mode 100644 index 000000000000..ebe917057b02 --- /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/common.py b/sonic_installer/common.py index f12454042a9c..e9bfeac04fdd 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. """ diff --git a/sonic_installer/main.py b/sonic_installer/main.py index ddd0bffdbaf7..8197e1206984 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -1,17 +1,85 @@ #! /usr/bin/python -u +try: + import ConfigParser as configparser +except ImportError: + 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 .common import run_command + +# 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. + """ + + 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 +105,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 +123,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 +144,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 +166,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 +175,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 +185,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 +193,42 @@ 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) + + +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() -def cli(): +@click.group(cls=AliasedGroup) +def sonic_installer(): """ SONiC image installation manager """ 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": + print_deprecation_warning("sonic_installer", "sonic-installer") + # 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 +276,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,32 +294,43 @@ 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 """ + # 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: + print_deprecation_warning("set_default", "set-default") + bootloader = get_bootloader() if image not in bootloader.get_installed_images(): click.echo('Error: Image does not exist') 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) """ + # 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: + print_deprecation_warning("set_next_boot", "set-next-boot") + bootloader = get_bootloader() if image not in bootloader.get_installed_images(): click.echo('Error: Image does not exist') 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,11 +346,16 @@ 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 """ + # 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: + print_deprecation_warning("binary_version", "binary-version") + bootloader = get_bootloader() version = bootloader.get_binary_image_version(binary_image_path) if not version: @@ -260,10 +364,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,19 +385,39 @@ def cleanup(): if image_removed == 0: 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 -@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(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""" + # 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: + print_deprecation_warning("upgrade_docker", "upgrade-docker") image_name = get_container_image_name(container_name) image_latest = image_name + ":latest" @@ -329,8 +454,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 +463,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 +478,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 +538,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 +550,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,14 +569,19 @@ 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(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) + if "rollback_docker" in sys.argv: + print_deprecation_warning("rollback_docker", "rollback-docker") + 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) @@ -487,4 +617,4 @@ def verify_next_image(): click.echo('Image successfully verified') if __name__ == '__main__': - cli() + sonic_installer()