8a07af95e5
Why I did it Requirements from Microsoft for fwutil update all state that all firmwares which support this upgrade flow must support upgrade within a single boot cycle. This conflicted with a number of Mellanox upgrade flows which have been revised to safely meet this requirement. How I did it Added --no-power-cycle flags to SSD and ONIE firmware scripts Modified Platform API to call firmware upgrade flows with this new flag during fwutil update all Added a script to our reboot plugin to handle installing firmwares in the correct order with prior to reboot How to verify it Populate platform_components.json with firmware for CPLD / BIOS / ONIE / SSD Execute fwutil update all fw --boot cold CPLD will burn / ONIE and BIOS images will stage / SSD will schedule for reboot Reboot the switch SSD will install / CPLD will refresh / switch will power cycle into ONIE ONIE installer will upgrade ONIE and BIOS / switch will reboot back into SONiC In SONiC run fwutil show status to check that all firmware upgrades were successful
733 lines
27 KiB
Bash
Executable File
733 lines
27 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2020-2021 NVIDIA CORPORATION & AFFILIATES.
|
|
# Apache-2.0
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
|
|
#==============================================================================#
|
|
#= Global variable #
|
|
#=
|
|
#=====
|
|
VERSION="1.5"
|
|
#=====
|
|
SWITCH_SSD_DEV="/dev/sda"
|
|
UTIL_TITLE="This is MLNX SSD firmware update utility to read and write SSD FW. Version ${VERSION}"
|
|
DEPENDECIES=("smartctl" "sha256sum" "tar" "/bin/bash" "gpg" "sed" "realpath" "dirname")
|
|
TRUE="0"
|
|
FALSE="1"
|
|
ERR_MSG="ERR_MSG"
|
|
INI_PREFIX="ini_section_"
|
|
PUBLIC_CERT_NAME="trusted.gpg"
|
|
CHECKSUM_NAME="checksum"
|
|
SCRIPT_MODE="RELESE" # RELESE -or- DEBUG
|
|
DEBUG_MSG="DEBUG" # remove all instance after script is ready.
|
|
#=====
|
|
PKG_EXTRACTED=$FALSE
|
|
LOGGER_UTIL=$FALSE
|
|
SSD_FW_VER=""
|
|
SSD_DEVICE_MODEL=""
|
|
SSD_SERIAL=""
|
|
SSD_SIZE=""
|
|
SECTIONS=()
|
|
#=====
|
|
ARG_IMAGE_FLAG=$FALSE
|
|
ARG_IMAGE_VAL=""
|
|
ARG_QUERY_FLAG=$FALSE
|
|
ARG_YES_FLAG=$FALSE
|
|
ARG_POWER_CYCLE_FLAG=$FALSE
|
|
ARG_FORCE_POWER_CYCLE_FLAG=$FALSE
|
|
ARG_HELP_FLAG=$FALSE
|
|
ARG_VERSION_FLAG=$FALSE
|
|
ARG_PACKAGE_INFO_FLAG=$FALSE
|
|
ARG_UPDATE_FLAG=$FALSE
|
|
|
|
|
|
|
|
#==============================================================================#
|
|
#= usage function. #
|
|
#=
|
|
function init_script() {
|
|
# check if logger utility supported
|
|
if [ -x "$(command -v logger)" ]; then
|
|
LOGGER_UTIL=$TRUE
|
|
else
|
|
LOGGER_UTIL=$FALSE
|
|
fi
|
|
export LC_ALL=
|
|
export LANG="en_US.UTF-8"
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= usage function. #
|
|
#=
|
|
function usage() {
|
|
echo
|
|
echo -e "$UTIL_TITLE"
|
|
echo
|
|
echo -e "Usage:"
|
|
echo -e "\tmlnx_ssd_fw_update.sh [OPTION]"
|
|
echo -e "Commands:"
|
|
echo -e "\t-i, --image\t\t Path to SSD FW package"
|
|
echo -e "\t-q, --query\t\t Print SSD information (SSD model, serial number, version and size)"
|
|
echo -e "\t\t\t\t Combined with image, comparison is made if update is required"
|
|
echo -e "\t-p, --package-info\t Get package info"
|
|
echo -e "\t-u, --update\t\t Upgrade firmware"
|
|
echo -e "\t-y --yes\t\t Assume \"yes\" to all questions"
|
|
echo -e "\t-V, --version\t\t Print utility version"
|
|
echo -e "\t-h, --help\t\t Show this usage"
|
|
echo -e "\t --power-cycle\t Execute power cycle at completion, even if not required"
|
|
echo
|
|
echo -e "Example:"
|
|
echo -e "\tmlnx_ssd_fw_update.sh -q"
|
|
echo -e "\tmlnx_ssd_fw_update.sh -q -i mlnx_ssd_fw_package.pkg"
|
|
echo -e "\tmlnx_ssd_fw_update.sh -p -i mlnx_ssd_fw_package.pkg"
|
|
echo -e "\tmlnx_ssd_fw_update.sh -u -i mlnx_ssd_fw_package.pkg"
|
|
echo
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= Log function. #
|
|
#=
|
|
function LOG_MSG() {
|
|
if [ $# -gt 0 ]; then
|
|
LOG_STR=$1
|
|
if [[ $# -eq 1 ]]; then
|
|
[[ "$LOGGER_UTIL" == "$TRUE" && "$LOG_STR" != "" ]] && logger -t mlnx_ssd_fw_update.sh -p user.notice $(echo "$LOG_STR" | sed 's/\\t//g')
|
|
echo -e "$LOG_STR"
|
|
elif [[ $# -eq 2 && "$2" == "$ERR_MSG" ]]; then
|
|
[[ "$LOGGER_UTIL" == "$TRUE" && "$LOG_STR" != "" ]] && logger -t mlnx_ssd_fw_update.sh -p user.err $(echo "$LOG_STR" | sed 's/\\t//g')
|
|
echo -e "$LOG_STR"
|
|
elif [[ $# -eq 2 && "$2" == "$SCRIPT_MODE" ]]; then
|
|
echo -e "DBG: $LOG_STR"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= Log function. #
|
|
#=
|
|
function LOG_MSG_AND_EXIT() {
|
|
LOG_MSG "$@" "$ERR_MSG"
|
|
erase_extract_package "$extraction_path"
|
|
LOG_MSG "Exiting..."
|
|
exit 1
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function check if given argument is valid and return boolean result. #
|
|
#=
|
|
function check_usage() {
|
|
local argument_count=$#
|
|
|
|
LOG_MSG "Number of argument:$argument_count" ${DEBUG_MSG}
|
|
|
|
if [ $# -eq 0 ]; then
|
|
LOG_MSG "Error: false usage given."
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
while [[ $# -gt 0 ]]
|
|
do
|
|
key="$1"
|
|
|
|
case $key in
|
|
-i|--image)
|
|
ARG_IMAGE_FLAG=$TRUE
|
|
ARG_IMAGE_VAL="$2"
|
|
shift # past argument
|
|
shift # past value
|
|
;;
|
|
-q|--query)
|
|
ARG_QUERY_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
-y|--yes)
|
|
ARG_YES_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
-h|--help)
|
|
ARG_HELP_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
-V|--version)
|
|
ARG_VERSION_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
-u|--update)
|
|
ARG_UPDATE_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
-p|--package-info)
|
|
ARG_PACKAGE_INFO_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
--power-cycle)
|
|
ARG_POWER_CYCLE_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
--no-power-cycle)
|
|
ARG_FORCE_NO_POWER_CYCLE_FLAG=$TRUE
|
|
shift # past argument
|
|
;;
|
|
*)
|
|
LOG_MSG "Error: false usage given."
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ ("$ARG_IMAGE_FLAG" == "$TRUE" && ( $argument_count -lt 3 )) ||
|
|
("$ARG_IMAGE_FLAG" == "$TRUE" && ( $argument_count -gt 5 )) ||
|
|
("$ARG_PACKAGE_INFO_FLAG" == "$TRUE" && ( $argument_count -ne 3 )) ||
|
|
("$ARG_QUERY_FLAG" == "$TRUE" && ( $argument_count -lt 1 )) ||
|
|
("$ARG_QUERY_FLAG" == "$TRUE" && ( $argument_count -gt 3 )) ||
|
|
("$ARG_HELP_FLAG" == "$TRUE" && ( $argument_count -gt 1 )) ||
|
|
("$ARG_VERSION_FLAG" == "$TRUE" && ( $argument_count -gt 1 )) ||
|
|
("$ARG_IMAGE_FLAG" == "$TRUE" && "$ARG_IMAGE_VAL" == "") ||
|
|
("$ARG_UPDATE_FLAG" == "$TRUE" && "$ARG_IMAGE_FLAG" == "$FALSE") ||
|
|
("$ARG_PACKAGE_INFO_FLAG" == "$TRUE" && "$ARG_IMAGE_FLAG" == "$FALSE") ||
|
|
("$ARG_POWER_CYCLE_FLAG" == "$TRUE" && "$ARG_UPDATE_FLAG" == "$FALSE") ||
|
|
("$ARG_FORCE_NO_POWER_CYCLE_FLAG" == "$TRUE" && "$ARG_POWER_CYCLE_FLAG" == "$TRUE") ||
|
|
("$ARG_UPDATE_FLAG" == "$TRUE" && "$ARG_PACKAGE_INFO_FLAG" == "$TRUE") ]]; then
|
|
|
|
LOG_MSG "Error: false usage given."
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
### Debug message remove when script is done.
|
|
LOG_MSG "ARG_IMAGE_FLAG = ${ARG_IMAGE_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_IMAGE_VAL = ${ARG_IMAGE_VAL}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_QUERY_FLAG = ${ARG_QUERY_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_YES_FLAG = ${ARG_YES_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_HELP_FLAG = ${ARG_HELP_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_VERSION_FLAG = ${ARG_VERSION_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_PACKAGE_INFO_FLAG = ${ARG_PACKAGE_INFO_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_POWER_CYCLE_FLAG = ${ARG_POWER_CYCLE_FLAG}" ${DEBUG_MSG}
|
|
LOG_MSG "ARG_FORCE_NO_POWER_CYCLE_FLAG = ${ARG_FORCE_NO_POWER_CYCLE_FLAG}" ${DEBUG_MSG}
|
|
|
|
}
|
|
|
|
#==============================================================================#
|
|
# This function return SSD fw version using hdparm utility #
|
|
#
|
|
function get_ssd_fw_version() {
|
|
[ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; }
|
|
|
|
local device_fw_version
|
|
device_fw_version=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Firmware Version: +\K[^,]+")
|
|
LOG_MSG "device_fw_version: $device_fw_version" ${DEBUG_MSG}
|
|
eval $1='$device_fw_version'
|
|
}
|
|
|
|
#==============================================================================#
|
|
# This function return SSD device model using hdparm utility #
|
|
#
|
|
function get_ssd_device_model() {
|
|
[ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; }
|
|
|
|
local device_model_name
|
|
device_model_name=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Device Model: +\K[^,]+")
|
|
LOG_MSG "device_model_name: $device_model_name" ${DEBUG_MSG}
|
|
eval $1='$device_model_name'
|
|
}
|
|
|
|
#==============================================================================#
|
|
# This function return SSD size using hdparm utility #
|
|
#
|
|
function get_ssd_size() {
|
|
[ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; }
|
|
|
|
local device_size
|
|
device_size=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "User Capacity:.+bytes \[\K[^ ]+")
|
|
LOG_MSG "device_size: $device_size" ${DEBUG_MSG}
|
|
eval $1='$device_size'
|
|
}
|
|
|
|
#==============================================================================#
|
|
# This function return SSD serial using hdparm utility #
|
|
#
|
|
function get_ssd_serial() {
|
|
[ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; }
|
|
|
|
local device_serial
|
|
device_serial=$(smartctl -i $SWITCH_SSD_DEV | grep -Po "Serial Number: +\K[^,]+")
|
|
LOG_MSG "device_serial: $device_serial" ${DEBUG_MSG}
|
|
eval $1='$device_serial'
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function check if given argument is valid and return boolean result. #
|
|
#=
|
|
function get_ssd_info() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
get_ssd_fw_version SSD_FW_VER
|
|
get_ssd_device_model SSD_DEVICE_MODEL
|
|
get_ssd_serial SSD_SERIAL
|
|
get_ssd_size SSD_SIZE
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function check if given argument is valid and return boolean result. #
|
|
#=
|
|
function check_tool_dependencies() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
for i in "${!DEPENDECIES[@]}"
|
|
do
|
|
if [ ! -x "$(command -v ${DEPENDECIES[$i]})" ]; then
|
|
LOG_MSG_AND_EXIT "Error: This tool require the following utils to be installed ${DEPENDECIES[$i]}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function parse package ini file and declare it attributes #
|
|
#=
|
|
function ini_parser {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
local filename="$1"
|
|
LOG_MSG "filename:$filename" ${DEBUG_MSG}
|
|
|
|
shopt -p extglob &> /dev/null
|
|
CHANGE_EXTGLOB=$?
|
|
if [ $CHANGE_EXTGLOB = 1 ]
|
|
then
|
|
shopt -s extglob
|
|
fi
|
|
ini="$(<$filename)" # read the file
|
|
ini=${ini//$'\r'/} # remove linefeed i.e dos2unix
|
|
ini="${ini//[/\\[}"
|
|
ini="${ini//]/\\]}"
|
|
IFS=$'\n' && ini=( ${ini} ) # convert to line-array
|
|
ini=( ${ini[*]//\)/\\\)} ) # append / before any parenthesis
|
|
ini=( ${ini[*]//\(/\\\(} ) # append / before any parenthesis
|
|
ini=( ${ini[*]/#*([[:space:]]);*/} )
|
|
ini=( ${ini[*]/#*([[:space:]])\#*/} )
|
|
ini=( ${ini[*]/#+([[:space:]])/} ) # remove init whitespace
|
|
ini=( ${ini[*]/%+([[:space:]])/} ) # remove ending whitespace
|
|
ini=( ${ini[*]/*([[:space:]])=*([[:space:]])/=} ) # remove whitespace around =
|
|
ini=( ${ini[*]/#\\[/\}$'\n'"$INI_PREFIX"} ) # set section prefix
|
|
ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1)
|
|
ini=( ${ini[*]/=/=\( } ) # convert item to array
|
|
ini=( ${ini[*]/%/ \)} ) # close array parenthesis
|
|
ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick
|
|
ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2)
|
|
ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis
|
|
ini=( ${ini[*]/%\{/\{$'\n''ini_unset ${FUNCNAME/#'$INI_PREFIX'}'$'\n'} ) # clean previous definition of section
|
|
ini[0]="" # remove first element
|
|
ini[${#ini[*]} + 1]='}' # add the last brace
|
|
eval "$(echo "${ini[*]}")" # eval the result
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: failed to parse package content."
|
|
SECTIONS="$(echo ${ini[*]} | grep -Po "$INI_PREFIX+\K[\w]+")"
|
|
if [ $CHANGE_EXTGLOB = 1 ]
|
|
then
|
|
shopt -u extglob
|
|
fi
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function unset parse ini section and variables #
|
|
#=
|
|
function ini_unset {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
SECTION=$1
|
|
OLDIFS="$IFS"
|
|
IFS=' '$'\n'
|
|
if [ -z "$SECTION" ]
|
|
then
|
|
fun="$(declare -F)"
|
|
else
|
|
fun="$(declare -F $INI_PREFIX$SECTION)"
|
|
if [ -z "$fun" ]
|
|
then
|
|
echo "section $SECTION not found" 1>&2
|
|
return
|
|
fi
|
|
fi
|
|
fun="${fun//declare -f/}"
|
|
for f in $fun; do
|
|
[ "${f#$INI_PREFIX}" == "${f}" ] && continue
|
|
item="$(declare -f ${f})"
|
|
item="${item##*\{}" # remove function definition
|
|
item="${item##*FUNCNAME*$INI_PREFIX\};}" # remove clear section
|
|
item="${item/\}}" # remove function close
|
|
item="${item%)*}" # remove everything after parenthesis
|
|
item="${item});" # add close parenthesis
|
|
vars=""
|
|
while [ "$item" != "" ]
|
|
do
|
|
newvar="${item%%=*}" # get item name
|
|
vars="$vars $newvar" # add name to collection
|
|
item="${item#*;}" # remove readed line
|
|
done
|
|
for var in $vars; do
|
|
unset $var
|
|
done
|
|
done
|
|
IFS="$OLDIFS"
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function check package signing and returns back true or false #
|
|
#=
|
|
function check_package_signing() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
[ $1 ] || { LOG_MSG_AND_EXIT "Wrong usage - ${FUNCNAME[0]}()"; }
|
|
|
|
local package_path=$1
|
|
local checksum_unsigned_file="$package_path/$CHECKSUM_NAME"
|
|
local checksum_signed_file="$package_path/$CHECKSUM_NAME.sig"
|
|
local public_cert_file="$package_path/$PUBLIC_CERT_NAME"
|
|
|
|
### Check if unsigned checksum file exists
|
|
[ ! -f "$checksum_unsigned_file" ] && LOG_MSG_AND_EXIT "Error: fail to find unsigned checksum file to verify package signing."
|
|
|
|
### Check if signed checksum file exists
|
|
[ ! -f "$checksum_signed_file" ] && LOG_MSG_AND_EXIT "Error: fail to find sign checksum file to verify package signing."
|
|
|
|
### Check if public key exists
|
|
[ ! -f "$public_cert_file" ] && LOG_MSG_AND_EXIT "Error: fail to find public certificate to verify package signing."
|
|
|
|
|
|
LOG_MSG "public_cert_file: ${public_cert_file}" ${DEBUG_MSG}
|
|
LOG_MSG "checksum_signed_file: ${checksum_signed_file}" ${DEBUG_MSG}
|
|
LOG_MSG "checksum_unsigned_file: ${checksum_unsigned_file}" ${DEBUG_MSG}
|
|
|
|
gpg --ignore-time-conflict --keyring "$public_cert_file" --verify "$checksum_signed_file" "$checksum_unsigned_file" > /dev/null 2>&1
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: fault package signing."
|
|
|
|
LOG_MSG "cd into: ${package_path}" ${DEBUG_MSG}
|
|
cd $package_path > /dev/null 2>&1
|
|
sha256sum -c $CHECKSUM_NAME > /dev/null 2>&1
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: fault package SHA signing, file has been compromised"
|
|
LOG_MSG "backing back:" ${DEBUG_MSG}
|
|
cd - > /dev/null 2>&1
|
|
LOG_MSG "exiting:" ${DEBUG_MSG}
|
|
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function prints supported SSD from package ini #
|
|
#=
|
|
function string_supported_model() {
|
|
|
|
local section=$1
|
|
|
|
if [[ ! -z "${Vendor[*]}" ]] && [[ ! -z "${SSD_FW_Model[*]}" ]] && [[ ! -z "${SSD_FW_Version[*]}" ]] \
|
|
&& [[ ! -z "${SSD_Size[*]}" ]] && [[ ! -z ${Shutdown_Policy[*]} ]]; then
|
|
printf 'o %-10s | %-30s | %-12s | %-6sGB | %-7s |\n' \
|
|
"$( IFS=$'\n'; echo "${Vendor[@]}" )" "$( IFS=$'\n'; echo "${SSD_FW_Model[@]}" )" \
|
|
"${SSD_FW_Version[@]}" "${SSD_Size[@]}" "${Shutdown_Policy[@],,}"
|
|
fi
|
|
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function extract SSD FW package into /tmp #
|
|
#=
|
|
function extract_package() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
local filename=$1
|
|
LOG_MSG "filename:$filename" ${DEBUG_MSG}
|
|
### Check if file exists
|
|
[ ! -f $filename ] && LOG_MSG_AND_EXIT "Error: given file ($filename) not found."
|
|
### Check if tmp available
|
|
[ ! -d "/tmp" ] && LOG_MSG_AND_EXIT "Error: directory /tmp DOES NOT exists."
|
|
|
|
local base_filename="${filename##*/}"
|
|
local folder_name="/tmp/""${base_filename%%.*}"
|
|
|
|
### Check if full path available
|
|
if [ -d $folder_name ]; then
|
|
LOG_MSG "Path:$folder_name already exists, removing folder." ${DEBUG_MSG}
|
|
rm -rf ${folder_name}
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: folder:$folder_name is already in use and can't be overwrite, please remove it and retry."
|
|
fi
|
|
|
|
mkdir ${folder_name} && tar xf ${filename} -C ${folder_name} --strip-components 1 --warning=no-timestamp > /dev/null 2>&1
|
|
#tar -xf $filename --directory /tmp/ --warning=no-timestamp > /dev/null 2>&1
|
|
### Check if untar succeed.
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: fail to extract given package ($filename)."
|
|
|
|
### return the path file extraction is
|
|
# local base_filename="${filename##*/}"
|
|
# local folder_name="/tmp/""${base_filename%%.*}"
|
|
eval $2="$folder_name"
|
|
|
|
PKG_EXTRACTED=$TRUE
|
|
|
|
check_package_signing $folder_name
|
|
|
|
LOG_MSG "successfully untar file." ${DEBUG_MSG}
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function extract SSD FW package into /tmp #
|
|
#=
|
|
function erase_extract_package() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
[[ "$PKG_EXTRACTED" == "$FALSE" ]] && return
|
|
|
|
local folder_name=$1
|
|
|
|
LOG_MSG "folder_name: $folder_name" ${DEBUG_MSG}
|
|
|
|
### Check if folder exists
|
|
if [ ! -d "$folder_name" ]; then
|
|
LOG_MSG "Error: directory $folder_name DOES NOT exists." "$ERR_MSG"
|
|
LOG_MSG "Exiting..."
|
|
exit 1
|
|
fi
|
|
rm -rf $folder_name
|
|
### Check if untar succeed.
|
|
if [ $? -ne 0 ]; then
|
|
LOG_MSG "Error: fail to delete $folder_name folder." "$ERR_MSG"
|
|
LOG_MSG "Exiting..."
|
|
exit 1
|
|
fi
|
|
|
|
PKG_EXTRACTED=$FALSE
|
|
LOG_MSG "successfully removed folder:$folder_name" ${DEBUG_MSG}
|
|
}
|
|
|
|
|
|
#==============================================================================#
|
|
#= This function returns back ini section array.
|
|
#=
|
|
function call_ini_section() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
local ini_section=$1
|
|
LOG_MSG "ini_section:$ini_section" ${DEBUG_MSG}
|
|
|
|
[[ -z "$ini_section" ]] && LOG_MSG_AND_EXIT "Error: given INI section is null."
|
|
[[ -z "$(declare -F "$INI_PREFIX$ini_section")" ]] && LOG_MSG_AND_EXIT "Error: $ini_section section is missing in INI file."
|
|
|
|
eval "$(echo "$INI_PREFIX$ini_section")" # call given section function.
|
|
}
|
|
|
|
#==============================================================================#
|
|
#= This function prints ssd info
|
|
#=
|
|
function print_ssd_info() {
|
|
LOG_MSG "func: ${FUNCNAME[0]}()" ${DEBUG_MSG}
|
|
|
|
local argument_count=$#
|
|
|
|
if [ $argument_count -eq 2 ]; then
|
|
local newer_fw_version=$1
|
|
local power_policy=$2
|
|
LOG_MSG "Device Model\t\t : $SSD_DEVICE_MODEL"
|
|
LOG_MSG "Serial Number\t\t : $SSD_SERIAL"
|
|
LOG_MSG "User Capacity\t\t : $SSD_SIZE GB"
|
|
LOG_MSG "Current Firmware Version : $SSD_FW_VER"
|
|
LOG_MSG "Available Firmware Version : $Newer_FW_Version"
|
|
LOG_MSG "Power Cycle Required\t : $power_policy"
|
|
LOG_MSG "Upgrade Required\t : yes"
|
|
elif [ $argument_count -eq 1 ]; then
|
|
local _upgrade_require=$1
|
|
LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL"
|
|
LOG_MSG "Serial Number\t : $SSD_SERIAL"
|
|
LOG_MSG "User Capacity\t : $SSD_SIZE GB"
|
|
LOG_MSG "Firmware Version : $SSD_FW_VER"
|
|
LOG_MSG "Upgrade Required : $_upgrade_require"
|
|
else
|
|
LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL"
|
|
LOG_MSG "Serial Number\t : $SSD_SERIAL"
|
|
LOG_MSG "User Capacity\t : $SSD_SIZE GB"
|
|
LOG_MSG "Firmware Version : $SSD_FW_VER"
|
|
fi
|
|
}
|
|
|
|
# Main
|
|
# ------------------------------------------------------------------------------
|
|
init_script
|
|
check_usage "$@"
|
|
|
|
# show help
|
|
if [ $ARG_HELP_FLAG == $TRUE ]; then
|
|
usage
|
|
exit 0
|
|
# show version
|
|
elif [ $ARG_VERSION_FLAG == $TRUE ]; then
|
|
echo $UTIL_TITLE
|
|
exit 0
|
|
# show SSD info
|
|
elif [ $ARG_QUERY_FLAG == $TRUE ]; then
|
|
match_found=$FALSE
|
|
check_tool_dependencies
|
|
get_ssd_info
|
|
|
|
if [ $ARG_IMAGE_FLAG == $TRUE ]; then
|
|
extract_package $ARG_IMAGE_VAL extraction_path
|
|
ini_parser "$extraction_path/list.ini"
|
|
|
|
for section in $SECTIONS; do
|
|
if [[ $section != "main" ]]; then
|
|
call_ini_section $section
|
|
if [[ "$SSD_DEVICE_MODEL" == "$( IFS=$'\n'; echo "${SSD_FW_Model[@]}" )" ]] && \
|
|
[[ "$SSD_FW_VER" == "${SSD_FW_Version[@]}" ]] && \
|
|
[[ "$SSD_SIZE" == "${SSD_Size[@]}" ]]; then
|
|
|
|
match_found=$TRUE
|
|
break
|
|
fi
|
|
ini_unset $section
|
|
fi
|
|
done
|
|
|
|
erase_extract_package "$extraction_path"
|
|
if [[ "$match_found" == "$FALSE" ]]; then
|
|
#LOG_MSG "SSD FW upgrade not require, based on given package latest version is in used."
|
|
print_ssd_info "no"
|
|
echo -e ""
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
if [[ "$match_found" == "$TRUE" ]]; then
|
|
print_ssd_info $Newer_FW_Version ${Shutdown_Policy[0],,}
|
|
erase_extract_package "$extraction_path"
|
|
else
|
|
print_ssd_info
|
|
fi
|
|
|
|
echo -e ""
|
|
|
|
exit 0
|
|
# show package version
|
|
elif [ $ARG_PACKAGE_INFO_FLAG == $TRUE ]; then
|
|
check_tool_dependencies
|
|
extract_package $ARG_IMAGE_VAL extraction_path
|
|
# 2. check signing
|
|
ini_parser "$extraction_path/list.ini"
|
|
|
|
call_ini_section "main"
|
|
LOG_MSG "Package Name: $ARG_IMAGE_VAL"
|
|
[[ ! -z ${description[@]} ]] && LOG_MSG "Description: ${description[@]}"
|
|
[[ ! -z ${version[@]} ]] && LOG_MSG "Version: ${version[@]}"
|
|
[[ ! -z ${release_date[@]} ]] && LOG_MSG "Release Date: ${release_date[@]}"
|
|
LOG_MSG "Supported SSDs:"
|
|
LOG_MSG " Vendor | Model | FW ver | Size | Pwr Cyc Req |"
|
|
LOG_MSG "=============|================================|==============|==========|=============|"
|
|
for section in $SECTIONS; do
|
|
if [[ "$section" != "main" ]]; then
|
|
call_ini_section $section
|
|
supported_model=$(string_supported_model $section)
|
|
LOG_MSG "$supported_model"
|
|
ini_unset $section
|
|
fi
|
|
done
|
|
echo -e ""
|
|
erase_extract_package "$extraction_path"
|
|
exit 0
|
|
# operate SSD fw update
|
|
elif [ $ARG_UPDATE_FLAG == $TRUE ]; then
|
|
check_tool_dependencies
|
|
get_ssd_info
|
|
extract_package $ARG_IMAGE_VAL extraction_path
|
|
# 2. check signing
|
|
UPDATE_DONE=$FALSE
|
|
ini_parser "$extraction_path/list.ini"
|
|
for section in $SECTIONS; do
|
|
if [[ $section != "main" ]]; then
|
|
call_ini_section $section
|
|
if [[ "$SSD_DEVICE_MODEL" == "$( IFS=$'\n'; echo "${SSD_FW_Model[@]}" )" ]] && \
|
|
[[ "$SSD_FW_VER" == "${SSD_FW_Version[@]}" ]] && \
|
|
[[ "$SSD_SIZE" == "${SSD_Size[@]}" ]]; then
|
|
UPDATE_DONE=$TRUE
|
|
|
|
power_policy=${Shutdown_Policy[0],,}
|
|
LOG_MSG "Power policy:$power_policy" ${DEBUG_MSG}
|
|
print_ssd_info $Newer_FW_Version ${Shutdown_Policy[0],,}
|
|
echo -e ""
|
|
#[[ "yes" == "$power_policy" ]] && LOG_MSG "PLEASE NOTE: System will power-cycle automatically once SSD FW Update complete!"
|
|
[[ "yes" == "$power_policy" || "$ARG_POWER_CYCLE_FLAG" == "$TRUE" ]] && LOG_MSG "Please note: Once SSD FW Update process ends, system will power-cycle automaticly and it will take up to 1 minute to access it back."
|
|
|
|
# Prompt approval for FW update if ignore in case "yes" flag is on.
|
|
if [[ "$ARG_YES_FLAG" == "$FALSE" ]]; then
|
|
read -p "Do you want to continue? [Y/N]" -n 1 -r
|
|
echo # (optional) move to a new line
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
LOG_MSG_AND_EXIT "Aborting..."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Check FWUpgrade scripts exists & if so call it.
|
|
ssd_script_name=$Update_Script
|
|
ssd_script_path="${extraction_path}/${section}/${ssd_script_name}"
|
|
LOG_MSG "ssd_script_path: $ssd_script_path" ${DEBUG_MSG}
|
|
if [ ! -f $ssd_script_path ]; then
|
|
LOG_MSG_AND_EXIT "Error: fail to call upgrade script ($ssd_script_path)!"
|
|
fi
|
|
(
|
|
if [[ "yes" == "$power_policy" && $ARG_FORCE_NO_POWER_CYCLE_FLAG == $TRUE ]]; then
|
|
# If a power cycle is required and we are not power cycling automatically lock the file system for safety
|
|
LOG_MSG "Immediate power cycle is required but override flag has been given. Locking file system as read only to protect system integrity."
|
|
echo u > /proc/sysrq-trigger
|
|
fi
|
|
cd "${extraction_path}/${section}" > /dev/null 2>&1 || exit
|
|
/bin/bash "$ssd_script_path" "${extraction_path}/${section}"
|
|
#cd - > /dev/null 2>&1 || exit
|
|
)
|
|
if [ $? -ne 0 ]; then
|
|
LOG_MSG_AND_EXIT "Error: SSD FW update failed."
|
|
else
|
|
LOG_MSG "SSD FW update completed successfully."
|
|
|
|
if [[ "yes" == "$power_policy" || $ARG_POWER_CYCLE_FLAG == $TRUE ]]; then
|
|
|
|
if [[ $ARG_FORCE_NO_POWER_CYCLE_FLAG == $TRUE ]]; then
|
|
LOG_MSG_AND_EXIT "An IMMEDIATE power cycle is REQUIRED to upgrade the SSD. Please perform a cold reboot as soon as possible."
|
|
fi
|
|
|
|
LOG_MSG "Execute power cycle..."
|
|
sleep 1
|
|
sync
|
|
power_cycle_script="${extraction_path}/common/mlnx_shutdown.sh"
|
|
[ ! -f $power_cycle_script ]&& LOG_MSG_AND_EXIT "Error: failed to initiate power cycle."
|
|
($power_cycle_script "-s")
|
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: failed to power cycle the system automatically."
|
|
erase_extract_package "$extraction_path"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
break # Exit the for loop
|
|
fi
|
|
ini_unset $section
|
|
fi
|
|
done
|
|
if [ $UPDATE_DONE == $FALSE ]; then
|
|
LOG_MSG "SSD FW upgrade is not required, latest version based on given package is in use."
|
|
print_ssd_info "no"
|
|
fi
|
|
|
|
echo -e ""
|
|
erase_extract_package "$extraction_path"
|
|
exit 0
|
|
fi
|
|
|
|
exit 0
|