diff --git a/data/etc/bash_completion.d/fwutil b/data/etc/bash_completion.d/fwutil index 60ec589a6a..7974889b49 100644 --- a/data/etc/bash_completion.d/fwutil +++ b/data/etc/bash_completion.d/fwutil @@ -1,7 +1,10 @@ +shopt -s extglob + _fwutil_completion() { COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \ COMP_CWORD=$COMP_CWORD \ _FWUTIL_COMPLETE=complete $1 ) ) + COMPREPLY=( ${COMPREPLY[*]//*(-install|-update)/} ) return 0 } diff --git a/fwutil/lib.py b/fwutil/lib.py index 897ead6cc8..9bfc229276 100755 --- a/fwutil/lib.py +++ b/fwutil/lib.py @@ -6,13 +6,16 @@ # try: - import click import os + import time import json import socket import urllib import subprocess + + import click import sonic_device_util + from collections import OrderedDict from urlparse import urlparse from tabulate import tabulate @@ -52,7 +55,8 @@ class URL(object): def __init__(self, url): self.__url = url self.__pb = None - self.__bytes_num = 0 + self.__pb_bytes_num = 0 + self.__pb_force_show = True def __str__(self): return self.__url @@ -68,15 +72,20 @@ def __reporthook(self, count, block_size, total_size): width=self.PB_FULL_TERMINAL_WIDTH ) - self.__pb.update(count * block_size - self.__bytes_num) - self.__bytes_num = count * block_size + self.__pb.update(count * block_size - self.__pb_bytes_num) + self.__pb_bytes_num = count * block_size + + if self.__pb_force_show: + time.sleep(1) + self.__pb_force_show = False def __pb_reset(self): if self.__pb: self.__pb.render_finish() self.__pb = None - self.__bytes_num = 0 + self.__pb_bytes_num = 0 + self.__pb_force_show = True def __validate(self): # Check basic URL syntax @@ -110,10 +119,6 @@ def retrieve(self): result = urlparse(self.__url) basename = os.path.basename(result.path) - name, extension = os.path.splitext(basename) - - if not extension: - raise RuntimeError("Filename is malformed: did not find an extension") default_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(self.DOWNLOAD_TIMEOUT) @@ -124,7 +129,7 @@ def retrieve(self): self.DOWNLOAD_PATH_TEMPLATE.format(basename), self.__reporthook ) - except Exception: + except: if os.path.exists(self.DOWNLOAD_PATH_TEMPLATE.format(basename)): os.remove(self.DOWNLOAD_PATH_TEMPLATE.format(basename)) raise @@ -283,7 +288,6 @@ class PlatformComponentsParser(object): COMPONENT_KEY = "component" FIRMWARE_KEY = "firmware" VERSION_KEY = "version" - INFO_KEY = "info" UTF8_ENCODING = "utf-8" @@ -344,18 +348,15 @@ def __parse_component_section(self, section, component, is_module_component=Fals self.__module_component_map[section][key1] = OrderedDict() if value1: - if len(value1) != 3: + if len(value1) != 1 and len(value1) != 2: self.__parser_component_fail("unexpected number of records: key={}".format(key1)) if self.FIRMWARE_KEY not in value1: missing_key = self.FIRMWARE_KEY break - elif self.VERSION_KEY not in value1: + elif len(value1) == 2 and self.VERSION_KEY not in value1: missing_key = self.VERSION_KEY break - elif self.INFO_KEY not in value1: - missing_key = self.INFO_KEY - break for key2, value2 in value1.items(): if not self.__is_str(value2): @@ -485,12 +486,9 @@ class ComponentUpdateProvider(PlatformDataProvider): """ ComponentUpdateProvider """ - STATUS_HEADER = [ "Chassis", "Module", "Component", "Firmware", "Version", "Status", "Info" ] - RESULT_HEADER = [ "Chassis", "Module", "Component", "Status" ] + STATUS_HEADER = [ "Chassis", "Module", "Component", "Firmware", "Version (Current/Available)", "Status" ] FORMAT = "simple" - FW_STATUS_UPDATE_SUCCESS = "success" - FW_STATUS_UPDATE_FAILURE = "failure" FW_STATUS_UPDATE_REQUIRED = "update is required" FW_STATUS_UP_TO_DATE = "up-to-date" @@ -545,7 +543,7 @@ def __validate_platform_schema(self, pcp): pcp.module_component_map ) - def get_status(self, force): + def get_status(self): status_table = [ ] append_chassis_name = self.is_chassis_has_components() @@ -556,42 +554,45 @@ def get_status(self, force): for chassis_component_name, chassis_component in chassis_component_map.items(): component = self.__pcp.chassis_component_map[chassis_name][chassis_component_name] - firmware_path = NA - firmware_version_current = chassis_component.get_firmware_version() - firmware_version = firmware_version_current - - status = self.FW_STATUS_UP_TO_DATE - info = NA - if component: firmware_path = component[self.__pcp.FIRMWARE_KEY] - firmware_version_available = component[self.__pcp.VERSION_KEY] - firmware_version = "{} / {}".format(firmware_version_current, firmware_version_available) - info = component[self.__pcp.INFO_KEY] if self.__root_path is not None: firmware_path = self.__root_path + firmware_path - if force or firmware_version_current != firmware_version_available: + firmware_version_current = chassis_component.get_firmware_version() + + if self.__pcp.VERSION_KEY in component: + firmware_version_available = component[self.__pcp.VERSION_KEY] + else: + firmware_version_available = chassis_component.get_available_firmware_version(firmware_path) + + if self.__root_path is not None: + firmware_path = component[self.__pcp.FIRMWARE_KEY] + + firmware_version = "{} / {}".format(firmware_version_current, firmware_version_available) + + if firmware_version_current != firmware_version_available: status = self.FW_STATUS_UPDATE_REQUIRED + else: + status = self.FW_STATUS_UP_TO_DATE - status_table.append( - [ - chassis_name if append_chassis_name else EMPTY, - module_name if append_module_na else EMPTY, - chassis_component_name, - firmware_path, - firmware_version, - status, - info - ] - ) + status_table.append( + [ + chassis_name if append_chassis_name else EMPTY, + module_name if append_module_na else EMPTY, + chassis_component_name, + firmware_path, + firmware_version, + status + ] + ) - if append_chassis_name: - append_chassis_name = False + if append_chassis_name: + append_chassis_name = False - if append_module_na: - append_module_na = False + if append_module_na: + append_module_na = False append_chassis_name = not self.is_chassis_has_components() chassis_name = self.chassis.get_name() @@ -603,168 +604,137 @@ def get_status(self, force): for module_component_name, module_component in module_component_map.items(): component = self.__pcp.module_component_map[module_name][module_component_name] - firmware_path = NA - firmware_version_current = module_component.get_firmware_version() - firmware_version = firmware_version_current - - status = self.FW_STATUS_UP_TO_DATE - info = NA - if component: firmware_path = component[self.__pcp.FIRMWARE_KEY] - firmware_version_available = component[self.__pcp.VERSION_KEY] - firmware_version = "{} / {}".format(firmware_version_current, firmware_version_available) - info = component[self.__pcp.INFO_KEY] if self.__root_path is not None: firmware_path = self.__root_path + firmware_path - if force or firmware_version_current != firmware_version_available: - status = self.FW_STATUS_UPDATE_REQUIRED + firmware_version_current = module_component.get_firmware_version() - status_table.append( - [ - chassis_name if append_chassis_name else EMPTY, - module_name if append_module_name else EMPTY, - module_component_name, - firmware_path, - firmware_version, - status, - info - ] - ) + if self.__pcp.VERSION_KEY in component: + firmware_version_available = component[self.__pcp.VERSION_KEY] + else: + firmware_version_available = module_component.get_available_firmware_version(firmware_path) - if append_chassis_name: - append_chassis_name = False + if self.__root_path is not None: + firmware_path = component[self.__pcp.FIRMWARE_KEY] - if append_module_name: - append_module_name = False + firmware_version = "{} / {}".format(firmware_version_current, firmware_version_available) - return tabulate(status_table, self.STATUS_HEADER, tablefmt=self.FORMAT) + if firmware_version_current != firmware_version_available: + status = self.FW_STATUS_UPDATE_REQUIRED + else: + status = self.FW_STATUS_UP_TO_DATE - def update_firmware(self, force): - status_table = [ ] + status_table.append( + [ + chassis_name if append_chassis_name else EMPTY, + module_name if append_module_name else EMPTY, + module_component_name, + firmware_path, + firmware_version, + status + ] + ) - append_chassis_name = self.is_chassis_has_components() - append_module_na = not self.is_modular_chassis() - module_name = NA + if append_chassis_name: + append_chassis_name = False - for chassis_name, chassis_component_map in self.chassis_component_map.items(): - for chassis_component_name, chassis_component in chassis_component_map.items(): - component = self.__pcp.chassis_component_map[chassis_name][chassis_component_name] - component_path = "{}/{}".format( - chassis_name, - chassis_component_name - ) + if append_module_name: + append_module_name = False - firmware_version_current = chassis_component.get_firmware_version() + if not status_table: + return None - status = self.FW_STATUS_UP_TO_DATE + return tabulate(status_table, self.STATUS_HEADER, tablefmt=self.FORMAT) - if component: - firmware_path = component[self.__pcp.FIRMWARE_KEY] - firmware_version_available = component[self.__pcp.VERSION_KEY] + def get_notification(self, chassis_name, module_name, component_name): + if self.is_modular_chassis(): + component = self.module_component_map[module_name][component_name] + parser = self.__pcp.module_component_map[module_name][component_name] + else: + component = self.chassis_component_map[chassis_name][component_name] + parser = self.__pcp.chassis_component_map[chassis_name][component_name] - if self.__root_path is not None: - firmware_path = self.__root_path + firmware_path + if not parser: + return None - if force or firmware_version_current != firmware_version_available: - result = False + firmware_path = parser[self.__pcp.FIRMWARE_KEY] - try: - click.echo("Installing firmware:") - click.echo(TAB + firmware_path) + if self.__root_path is not None: + firmware_path = self.__root_path + firmware_path - log_helper.log_fw_install_start(component_path, firmware_path) + return component.get_firmware_update_notification(firmware_path) - if not os.path.exists(firmware_path): - raise RuntimeError("Path \"{}\" does not exist".format(firmware_path)) + def update_firmware(self, chassis_name, module_name, component_name): + if self.is_modular_chassis(): + component = self.module_component_map[module_name][component_name] + parser = self.__pcp.module_component_map[module_name][component_name] - result = chassis_component.install_firmware(firmware_path) - log_helper.log_fw_install_end(component_path, firmware_path, result) - except Exception as e: - log_helper.log_fw_install_end(component_path, firmware_path, False, e) - log_helper.print_error(str(e)) + component_path = "{}/{}/{}".format(chassis_name, module_name, component_name) + else: + component = self.chassis_component_map[chassis_name][component_name] + parser = self.__pcp.chassis_component_map[chassis_name][component_name] - status = self.FW_STATUS_UPDATE_SUCCESS if result else self.FW_STATUS_UPDATE_FAILURE + component_path = "{}/{}".format(chassis_name, component_name) - status_table.append( - [ - chassis_name if append_chassis_name else EMPTY, - module_name if append_module_na else EMPTY, - chassis_component_name, - status, - ] - ) + if not parser: + return - if append_chassis_name: - append_chassis_name = False + firmware_path = parser[self.__pcp.FIRMWARE_KEY] - if append_module_na: - append_module_na = False + if self.__root_path is not None: + firmware_path = self.__root_path + firmware_path - append_chassis_name = not self.is_chassis_has_components() - chassis_name = self.chassis.get_name() + try: + click.echo("Updating firmware:") + click.echo(TAB + firmware_path) + log_helper.log_fw_update_start(component_path, firmware_path) + component.update_firmware(firmware_path) + log_helper.log_fw_update_end(component_path, firmware_path, True) + except KeyboardInterrupt: + log_helper.log_fw_update_end(component_path, firmware_path, False, "Keyboard interrupt") + raise + except Exception as e: + log_helper.log_fw_update_end(component_path, firmware_path, False, e) + raise + def is_firmware_update_available(self, chassis_name, module_name, component_name): if self.is_modular_chassis(): - for module_name, module_component_map in self.module_component_map.items(): - append_module_name = True - - for module_component_name, module_component in module_component_map.items(): - component = self.__pcp.module_component_map[module_name][module_component_name] - component_path = "{}/{}/{}".format( - self.chassis.get_name(), - module_name, - module_component_name - ) - - firmware_version_current = module_component.get_firmware_version() - - status = self.FW_STATUS_UP_TO_DATE - - if component: - firmware_path = component[self.__pcp.FIRMWARE_KEY] - firmware_version_available = component[self.__pcp.VERSION_KEY] - - if self.__root_path is not None: - firmware_path = self.__root_path + firmware_path - - if force or firmware_version_current != firmware_version_available: - result = False + component = self.__pcp.module_component_map[module_name][component_name] + else: + component = self.__pcp.chassis_component_map[chassis_name][component_name] - try: - click.echo("Installing firmware:") - click.echo(TAB + firmware_path) + if not component: + return False - log_helper.log_fw_install_start(component_path, firmware_path) + return True - if not os.path.exists(firmware_path): - raise RuntimeError("Path \"{}\" does not exist".format(firmware_path)) + def is_firmware_update_required(self, chassis_name, module_name, component_name): + if self.is_modular_chassis(): + component = self.module_component_map[module_name][component_name] + parser = self.__pcp.module_component_map[module_name][component_name] + else: + component = self.chassis_component_map[chassis_name][component_name] + parser = self.__pcp.chassis_component_map[chassis_name][component_name] - result = module_component.install_firmware(firmware_path) - log_helper.log_fw_install_end(component_path, firmware_path, result) - except Exception as e: - log_helper.log_fw_install_end(component_path, firmware_path, False, e) - log_helper.print_error(str(e)) + if not parser: + return False - status = self.FW_STATUS_UPDATE_SUCCESS if result else self.FW_STATUS_UPDATE_FAILURE + firmware_path = parser[self.__pcp.FIRMWARE_KEY] - status_table.append( - [ - chassis_name if append_chassis_name else EMPTY, - module_name if append_module_name else EMPTY, - module_component_name, - status, - ] - ) + if self.__root_path is not None: + firmware_path = self.__root_path + firmware_path - if append_chassis_name: - append_chassis_name = False + firmware_version_current = component.get_firmware_version() - if append_module_name: - append_module_name = False + if self.__pcp.VERSION_KEY in parser: + firmware_version_available = parser[self.__pcp.VERSION_KEY] + else: + firmware_version_available = component.get_available_firmware_version(firmware_path) - return tabulate(status_table, self.RESULT_HEADER, tablefmt=self.FORMAT) + return firmware_version_current != firmware_version_available class ComponentStatusProvider(PlatformDataProvider): diff --git a/fwutil/log.py b/fwutil/log.py index a686c437ef..69d60a28f5 100755 --- a/fwutil/log.py +++ b/fwutil/log.py @@ -6,8 +6,8 @@ # try: - import click import syslog + import click except ImportError as e: raise ImportError("Required module not found: {}".format(str(e))) @@ -58,6 +58,7 @@ class LogHelper(object): """ FW_ACTION_DOWNLOAD = "download" FW_ACTION_INSTALL = "install" + FW_ACTION_UPDATE = "update" STATUS_SUCCESS = "success" STATUS_FAILURE = "failure" @@ -122,8 +123,17 @@ def log_fw_install_start(self, component, firmware): def log_fw_install_end(self, component, firmware, status, exception=None): self.__log_fw_action_end(self.FW_ACTION_INSTALL, component, firmware, status, exception) + def log_fw_update_start(self, component, firmware): + self.__log_fw_action_start(self.FW_ACTION_UPDATE, component, firmware) + + def log_fw_update_end(self, component, firmware, status, exception=None): + self.__log_fw_action_end(self.FW_ACTION_UPDATE, component, firmware, status, exception) + def print_error(self, msg): click.echo("Error: {}.".format(msg)) def print_warning(self, msg): click.echo("Warning: {}.".format(msg)) + + def print_info(self, msg): + click.echo("Info: {}.".format(msg)) diff --git a/fwutil/main.py b/fwutil/main.py index c320929cc5..703a5fa9f3 100755 --- a/fwutil/main.py +++ b/fwutil/main.py @@ -6,8 +6,9 @@ # try: - import click import os + import click + from lib import PlatformDataProvider, ComponentStatusProvider, ComponentUpdateProvider from lib import URL, SquashFs from log import LogHelper @@ -16,7 +17,7 @@ # ========================= Constants ========================================== -VERSION = '1.0.0.0' +VERSION = '2.0.0.0' CHASSIS_NAME_CTX_KEY = "chassis_name" MODULE_NAME_CTX_KEY = "module_name" @@ -51,6 +52,11 @@ def cli_abort(ctx, msg): ctx.abort() +def cli_exit(ctx, msg): + log_helper.print_info(msg) + ctx.exit(EXIT_SUCCESS) + + def cli_init(ctx): if os.geteuid() != ROOT_UID: cli_abort(ctx, "Root privileges are required") @@ -64,7 +70,6 @@ def cli_init(ctx): @click.pass_context def cli(ctx): """fwutil - Command-line utility for interacting with platform components""" - cli_init(ctx) @@ -76,13 +81,39 @@ def install(ctx): ctx.obj[COMPONENT_PATH_CTX_KEY] = [ ] +# 'update' group +@cli.group() +@click.pass_context +def update(ctx): + """Update platform firmware""" + ctx.obj[COMPONENT_PATH_CTX_KEY] = [ ] + + +def chassis_handler(ctx): + ctx.obj[CHASSIS_NAME_CTX_KEY] = pdp.chassis.get_name() + ctx.obj[COMPONENT_PATH_CTX_KEY].append(pdp.chassis.get_name()) + + # 'chassis' subgroup @click.group() @click.pass_context -def chassis(ctx): +def chassis_install(ctx): """Install chassis firmware""" - ctx.obj[CHASSIS_NAME_CTX_KEY] = pdp.chassis.get_name() + chassis_handler(ctx) + + +# 'chassis' subgroup +@click.group() +@click.pass_context +def chassis_update(ctx): + """Update chassis firmware""" + chassis_handler(ctx) + + +def module_handler(ctx, module_name): + ctx.obj[MODULE_NAME_CTX_KEY] = module_name ctx.obj[COMPONENT_PATH_CTX_KEY].append(pdp.chassis.get_name()) + ctx.obj[COMPONENT_PATH_CTX_KEY].append(module_name) def validate_module(ctx, param, value): @@ -102,11 +133,22 @@ def validate_module(ctx, param, value): @click.group() @click.argument('module_name', metavar='', callback=validate_module) @click.pass_context -def module(ctx, module_name): +def module_install(ctx, module_name): """Install module firmware""" - ctx.obj[MODULE_NAME_CTX_KEY] = module_name - ctx.obj[COMPONENT_PATH_CTX_KEY].append(pdp.chassis.get_name()) - ctx.obj[COMPONENT_PATH_CTX_KEY].append(module_name) + module_handler(ctx, module_name) + + +# 'module' subgroup +@click.group() +@click.argument('module_name', metavar='', callback=validate_module) +@click.pass_context +def module_update(ctx, module_name): + """Update module firmware""" + module_handler(ctx, module_name) + + +def component_handler(ctx, component_name): + ctx.obj[COMPONENT_PATH_CTX_KEY].append(component_name) def validate_component(ctx, param, value): @@ -132,9 +174,18 @@ def validate_component(ctx, param, value): @click.group() @click.argument('component_name', metavar='', callback=validate_component) @click.pass_context -def component(ctx, component_name): +def component_install(ctx, component_name): """Install component firmware""" - ctx.obj[COMPONENT_PATH_CTX_KEY].append(component_name) + component_handler(ctx, component_name) + + +# 'component' subgroup +@click.group() +@click.argument('component_name', metavar='', callback=validate_component) +@click.pass_context +def component_update(ctx, component_name): + """Update component firmware""" + component_handler(ctx, component_name) def install_fw(ctx, fw_path): @@ -149,6 +200,9 @@ def install_fw(ctx, fw_path): log_helper.log_fw_install_start(component_path, fw_path) status = component.install_firmware(fw_path) log_helper.log_fw_install_end(component_path, fw_path, status) + except KeyboardInterrupt: + log_helper.log_fw_install_end(component_path, fw_path, False, "Keyboard interrupt") + raise except Exception as e: log_helper.log_fw_install_end(component_path, fw_path, False, e) cli_abort(ctx, str(e)) @@ -168,6 +222,9 @@ def download_fw(ctx, url): log_helper.log_fw_download_start(component_path, str(url)) filename, headers = url.retrieve() log_helper.log_fw_download_end(component_path, str(url), True) + except KeyboardInterrupt: + log_helper.log_fw_download_end(component_path, str(url), False, "Keyboard interrupt") + raise except Exception as e: log_helper.log_fw_download_end(component_path, str(url), False, e) cli_abort(ctx, str(e)) @@ -191,37 +248,50 @@ def validate_fw(ctx, param, value): # 'fw' subcommand -@component.command() +@component_install.command(name='fw') @click.option('-y', '--yes', 'yes', is_flag=True, show_default=True, help="Assume \"yes\" as answer to all prompts and run non-interactively") @click.argument('fw_path', metavar='', callback=validate_fw) @click.pass_context -def fw(ctx, yes, fw_path): - """Install firmware from local binary or URL""" - if not yes: - click.confirm("New firmware will be installed, continue?", abort=True) - +def fw_install(ctx, yes, fw_path): + """Install firmware from local path or URL""" url = None - if URL_CTX_KEY in ctx.obj: - url = ctx.obj[URL_CTX_KEY] - fw_path = download_fw(ctx, url) - try: + if URL_CTX_KEY in ctx.obj: + url = ctx.obj[URL_CTX_KEY] + fw_path = download_fw(ctx, url) + + component = ctx.obj[COMPONENT_CTX_KEY] + + notification = component.get_firmware_update_notification(fw_path) + if notification: + log_helper.print_warning(notification) + + if not yes: + click.confirm("New firmware will be installed, continue?", abort=True) + install_fw(ctx, fw_path) finally: if url is not None and os.path.exists(fw_path): os.remove(fw_path) -# 'update' subgroup -@cli.command() +# 'fw' subcommand +@component_update.command(name='fw') @click.option('-y', '--yes', 'yes', is_flag=True, show_default=True, help="Assume \"yes\" as answer to all prompts and run non-interactively") -@click.option('-f', '--force', 'force', is_flag=True, show_default=True, help="Install firmware regardless the current version") -@click.option('-i', '--image', 'image', type=click.Choice(["current", "next"]), default="current", show_default=True, help="Update firmware using current/next image") +@click.option('-f', '--force', 'force', is_flag=True, show_default=True, help="Update firmware regardless the current version") +@click.option('-i', '--image', 'image', type=click.Choice(["current", "next"]), default="current", show_default=True, help="Update firmware using current/next SONiC image") @click.pass_context -def update(ctx, yes, force, image): - """Update platform firmware""" - aborted = False +def fw_update(ctx, yes, force, image): + """Update firmware from SONiC image""" + if CHASSIS_NAME_CTX_KEY in ctx.obj: + chassis_name = ctx.obj[CHASSIS_NAME_CTX_KEY] + module_name = None + elif MODULE_NAME_CTX_KEY in ctx.obj: + chassis_name = pdp.chassis.get_name() + module_name = ctx.obj[MODULE_NAME_CTX_KEY] + + component_name = ctx.obj[COMPONENT_CTX_KEY].get_name() try: squashfs = None @@ -239,32 +309,30 @@ def update(ctx, yes, force, image): else: cup = ComponentUpdateProvider() - click.echo(cup.get_status(force)) + if not cup.is_firmware_update_available(chassis_name, module_name, component_name): + cli_exit(ctx, "Firmware update is not available") - if not yes: - click.confirm("New firmware will be installed, continue?", abort=True) - - result = cup.update_firmware(force) + if not (cup.is_firmware_update_required(chassis_name, module_name, component_name) or force): + cli_exit(ctx, "Firmware is up-to-date") - click.echo() - click.echo("Summary:") - click.echo() + notification = cup.get_notification(chassis_name, module_name, component_name) + if notification: + log_helper.print_warning(notification) - click.echo(result) - except click.Abort: - aborted = True - except Exception as e: - aborted = True - click.echo("Error: " + str(e) + ". Aborting...") + if not yes: + click.confirm("New firmware will be installed, continue?", abort=True) - if image == IMAGE_NEXT and squashfs is not None: - squashfs.umount_next_image_fs() + cup.update_firmware(chassis_name, module_name, component_name) + finally: + if squashfs is not None: + squashfs.umount_next_image_fs() + except click.exceptions.Abort: + ctx.abort() + except click.exceptions.Exit as e: + ctx.exit(e.exit_code) except Exception as e: cli_abort(ctx, str(e)) - if aborted: - ctx.abort() - # 'show' subgroup @cli.group() @@ -273,6 +341,40 @@ def show(): pass +# 'updates' subcommand +@show.command() +@click.option('-i', '--image', 'image', type=click.Choice(["current", "next"]), default="current", show_default=True, help="Show updates using current/next SONiC image") +@click.pass_context +def updates(ctx, image): + """Show available updates""" + try: + squashfs = None + + try: + if image == IMAGE_NEXT: + squashfs = SquashFs() + + if squashfs.is_next_boot_set(): + fs_path = squashfs.mount_next_image_fs() + cup = ComponentUpdateProvider(fs_path) + else: + log_helper.print_warning("Next boot is set to current: fallback to defaults") + cup = ComponentUpdateProvider() + else: + cup = ComponentUpdateProvider() + + status = cup.get_status() + if status is not None: + click.echo(status) + else: + log_helper.print_info("Firmware updates are not available") + finally: + if squashfs is not None: + squashfs.umount_next_image_fs() + except Exception as e: + cli_abort(ctx, str(e)) + + # 'status' subcommand @show.command() @click.pass_context @@ -291,11 +393,17 @@ def version(): """Show utility version""" click.echo("fwutil version {0}".format(VERSION)) -install.add_command(chassis) -install.add_command(module) +install.add_command(chassis_install, name='chassis') +install.add_command(module_install, name='module') + +update.add_command(chassis_update, name='chassis') +update.add_command(module_update, name='module') + +chassis_install.add_command(component_install, name='component') +module_install.add_command(component_install, name='component') -chassis.add_command(component) -module.add_command(component) +chassis_update.add_command(component_update, name='component') +module_update.add_command(component_update, name='component') # ========================= CLI entrypoint ===================================== diff --git a/show/main.py b/show/main.py index 981673910d..abae54490c 100755 --- a/show/main.py +++ b/show/main.py @@ -1738,13 +1738,6 @@ def pcieinfo(check, verbose): cmd = "pcieutil pcie_check" run_command(cmd, display_cmd=verbose) -# 'firmware' subcommand ("show platform firmware") -@platform.command() -def firmware(): - """Show firmware status information""" - cmd = "fwutil show status" - run_command(cmd) - # 'fan' subcommand ("show platform fan") @platform.command() def fan(): @@ -1758,7 +1751,25 @@ def temperature(): """Show device temperature information""" cmd = 'tempershow' run_command(cmd) - + +# 'firmware' subcommand ("show platform firmware") +@platform.command( + context_settings=dict( + ignore_unknown_options=True, + allow_extra_args=True + ), + add_help_option=False +) +@click.argument('args', nargs=-1, type=click.UNPROCESSED) +def firmware(args): + """Show firmware information""" + cmd = "fwutil show {}".format(" ".join(args)) + + try: + subprocess.check_call(cmd, shell=True) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) + # # 'logging' command ("show logging") #