diff --git a/fwutil/lib.py b/fwutil/lib.py index 7163160b91..af9521c1b2 100755 --- a/fwutil/lib.py +++ b/fwutil/lib.py @@ -12,6 +12,7 @@ import shutil import socket import subprocess + import sys import time import tarfile import urllib @@ -34,9 +35,11 @@ NA = "N/A" NEWLINE = "\n" PLATFORM_COMPONENTS_FILE = "platform_components.json" -FWUPDATE_FWUPDATE_DIR = "/tmp/firmwareupdate/" -FWUPDATE_FWPACKAGE_DIR = os.path.join(FWUPDATE_FWUPDATE_DIR, "fwpackage/") -FWUPDATE_AU_TASK_FILE = "*_fw_auto_update" +FIRMWARE_UPDATE_DIR = "/tmp/firmwareupdate/" +FWUPDATE_FWPACKAGE_DIR = os.path.join(FIRMWARE_UPDATE_DIR, "fwpackage/") +FW_AU_TASK_FILE_REGEX = "*_fw_auto_update" +FW_AU_STATUS_FILE = "fw_auto_update_status" +FW_AU_STATUS_FILE_PATH = os.path.join(FIRMWARE_UPDATE_DIR, FW_AU_STATUS_FILE) # ========================= Variables ========================================== @@ -292,8 +295,8 @@ class FWPackage(object): def __init__(self, fwpackage): self.fwupdate_package_name = fwpackage - if not os.path.isdir(FWUPDATE_FWUPDATE_DIR): - os.mkdir(FWUPDATE_FWUPDATE_DIR) + if not os.path.isdir(FIRMWARE_UPDATE_DIR): + os.mkdir(FIRMWARE_UPDATE_DIR) if os.path.isdir(FWUPDATE_FWPACKAGE_DIR): shutil.rmtree(FWUPDATE_FWPACKAGE_DIR) os.mkdir(FWUPDATE_FWPACKAGE_DIR) @@ -311,7 +314,7 @@ def get_fw_package_path(self): for file in f: if PLATFORM_COMPONENTS_FILE in file: self.fwupdate_fwimage_dir = os.path.join(r, os.path.dirname(file)) - click.echo("fwupdate_fwimage_dir: {}".format(self.fwupdate_fwimage_dir)) + log_helper.print_warning("fwupdate_fwimage_dir: {}".format(self.fwupdate_fwimage_dir)) return self.fwupdate_fwimage_dir def cleanup_tmp_fwpackage(self): @@ -391,7 +394,7 @@ def __parse_component_section(self, section, component, is_module_component=Fals self.__module_component_map[section][key1] = OrderedDict() if value1: - if len(value1) != 1 and len(value1) != 2: + if len(value1) < 1 or len(value1) > 3: self.__parser_component_fail("unexpected number of records: key={}".format(key1)) if self.FIRMWARE_KEY not in value1: @@ -400,6 +403,9 @@ def __parse_component_section(self, section, component, is_module_component=Fals elif len(value1) == 2 and self.VERSION_KEY not in value1: missing_key = self.VERSION_KEY break + elif len(value1) == 3 and self.UTILITY_KEY not in value1: + missing_key = self.UTILITY_KEY + break for key2, value2 in value1.items(): if not self.__is_str(value2): @@ -410,7 +416,7 @@ def __parse_component_section(self, section, component, is_module_component=Fals else: self.__module_component_map[section][key1] = value1 - if missing_key is not None: + if missing_key is not None and missing_key is not self.UTILITY_KEY: self.__parser_component_fail("\"{}\" key hasn't been found".format(missing_key)) def __parse_chassis_section(self, chassis): @@ -530,6 +536,7 @@ class ComponentUpdateProvider(PlatformDataProvider): ComponentUpdateProvider """ STATUS_HEADER = [ "Chassis", "Module", "Component", "Firmware", "Version (Current/Available)", "Status" ] + AU_STATUS_HEADER = [ "Component", "Status", "Info", "Boot" ] FORMAT = "simple" FW_STATUS_UPDATE_REQUIRED = "update is required" @@ -792,14 +799,63 @@ def update_firmware(self, chassis_name, module_name, component_name): log_helper.log_fw_update_end(component_path, firmware_path, False, e) raise - def auto_update_firmware(self, component, boot): - is_chassis_component = component[0] - chassis_name = component[1] - module_name = component[2] - component_name = component[3] - utility = component[6] + def update_au_status_file(self, au_info_data, filename=FW_AU_STATUS_FILE_PATH): + with open(filename, 'w') as f: + json.dump(au_info_data, f) + + def read_au_status_file_if_exists(self, filename=FW_AU_STATUS_FILE_PATH): + data = None + if os.path.exists(filename): + with open(filename) as au_status_file: + data = json.load(au_status_file) + return data + + def set_firmware_auto_update_status(self, component_path, boot, status, info): + data = self.read_au_status_file_if_exists(FW_AU_STATUS_FILE_PATH) + if data is None: + data = {} + if boot not in data: + data[boot] = [] + + au_status = data[boot] + + comp_au_status = {} + comp_au_status['comp'] = component_path + comp_au_status['status'] = status + comp_au_status['info'] = info + + au_status.append(comp_au_status) + + self.update_au_status_file(data, FW_AU_STATUS_FILE_PATH) + + def get_au_status(self): + au_status = [] + auto_updated_status_table = [] + data = self.read_au_status_file_if_exists(FW_AU_STATUS_FILE_PATH) + + if data is None: + return None + + boot_type = list(data.keys())[0] + click.echo("Firmware auto-update performed for {} reboot".format(boot_type)) + + au_status = data[boot_type] + for comp_au_status in au_status: + r = [] + r.append(comp_au_status['comp'] if 'comp' in comp_au_status else "") + r.append(comp_au_status['status'] if 'status' in comp_au_status else "") + r.append(comp_au_status['info'] if 'info' in comp_au_status else "") + auto_updated_status_table.append(r) + + return tabulate(auto_updated_status_table, self.AU_STATUS_HEADER, tablefmt=self.FORMAT) + + def auto_update_firmware(self, component_au_info, boot): + is_chassis_component = component_au_info[0] + chassis_name = component_au_info[1] + module_name = component_au_info[2] + component_name = component_au_info[3] + utility = component_au_info[6] - click.echo("{} {} {} {}".format(is_chassis_component, chassis_name, module_name, boot)) if is_chassis_component: component = self.chassis_component_map[chassis_name][component_name] parser = self.__pcp.chassis_component_map[chassis_name][component_name] @@ -821,7 +877,7 @@ def auto_update_firmware(self, component, boot): utility = self.__root_path + utility try: - click.echo("{} firmware auto-update: {} with boot_type {}".format(component, firmware_path, boot)) + click.echo("{} firmware auto-update starting: {} with boot_type {}".format(component_path, firmware_path, boot)) log_helper.log_fw_auto_update_start(component_path, firmware_path, boot) if os.path.isfile(utility) and os.access(utility, os.X_OK): cmd = "{} -a {} {}".format( @@ -832,7 +888,8 @@ def auto_update_firmware(self, component, boot): status, info = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode(sys.stdout.encoding).strip().split(":") else: status, info = component.auto_update_firmware(firmware_path, boot).decode(sys.stdout.encoding).strip().split(":") - click.echo("{} firmware auto-update status : {} with {}".format(component_name, status, info)) + self.set_firmware_auto_update_status(component_path, boot, status, info) + click.echo("{} firmware auto-update status: {} with {}".format(component_path, status, info)) log_helper.log_fw_auto_update_end(component_path, firmware_path, boot, status, info) except KeyboardInterrupt: log_helper.log_fw_auto_update_end(component_path, firmware_path, boot, False, "Keyboard interrupt") @@ -841,13 +898,19 @@ def auto_update_firmware(self, component, boot): log_helper.log_fw_auto_update_end(component_path, firmware_path, boot, False, e) raise + def is_first_auto_update(self, boot): task_file = None - for task_file in glob.glob(os.path.join(FWUPDATE_FWUPDATE_DIR, FWUPDATE_AU_TASK_FILE)): + status_file = None + for task_file in glob.glob(os.path.join(FIRMWARE_UPDATE_DIR, FW_AU_TASK_FILE_REGEX)): if task_file is not None: click.echo("{} firmware auto-update is already performed, {} firmware auto update is not allowed any more".format(task_file, boot)) return False - click.echo("firmware auto-update for boot_type {} is allowed".format(boot)) + for status_file in glob.glob(os.path.join(FIRMWARE_UPDATE_DIR, FW_AU_STATUS_FILE)): + if status_file is not None: + click.echo("{} firmware auto-update is already performed, {} firmware auto update is not allowed any more".format(task_file, boot)) + return False + click.echo("Firmware auto-update for boot_type {} is allowed".format(boot)) return True def is_firmware_update_available(self, chassis_name, module_name, component_name): diff --git a/fwutil/main.py b/fwutil/main.py index 2cae6e10b9..a8c7f85bcc 100755 --- a/fwutil/main.py +++ b/fwutil/main.py @@ -379,13 +379,15 @@ def fw_auto_update(ctx, boot, image=None, fw_image=None): cup = ComponentUpdateProvider() if cup is not None: - if cup.is_first_auto_update(boot): - component_list = cup.get_update_available_components() - if component_list: - for component in component_list: - cup.auto_update_firmware(component, boot) - else: - log_helper.print_warning("All components: {}".format(cup.FW_STATUS_UP_TO_DATE)) + au_component_list = cup.get_update_available_components() + if au_component_list: + if cup.is_first_auto_update(boot): + for au_component in au_component_list: + cup.auto_update_firmware(au_component, boot) + log_helper.print_warning("All firmware auto-update has been performed") + click.echo("All firmware auto-update has been performed") + else: + log_helper.print_warning("All components: {}".format(cup.FW_STATUS_UP_TO_DATE)) else: log_helper.print_warning("compoenet update package is not available") finally: @@ -460,6 +462,18 @@ def status(ctx): cli_abort(ctx, str(e)) +# 'auto_update_status' subcommand +@show.command() +@click.pass_context +def auto_update_status(ctx): + """Show platform components auto firmware update status""" + try: + cup = ComponentUpdateProvider() + click.echo(cup.get_au_status()) + except Exception as e: + cli_abort(ctx, str(e)) + + # 'version' subcommand @show.command() def version():