From 2c8e646c215a26d045f3a819cd646756f6adde23 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 21 Nov 2019 09:53:16 +0200 Subject: [PATCH 1/9] [sonic_platform]support install firmware --- .../build_templates/sonic_debian_extension.j2 | 1 + .../sonic_platform/component.py | 149 ++++++++++++++++- platform/mellanox/one-image.mk | 2 +- platform/mellanox/onie-fw-update | 150 ++++++++++++++++++ platform/mellanox/onie-fw-update.mk | 7 + platform/mellanox/rules.mk | 1 + 6 files changed, 304 insertions(+), 6 deletions(-) create mode 100755 platform/mellanox/onie-fw-update create mode 100644 platform/mellanox/onie-fw-update.mk diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index d08aa7b6f1cc..feb4cc7416d1 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -381,6 +381,7 @@ sudo cp $files_path/$MLNX_SPC_FW_FILE $FILESYSTEM_ROOT/etc/mlnx/fw-SPC.mfa sudo cp $files_path/$MLNX_SPC2_FW_FILE $FILESYSTEM_ROOT/etc/mlnx/fw-SPC2.mfa sudo cp $files_path/$ISSU_VERSION_FILE $FILESYSTEM_ROOT/etc/mlnx/issu-version sudo cp $files_path/$MLNX_FFB_SCRIPT $FILESYSTEM_ROOT/usr/bin/mlnx-ffb.sh +sudo cp $files_path/$ONIE_FW_UPDATE $FILESYSTEM_ROOT/usr/bin/onie-fw-update.sh j2 platform/mellanox/mlnx-fw-upgrade.j2 | sudo tee $FILESYSTEM_ROOT/usr/bin/mlnx-fw-upgrade.sh sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/mlnx-fw-upgrade.sh diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index fd593f7bbe45..4bc12d183770 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -5,13 +5,14 @@ # # implementation of new platform api ############################################################################# - +from __future__ import print_function try: from sonic_platform_base.component_base import ComponentBase from glob import glob import subprocess import io import re + import sys except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -23,6 +24,26 @@ CPLD_VERSION_FILE_PATTERN = '/var/run/hw-management/system/cpld[0-9]_version' CPLD_VERSION_MAX_LENGTH = 4 +CPLD_UPDATE_COMMAND = "cpldupdate --dev {} {}" +CPLD_INSTALL_SUCCESS_FLAG = "PASS!" + +MST_DEVICE_PATTERN = "/dev/mst/mt[0-9]*_pciconf0" +MACHINE_CONF_FILE = "/host/machine.conf" +MACHINE_CONF_MAX_SIZE = 2048 + +BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' +ONIE_VERSION_PARSE_PATTERN = 'onie_version=[0-9]{4}\.[0-9]{2}-([0-9]+)\.([0-9]+)\.([0-9]+)' +ONIE_VERSION_MAJOR_OFFSET = 1 +ONIE_VERSION_MINOR_OFFSET = 2 +ONIE_VERSION_RELEASE_OFFSET = 3 +# To update BIOS requires the ONIE with version 5.2.0016 or upper +ONIE_REQUIRED_MAJOR = "5" +ONIE_REQUIRED_MINOR = "2" +ONIE_REQUIRED_RELEASE = "0016" + +ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}" +ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update" + class Component(ComponentBase): def get_name(self): """ @@ -60,10 +81,16 @@ def _get_command_result(self, cmdline): return result -class ComponentBIOS(Component): - BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' + def _does_file_exist(self, image_path): + image = glob(image_path) + if not image: + return False, "Failed to get the image file via {} or multiple files matched".format(image_path) + if len(image) != 1: + return False, "Multiple files {} matched {}".format(image, image_path) + return True, "" +class ComponentBIOS(Component): def __init__(self): self.name = COMPONENT_BIOS @@ -101,15 +128,63 @@ def get_firmware_version(self): """ bios_ver_str = self._get_command_result(BIOS_QUERY_VERSION_COMMAND) try: - m = re.search(self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str) + m = re.search(BIOS_VERSION_PARSE_PATTERN, bios_ver_str) result = m.group(1) except AttributeError as e: raise RuntimeError("Failed to parse BIOS version by {} from {} due to {}".format( - self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e))) + BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e))) return result + def install_firmware(self, image_path): + """ + Installs firmware to the component + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + """ + # check ONIE version. To update ONIE requires version 5.2.0016 or later. + machine_conf = self._read_generic_file(MACHINE_CONF_FILE, MACHINE_CONF_MAX_SIZE) + try: + m = re.search(ONIE_VERSION_PARSE_PATTERN, machine_conf) + onie_major = m.group(ONIE_VERSION_MAJOR_OFFSET) + onie_minor = m.group(ONIE_VERSION_MINOR_OFFSET) + onie_release = m.group(ONIE_VERSION_RELEASE_OFFSET) + except AttributeError as e: + print("Failed to parse ONIE version by {} from {} due to {}".format( + BIOS_VERSION_PARSE_PATTERN, machine_conf, repr(e))) + return False + if onie_major < ONIE_REQUIRED_MAJOR or onie_minor < ONIE_REQUIRED_MINOR or onie_release < ONIE_REQUIRED_RELEASE: + print("ONIE {}.{}.{} or later is required".format(ONIE_REQUIRED_MAJOR, ONIE_REQUIRED_MINOR, ONIE_REQUIRED_RELEASE)) + return False + + # check whether the file exists + image_exists, message = self._does_file_exist(image_path) + if not image_exists: + print(message) + return False + + # do the real work. + try: + result = subprocess.call(ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) + if result: + return False + result = subprocess.call(ONIE_FW_UPDATE_CMD_UPDATE.split()) + if result: + return False + except Exception as e: + print("Installing BIOS failed due to {}".format(repr(e))) + return False + + print("Reboot via \"/sbin/reboot\" is required to finish BIOS installation.") + print("Please don't try installing a new sonic image before BIOS installation finishing") + return True + + class ComponentCPLD(Component): def __init__(self): self.name = COMPONENT_CPLD @@ -146,3 +221,67 @@ def get_firmware_version(self): return cpld_version + + def install_firmware(self, image_path): + """ + Installs firmware to the component + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + + Details: + The command "cpldupdate" is provided to install CPLD. There are two ways to do it: + 1. To burn CPLD via gpio, which is faster but only supported on new systems, like Anaconda, ... + 2. To install CPLD via firmware, which is slower but supported on older systems. + This also requires the mst device designated. + "cpldupdate --dev " has the logic of testing whether to update via gpio is supported, + and if so then go this way, otherwise tries updating software via fw. So we take advantage of it to update the CPLD. + By doing so we don't have to mind whether to update via gpio supported, which belongs to hardware details. + + So the procedure should be: + 1. Test whether the file exists + 2. Fetch the mst device name + 3. Update CPLD via executing "cpldupdate --dev " + 4. Check the result + """ + # check whether the file exists + image_exists, message = self._does_file_exist(image_path) + if not image_exists: + print(message) + return False + + mst_dev_list = glob(MST_DEVICE_PATTERN) + if not mst_dev_list or not len(mst_dev_list) == 1: + print("Failed to get mst device which is required for CPLD updating or multiple device files matched") + return False + + cmdline = CPLD_UPDATE_COMMAND.format(mst_dev_list[0], image_path) + outputline = "" + success_flag = False + try: + proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT) + while True: + out = proc.stdout.read(1) + if out == '' and proc.poll() != None: + break + if out != '': + sys.stdout.write(out) + sys.stdout.flush() + outputline += out + if (out == '\n' or out == '\r') and len(outputline): + m = re.search(CPLD_INSTALL_SUCCESS_FLAG, outputline) + if m and m.group(0) == CPLD_INSTALL_SUCCESS_FLAG: + success_flag = True + + except OSError as e: + raise RuntimeError("Failed to execute command {} due to {}".format(cmdline, repr(e))) + + if success_flag: + print("Success. Refresh or power cycle is required to finish CPLD installation.") + else: + print("Failed to install CPLD") + + return success_flag diff --git a/platform/mellanox/one-image.mk b/platform/mellanox/one-image.mk index 2946ae53f47b..d30e44690991 100644 --- a/platform/mellanox/one-image.mk +++ b/platform/mellanox/one-image.mk @@ -11,5 +11,5 @@ $(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.g else $(SONIC_ONE_IMAGE)_DOCKERS = $(SONIC_INSTALL_DOCKER_IMAGES) endif -$(SONIC_ONE_IMAGE)_FILES += $(MLNX_SPC_FW_FILE) $(MLNX_SPC2_FW_FILE) $(MLNX_FFB_SCRIPT) $(ISSU_VERSION_FILE) +$(SONIC_ONE_IMAGE)_FILES += $(MLNX_SPC_FW_FILE) $(MLNX_SPC2_FW_FILE) $(MLNX_FFB_SCRIPT) $(ISSU_VERSION_FILE) $(ONIE_FW_UPDATE) SONIC_INSTALLERS += $(SONIC_ONE_IMAGE) diff --git a/platform/mellanox/onie-fw-update b/platform/mellanox/onie-fw-update new file mode 100755 index 000000000000..d7bfbdb1eced --- /dev/null +++ b/platform/mellanox/onie-fw-update @@ -0,0 +1,150 @@ +#!/bin/sh + +# Copyright (C) 2019 Mellanox Technologies Ltd. +# Copyright (C) 2019 Michael Shych +# +# SPDX-License-Identifier: GPL-2.0 + +this_script=${ONIE_FWPKG_PROGRAM_NAME:-$(basename $(realpath $0))} + +onie_mount=/mnt/onie-boot +os_boot=/host +onie_partition= +onie_entry=0 + +export ONIE_FWPKG_PROGRAM_NAME=$(basename $(realpath $0)) + +usage() +{ +cat < before update." + clean_onie_access + exit 1 + fi + ;; + purge | show | show-results | show-log | show-pending | help) + ;; + *) + echo "Unknown command: $cmd" + exit 1 + ;; +esac + +enable_onie_access +$onie_mount/onie/tools/bin/onie-fwpkg "$@" +rc=$? +if [ $cmd = "help" ]; then + usage +fi +clean_onie_access + +exit $rc diff --git a/platform/mellanox/onie-fw-update.mk b/platform/mellanox/onie-fw-update.mk new file mode 100644 index 000000000000..160f1c98f7b1 --- /dev/null +++ b/platform/mellanox/onie-fw-update.mk @@ -0,0 +1,7 @@ +# bios update tool + +ONIE_FW_UPDATE= onie-fw-update +$(ONIE_FW_UPDATE)_PATH = platform/mellanox/ +SONIC_COPY_FILES += $(ONIE_FW_UPDATE) + +export ONIE_FW_UPDATE diff --git a/platform/mellanox/rules.mk b/platform/mellanox/rules.mk index dd10cefda571..efd0af2c8f4a 100644 --- a/platform/mellanox/rules.mk +++ b/platform/mellanox/rules.mk @@ -12,6 +12,7 @@ include $(PLATFORM_PATH)/libsaithrift-dev.mk include $(PLATFORM_PATH)/docker-ptf-mlnx.mk include $(PLATFORM_PATH)/mlnx-ffb.mk include $(PLATFORM_PATH)/issu-version.mk +include $(PLATFORM_PATH)/onie-fw-update.mk SONIC_ALL += $(SONIC_ONE_IMAGE) \ $(DOCKER_FPM) From 80ecdaa1af2eb8537cb7127c402e5dadcfe353ba Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 12 Dec 2019 19:11:06 +0800 Subject: [PATCH 2/9] [component.py]Address comments --- .../sonic_platform/component.py | 150 ++++++++++++------ platform/mellanox/onie-fw-update | 10 -- 2 files changed, 98 insertions(+), 62 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index 4bc12d183770..a8b34eae60ab 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -8,9 +8,11 @@ from __future__ import print_function try: from sonic_platform_base.component_base import ComponentBase + from sonic_device_util import get_machine_info from glob import glob import subprocess import io + import os import re import sys except ImportError as e: @@ -21,30 +23,21 @@ COMPONENT_CPLD = "CPLD" BIOS_QUERY_VERSION_COMMAND = 'dmidecode -t 11' -CPLD_VERSION_FILE_PATTERN = '/var/run/hw-management/system/cpld[0-9]_version' -CPLD_VERSION_MAX_LENGTH = 4 - -CPLD_UPDATE_COMMAND = "cpldupdate --dev {} {}" -CPLD_INSTALL_SUCCESS_FLAG = "PASS!" MST_DEVICE_PATTERN = "/dev/mst/mt[0-9]*_pciconf0" MACHINE_CONF_FILE = "/host/machine.conf" MACHINE_CONF_MAX_SIZE = 2048 -BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' -ONIE_VERSION_PARSE_PATTERN = 'onie_version=[0-9]{4}\.[0-9]{2}-([0-9]+)\.([0-9]+)\.([0-9]+)' -ONIE_VERSION_MAJOR_OFFSET = 1 -ONIE_VERSION_MINOR_OFFSET = 2 -ONIE_VERSION_RELEASE_OFFSET = 3 -# To update BIOS requires the ONIE with version 5.2.0016 or upper -ONIE_REQUIRED_MAJOR = "5" -ONIE_REQUIRED_MINOR = "2" -ONIE_REQUIRED_RELEASE = "0016" - ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}" +ONIE_FW_UPDATE_CMD_REMOVE = "/usr/bin/onie-fw-update remove {}" ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update" +ONIE_FW_UPDATE_CMD_SHOW = "/usr/bin/onie-fw-update show-pending" class Component(ComponentBase): + def __init__(self): + image_ext_name = None + + def get_name(self): """ Retrieves the name of the component @@ -81,18 +74,39 @@ def _get_command_result(self, cmdline): return result - def _does_file_exist(self, image_path): - image = glob(image_path) - if not image: - return False, "Failed to get the image file via {} or multiple files matched".format(image_path) - if len(image) != 1: - return False, "Multiple files {} matched {}".format(image, image_path) - return True, "" + def _check_file_validity(self, image_path): + # check whether the image file exists + if not os.path.isfile(image_path): + print("File {} doesn't exist or is not a file".format(image_path)) + return False + + if self.image_ext_name is not None: + name_list = os.path.splitext(image_path) + if name_list[1] != self.image_ext_name: + print("Extend name of file {} is wrong. Image for {} should have extend name {}".format(image_path, self.name, self.image_ext_name)) + return False + + return True + class ComponentBIOS(Component): + # To update BIOS requires the ONIE with version 5.2.0016 or upper + ONIE_VERSION_PARSE_PATTERN = '[0-9]{4}\.[0-9]{2}-([0-9]+)\.([0-9]+)\.([0-9]+)' + ONIE_VERSION_MAJOR_OFFSET = 1 + ONIE_VERSION_MINOR_OFFSET = 2 + ONIE_VERSION_RELEASE_OFFSET = 3 + ONIE_REQUIRED_MAJOR = "5" + ONIE_REQUIRED_MINOR = "2" + ONIE_REQUIRED_RELEASE = "0016" + + BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' + + BIOS_PENDING_UPDATE_PATTERN = '([0-9A-Za-z_]*.rom)[\s]*\|[\s]*bios_update' + def __init__(self): self.name = COMPONENT_BIOS + self.image_ext_name = ".rom" def get_description(self): @@ -128,15 +142,37 @@ def get_firmware_version(self): """ bios_ver_str = self._get_command_result(BIOS_QUERY_VERSION_COMMAND) try: - m = re.search(BIOS_VERSION_PARSE_PATTERN, bios_ver_str) + m = re.search(self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str) result = m.group(1) except AttributeError as e: raise RuntimeError("Failed to parse BIOS version by {} from {} due to {}".format( - BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e))) + self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e))) return result + def _check_onie_version(self): + # check ONIE version. To update ONIE requires version 5.2.0016 or later. + machine_conf = self._read_generic_file(MACHINE_CONF_FILE, MACHINE_CONF_MAX_SIZE) + + try: + machine_info = get_machine_info() + onie_version_string = machine_info['onie_version'] + m = re.search(self.ONIE_VERSION_PARSE_PATTERN, onie_version_string) + onie_major = m.group(self.ONIE_VERSION_MAJOR_OFFSET) + onie_minor = m.group(self.ONIE_VERSION_MINOR_OFFSET) + onie_release = m.group(self.ONIE_VERSION_RELEASE_OFFSET) + except AttributeError as e: + print("Failed to parse ONIE version by {} from {} due to {}".format( + self.ONIE_VERSION_PARSE_PATTERN, machine_conf, repr(e))) + return False + if onie_major < self.ONIE_REQUIRED_MAJOR or onie_minor < self.ONIE_REQUIRED_MINOR or onie_release < self.ONIE_REQUIRED_RELEASE: + print("ONIE {}.{}.{} or later is required".format(self.ONIE_REQUIRED_MAJOR, self.ONIE_REQUIRED_MINOR, self.ONIE_REQUIRED_RELEASE)) + return False + + return True + + def install_firmware(self, image_path): """ Installs firmware to the component @@ -147,29 +183,27 @@ def install_firmware(self, image_path): Returns: A boolean, True if install was successful, False if not """ - # check ONIE version. To update ONIE requires version 5.2.0016 or later. - machine_conf = self._read_generic_file(MACHINE_CONF_FILE, MACHINE_CONF_MAX_SIZE) - try: - m = re.search(ONIE_VERSION_PARSE_PATTERN, machine_conf) - onie_major = m.group(ONIE_VERSION_MAJOR_OFFSET) - onie_minor = m.group(ONIE_VERSION_MINOR_OFFSET) - onie_release = m.group(ONIE_VERSION_RELEASE_OFFSET) - except AttributeError as e: - print("Failed to parse ONIE version by {} from {} due to {}".format( - BIOS_VERSION_PARSE_PATTERN, machine_conf, repr(e))) - return False - if onie_major < ONIE_REQUIRED_MAJOR or onie_minor < ONIE_REQUIRED_MINOR or onie_release < ONIE_REQUIRED_RELEASE: - print("ONIE {}.{}.{} or later is required".format(ONIE_REQUIRED_MAJOR, ONIE_REQUIRED_MINOR, ONIE_REQUIRED_RELEASE)) + # check ONIE version requirement + if not self._check_onie_version(): return False # check whether the file exists - image_exists, message = self._does_file_exist(image_path) - if not image_exists: - print(message) + if not self._check_file_validity(image_path): return False # do the real work. try: + # check whether there has already been some images pending + # if yes, remove them + result = self._get_command_result(ONIE_FW_UPDATE_CMD_SHOW) + pending_list = result.split("\n") + for pending in pending_list: + m = re.match(self.BIOS_PENDING_UPDATE_PATTERN, pending) + if m is not None: + pending_image = m.group(1) + self._get_command_result(ONIE_FW_UPDATE_CMD_REMOVE.format(pending_image)) + print("Image {} which is already pending to upgrade has been removed".format(pending_image)) + result = subprocess.call(ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) if result: return False @@ -186,8 +220,15 @@ def install_firmware(self, image_path): class ComponentCPLD(Component): + CPLD_VERSION_FILE_PATTERN = '/var/run/hw-management/system/cpld[0-9]_version' + CPLD_VERSION_MAX_LENGTH = 4 + + CPLD_UPDATE_COMMAND = "cpldupdate --dev {} {}" + CPLD_INSTALL_SUCCESS_FLAG = "PASS!" + def __init__(self): self.name = COMPONENT_CPLD + self.image_ext_name = ".vme" def get_description(self): @@ -207,21 +248,28 @@ def get_firmware_version(self): Returns: A string containing the firmware version of the component """ - cpld_version_file_list = glob(CPLD_VERSION_FILE_PATTERN) + cpld_version_file_list = glob(self.CPLD_VERSION_FILE_PATTERN) cpld_version = '' if cpld_version_file_list is not None and cpld_version_file_list: cpld_version_file_list.sort() for version_file in cpld_version_file_list: - version = self._read_generic_file(version_file, CPLD_VERSION_MAX_LENGTH) + version = self._read_generic_file(version_file, self.CPLD_VERSION_MAX_LENGTH) if not cpld_version == '': cpld_version += '.' cpld_version += version.rstrip('\n') else: - raise RuntimeError("Failed to get CPLD version files by matching {}".format(CPLD_VERSION_FILE_PATTERN)) + raise RuntimeError("Failed to get CPLD version files by matching {}".format(self.CPLD_VERSION_FILE_PATTERN)) return cpld_version + def _get_mst_device(self): + mst_dev_list = glob(MST_DEVICE_PATTERN) + if mst_dev_list is None or len(mst_dev_list) != 1: + return None + return mst_dev_list + + def install_firmware(self, image_path): """ Installs firmware to the component @@ -247,18 +295,16 @@ def install_firmware(self, image_path): 3. Update CPLD via executing "cpldupdate --dev " 4. Check the result """ - # check whether the file exists - image_exists, message = self._does_file_exist(image_path) - if not image_exists: - print(message) + # check whether the image file exists + if not self._check_file_validity(image_path): return False - mst_dev_list = glob(MST_DEVICE_PATTERN) - if not mst_dev_list or not len(mst_dev_list) == 1: + mst_dev_list = self._get_mst_device() + if mst_dev_list is None: print("Failed to get mst device which is required for CPLD updating or multiple device files matched") return False - cmdline = CPLD_UPDATE_COMMAND.format(mst_dev_list[0], image_path) + cmdline = self.CPLD_UPDATE_COMMAND.format(mst_dev_list[0], image_path) outputline = "" success_flag = False try: @@ -272,8 +318,8 @@ def install_firmware(self, image_path): sys.stdout.flush() outputline += out if (out == '\n' or out == '\r') and len(outputline): - m = re.search(CPLD_INSTALL_SUCCESS_FLAG, outputline) - if m and m.group(0) == CPLD_INSTALL_SUCCESS_FLAG: + m = re.search(self.CPLD_INSTALL_SUCCESS_FLAG, outputline) + if m and m.group(0) == self.CPLD_INSTALL_SUCCESS_FLAG: success_flag = True except OSError as e: diff --git a/platform/mellanox/onie-fw-update b/platform/mellanox/onie-fw-update index d7bfbdb1eced..220743694b67 100755 --- a/platform/mellanox/onie-fw-update +++ b/platform/mellanox/onie-fw-update @@ -87,13 +87,6 @@ show_pending() return $num } -system_reboot() -{ - echo "Reboot will be done after 5 sec." - sleep 5 - /sbin/reboot -} - # Process command arguments cmd=$1 # optional argument @@ -120,9 +113,6 @@ case "$cmd" in change_grub_boot_order rc=$? clean_onie_access - if [ $rc -eq 0 ]; then - system_reboot - fi exit $rc else echo "ERROR: NO FW images for update." From f73b20b25ad1c29cba1e763d2c95430080dec889 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 12 Dec 2019 22:16:42 +0800 Subject: [PATCH 3/9] [component.py]address comments --- .../sonic_platform/component.py | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index a8b34eae60ab..b8de2ea5b416 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -22,20 +22,10 @@ COMPONENT_BIOS = "BIOS" COMPONENT_CPLD = "CPLD" -BIOS_QUERY_VERSION_COMMAND = 'dmidecode -t 11' - -MST_DEVICE_PATTERN = "/dev/mst/mt[0-9]*_pciconf0" -MACHINE_CONF_FILE = "/host/machine.conf" -MACHINE_CONF_MAX_SIZE = 2048 - -ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}" -ONIE_FW_UPDATE_CMD_REMOVE = "/usr/bin/onie-fw-update remove {}" -ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update" -ONIE_FW_UPDATE_CMD_SHOW = "/usr/bin/onie-fw-update show-pending" - class Component(ComponentBase): def __init__(self): - image_ext_name = None + self.name = None + self.image_ext_name = None def get_name(self): @@ -77,13 +67,13 @@ def _get_command_result(self, cmdline): def _check_file_validity(self, image_path): # check whether the image file exists if not os.path.isfile(image_path): - print("File {} doesn't exist or is not a file".format(image_path)) + print("ERROR: File {} doesn't exist or is not a file".format(image_path)) return False if self.image_ext_name is not None: name_list = os.path.splitext(image_path) if name_list[1] != self.image_ext_name: - print("Extend name of file {} is wrong. Image for {} should have extend name {}".format(image_path, self.name, self.image_ext_name)) + print("ERROR: Extend name of file {} is wrong. Image for {} should have extend name {}".format(image_path, self.name, self.image_ext_name)) return False return True @@ -101,9 +91,15 @@ class ComponentBIOS(Component): ONIE_REQUIRED_RELEASE = "0016" BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' - BIOS_PENDING_UPDATE_PATTERN = '([0-9A-Za-z_]*.rom)[\s]*\|[\s]*bios_update' + ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}" + ONIE_FW_UPDATE_CMD_REMOVE = "/usr/bin/onie-fw-update remove {}" + ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update" + ONIE_FW_UPDATE_CMD_SHOW = "/usr/bin/onie-fw-update show-pending" + + BIOS_QUERY_VERSION_COMMAND = 'dmidecode -t 11' + def __init__(self): self.name = COMPONENT_BIOS self.image_ext_name = ".rom" @@ -140,7 +136,7 @@ def get_firmware_version(self): By using regular expression 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' we can extrace the version string which is marked with * in the above context """ - bios_ver_str = self._get_command_result(BIOS_QUERY_VERSION_COMMAND) + bios_ver_str = self._get_command_result(self.BIOS_QUERY_VERSION_COMMAND) try: m = re.search(self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str) result = m.group(1) @@ -153,8 +149,6 @@ def get_firmware_version(self): def _check_onie_version(self): # check ONIE version. To update ONIE requires version 5.2.0016 or later. - machine_conf = self._read_generic_file(MACHINE_CONF_FILE, MACHINE_CONF_MAX_SIZE) - try: machine_info = get_machine_info() onie_version_string = machine_info['onie_version'] @@ -163,11 +157,12 @@ def _check_onie_version(self): onie_minor = m.group(self.ONIE_VERSION_MINOR_OFFSET) onie_release = m.group(self.ONIE_VERSION_RELEASE_OFFSET) except AttributeError as e: - print("Failed to parse ONIE version by {} from {} due to {}".format( + print("ERROR: Failed to parse ONIE version by {} from {} due to {}".format( self.ONIE_VERSION_PARSE_PATTERN, machine_conf, repr(e))) return False + if onie_major < self.ONIE_REQUIRED_MAJOR or onie_minor < self.ONIE_REQUIRED_MINOR or onie_release < self.ONIE_REQUIRED_RELEASE: - print("ONIE {}.{}.{} or later is required".format(self.ONIE_REQUIRED_MAJOR, self.ONIE_REQUIRED_MINOR, self.ONIE_REQUIRED_RELEASE)) + print("ERROR: ONIE {}.{}.{} or later is required".format(self.ONIE_REQUIRED_MAJOR, self.ONIE_REQUIRED_MINOR, self.ONIE_REQUIRED_RELEASE)) return False return True @@ -191,31 +186,31 @@ def install_firmware(self, image_path): if not self._check_file_validity(image_path): return False - # do the real work. + # do the real work try: # check whether there has already been some images pending # if yes, remove them - result = self._get_command_result(ONIE_FW_UPDATE_CMD_SHOW) + result = self._get_command_result(self.ONIE_FW_UPDATE_CMD_SHOW) pending_list = result.split("\n") for pending in pending_list: m = re.match(self.BIOS_PENDING_UPDATE_PATTERN, pending) if m is not None: pending_image = m.group(1) - self._get_command_result(ONIE_FW_UPDATE_CMD_REMOVE.format(pending_image)) - print("Image {} which is already pending to upgrade has been removed".format(pending_image)) + self._get_command_result(self.ONIE_FW_UPDATE_CMD_REMOVE.format(pending_image)) + print("WARNING: Image {} which is already pending to upgrade has been removed".format(pending_image)) - result = subprocess.call(ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) + result = subprocess.call(self.ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) if result: return False - result = subprocess.call(ONIE_FW_UPDATE_CMD_UPDATE.split()) + result = subprocess.call(self.ONIE_FW_UPDATE_CMD_UPDATE.split()) if result: return False except Exception as e: - print("Installing BIOS failed due to {}".format(repr(e))) + print("ERROR: Installing BIOS failed due to {}".format(repr(e))) return False - print("Reboot via \"/sbin/reboot\" is required to finish BIOS installation.") - print("Please don't try installing a new sonic image before BIOS installation finishing") + print("INFO: Reboot via \"/sbin/reboot\" is required to finish BIOS installation.") + print("INFO: Please don't try installing a new sonic image before BIOS installation finishing") return True @@ -226,6 +221,8 @@ class ComponentCPLD(Component): CPLD_UPDATE_COMMAND = "cpldupdate --dev {} {}" CPLD_INSTALL_SUCCESS_FLAG = "PASS!" + MST_DEVICE_PATTERN = "/dev/mst/mt[0-9]*_pciconf0" + def __init__(self): self.name = COMPONENT_CPLD self.image_ext_name = ".vme" @@ -250,11 +247,11 @@ def get_firmware_version(self): """ cpld_version_file_list = glob(self.CPLD_VERSION_FILE_PATTERN) cpld_version = '' - if cpld_version_file_list is not None and cpld_version_file_list: + if cpld_version_file_list: cpld_version_file_list.sort() for version_file in cpld_version_file_list: version = self._read_generic_file(version_file, self.CPLD_VERSION_MAX_LENGTH) - if not cpld_version == '': + if cpld_version: cpld_version += '.' cpld_version += version.rstrip('\n') else: @@ -264,7 +261,7 @@ def get_firmware_version(self): def _get_mst_device(self): - mst_dev_list = glob(MST_DEVICE_PATTERN) + mst_dev_list = glob(self.MST_DEVICE_PATTERN) if mst_dev_list is None or len(mst_dev_list) != 1: return None return mst_dev_list @@ -301,7 +298,7 @@ def install_firmware(self, image_path): mst_dev_list = self._get_mst_device() if mst_dev_list is None: - print("Failed to get mst device which is required for CPLD updating or multiple device files matched") + print("ERROR: Failed to get mst device which is required for CPLD updating or multiple device files matched") return False cmdline = self.CPLD_UPDATE_COMMAND.format(mst_dev_list[0], image_path) @@ -326,8 +323,8 @@ def install_firmware(self, image_path): raise RuntimeError("Failed to execute command {} due to {}".format(cmdline, repr(e))) if success_flag: - print("Success. Refresh or power cycle is required to finish CPLD installation.") + print("INFO: Success. Refresh or power cycle is required to finish CPLD installation.") else: - print("Failed to install CPLD") + print("ERROR: Failed to install CPLD") return success_flag From b4115661aa21e336d7e7bcacf74d1a5463725626 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 13 Dec 2019 14:44:26 +0800 Subject: [PATCH 4/9] [platform_api]add chassis.get_name, add exit code checking --- .../mlnx-platform-api/sonic_platform/chassis.py | 11 +++++++++++ .../mlnx-platform-api/sonic_platform/component.py | 12 ++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 44ef8981281f..a2bf32128817 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -54,6 +54,7 @@ def __init__(self): # Initialize SKU name self.sku_name = self._get_sku_name() + self.name = self.sku_name # move the initialization of each components to their dedicated initializer # which will be called from platform @@ -136,6 +137,16 @@ def initialize_components(self): self._component_list.append(ComponentCPLD()) + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return self.name + + ############################################## # SFP methods ############################################## diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index b8de2ea5b416..a497ef6810a7 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -199,10 +199,10 @@ def install_firmware(self, image_path): self._get_command_result(self.ONIE_FW_UPDATE_CMD_REMOVE.format(pending_image)) print("WARNING: Image {} which is already pending to upgrade has been removed".format(pending_image)) - result = subprocess.call(self.ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) + result = subprocess.check_call(self.ONIE_FW_UPDATE_CMD_ADD.format(image_path).split()) if result: return False - result = subprocess.call(self.ONIE_FW_UPDATE_CMD_UPDATE.split()) + result = subprocess.check_call(self.ONIE_FW_UPDATE_CMD_UPDATE.split()) if result: return False except Exception as e: @@ -214,6 +214,7 @@ def install_firmware(self, image_path): return True + class ComponentCPLD(Component): CPLD_VERSION_FILE_PATTERN = '/var/run/hw-management/system/cpld[0-9]_version' CPLD_VERSION_MAX_LENGTH = 4 @@ -308,17 +309,24 @@ def install_firmware(self, image_path): proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT) while True: out = proc.stdout.read(1) + if out == '' and proc.poll() != None: break + if out != '': sys.stdout.write(out) sys.stdout.flush() outputline += out + if (out == '\n' or out == '\r') and len(outputline): m = re.search(self.CPLD_INSTALL_SUCCESS_FLAG, outputline) if m and m.group(0) == self.CPLD_INSTALL_SUCCESS_FLAG: success_flag = True + if proc.returncode: + print("ERROR: Upgrade CPLD failed, return code {}".format(proc.returncode)) + success_flag = False + except OSError as e: raise RuntimeError("Failed to execute command {} due to {}".format(cmdline, repr(e))) From 37b11cabb7591b3211ca06cba706006185db9188 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Fri, 13 Dec 2019 19:07:20 +0800 Subject: [PATCH 5/9] [component.py]add return code check for subprocess handling --- .../mlnx-platform-api/sonic_platform/component.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index a497ef6810a7..6c825fd9e527 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -55,8 +55,10 @@ def _get_command_result(self, cmdline): try: proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT) stdout = proc.communicate()[0] - proc.wait() + rc = proc.wait() result = stdout.rstrip('\n') + if rc != 0: + raise RuntimeError("Failed to execute command {}, return code {}, message {}".format(cmdline, rc, stdout)) except OSError as e: raise RuntimeError("Failed to execute command {} due to {}".format(cmdline, repr(e))) @@ -136,13 +138,12 @@ def get_firmware_version(self): By using regular expression 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' we can extrace the version string which is marked with * in the above context """ - bios_ver_str = self._get_command_result(self.BIOS_QUERY_VERSION_COMMAND) try: + bios_ver_str = self._get_command_result(self.BIOS_QUERY_VERSION_COMMAND) m = re.search(self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str) result = m.group(1) - except AttributeError as e: - raise RuntimeError("Failed to parse BIOS version by {} from {} due to {}".format( - self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e))) + except (AttributeError, RuntimeError) as e: + raise RuntimeError("Failed to parse BIOS version due to {}".format(repr(e))) return result From 30a4ae7329a1201e4f07c46bd5291917915be0c1 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 18 Dec 2019 18:52:57 +0800 Subject: [PATCH 6/9] [component]update name: onie-fw-updated.sh the onie-fw-update has been postfixed with ".sh" after installed on the DUT. --- .../mlnx-platform-api/sonic_platform/component.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index 6c825fd9e527..106aa4d84770 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -95,10 +95,10 @@ class ComponentBIOS(Component): BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)' BIOS_PENDING_UPDATE_PATTERN = '([0-9A-Za-z_]*.rom)[\s]*\|[\s]*bios_update' - ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}" - ONIE_FW_UPDATE_CMD_REMOVE = "/usr/bin/onie-fw-update remove {}" - ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update" - ONIE_FW_UPDATE_CMD_SHOW = "/usr/bin/onie-fw-update show-pending" + ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update.sh add {}" + ONIE_FW_UPDATE_CMD_REMOVE = "/usr/bin/onie-fw-update.sh remove {}" + ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update.sh update" + ONIE_FW_UPDATE_CMD_SHOW = "/usr/bin/onie-fw-update.sh show-pending" BIOS_QUERY_VERSION_COMMAND = 'dmidecode -t 11' From 9477140a97210d7be18ac6cccf40a64cf6df8a06 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Wed, 18 Dec 2019 20:13:40 +0800 Subject: [PATCH 7/9] [chassis]update the way to get the chassis name to using onie_platform info --- .../mellanox/mlnx-platform-api/sonic_platform/chassis.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index a2bf32128817..16db48614aa2 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -11,6 +11,7 @@ try: from sonic_platform_base.chassis_base import ChassisBase from sonic_platform_base.component_base import ComponentBase + from sonic_device_util import get_machine_info from sonic_daemon_base.daemon_base import Logger from os import listdir from os.path import isfile, join @@ -54,7 +55,11 @@ def __init__(self): # Initialize SKU name self.sku_name = self._get_sku_name() - self.name = self.sku_name + mi = get_machine_info() + if mi is not None: + self.name = mi['onie_platform'] + else: + self.name = self.sku_name # move the initialization of each components to their dedicated initializer # which will be called from platform From 08d885a8b4f2ed2b6834c77e958b442979f40e60 Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 16 Jan 2020 16:28:07 +0800 Subject: [PATCH 8/9] [fwupdate]fix issue that setting next_entry=ONIE breaks the sonic_installer via using the newly introduced onie_entry --- .../mellanox/mlnx-platform-api/sonic_platform/component.py | 3 +-- platform/mellanox/onie-fw-update | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py index 106aa4d84770..dc09ae4011fa 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/component.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/component.py @@ -210,8 +210,7 @@ def install_firmware(self, image_path): print("ERROR: Installing BIOS failed due to {}".format(repr(e))) return False - print("INFO: Reboot via \"/sbin/reboot\" is required to finish BIOS installation.") - print("INFO: Please don't try installing a new sonic image before BIOS installation finishing") + print("INFO: Reboot is required to finish BIOS installation.") return True diff --git a/platform/mellanox/onie-fw-update b/platform/mellanox/onie-fw-update index 220743694b67..1ffafc45c5ac 100755 --- a/platform/mellanox/onie-fw-update +++ b/platform/mellanox/onie-fw-update @@ -61,7 +61,7 @@ change_grub_boot_order() find_onie_menuentry rc=$? if [ $rc -eq 0 ]; then - grub-reboot --boot-directory=$os_boot $onie_entry + grub-editenv $os_boot/grub/grubenv set onie_entry=ONIE else echo "ERROR: ONIE entry wasn't found in grub config" return 1 From c3fa9925af516f608d16d417867601eef3d3908c Mon Sep 17 00:00:00 2001 From: Stephen Sun Date: Thu, 23 Jan 2020 14:48:21 +0800 Subject: [PATCH 9/9] [onie-fw-update]remove find_onie_menuentry. --- platform/mellanox/onie-fw-update | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/platform/mellanox/onie-fw-update b/platform/mellanox/onie-fw-update index 1ffafc45c5ac..314f4ed70268 100755 --- a/platform/mellanox/onie-fw-update +++ b/platform/mellanox/onie-fw-update @@ -10,7 +10,6 @@ this_script=${ONIE_FWPKG_PROGRAM_NAME:-$(basename $(realpath $0))} onie_mount=/mnt/onie-boot os_boot=/host onie_partition= -onie_entry=0 export ONIE_FWPKG_PROGRAM_NAME=$(basename $(realpath $0)) @@ -45,28 +44,9 @@ clean_onie_access() umount $onie_partition } -# ONIE entry must exist in grub config -find_onie_menuentry() -{ - onie_entry="$(cat $os_boot/grub/grub.cfg | grep -e 'menuentry' | cat -n | awk '$0~/ONIE/ {print $1-1}')" - entries_num="$(echo "$onie_entry" | grep -E '^[0-9]+$' | wc -l)" - if [ $entries_num -eq 1 ] && [ $onie_entry -ge 1 ]; then - return 0 - fi - return 1 -} - change_grub_boot_order() { - find_onie_menuentry - rc=$? - if [ $rc -eq 0 ]; then - grub-editenv $os_boot/grub/grubenv set onie_entry=ONIE - else - echo "ERROR: ONIE entry wasn't found in grub config" - return 1 - fi - + grub-editenv $os_boot/grub/grubenv set onie_entry=ONIE grub-editenv $onie_mount/grub/grubenv set onie_mode=update return 0 }