[Mellanox] Add ONIE and SSD platform components. (#4758)
Signed-off-by: Nazarii Hnydyn <nazariig@mellanox.com>
This commit is contained in:
parent
0a750a6b54
commit
1db64a3bc1
@ -1,120 +1,205 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
# Copyright (C) 2019 Mellanox Technologies Ltd.
|
# Copyright (C) 2019 Mellanox Technologies Ltd.
|
||||||
# Copyright (C) 2019 Michael Shych <michaelsh@mellanox.com>
|
# Copyright (C) 2019 Michael Shych <michaelsh@mellanox.com>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
this_script=${ONIE_FWPKG_PROGRAM_NAME:-$(basename $(realpath $0))}
|
this_script="$(basename $(realpath ${0}))"
|
||||||
|
lock_file="/var/run/${this_script%.*}.lock"
|
||||||
|
|
||||||
onie_mount=/mnt/onie-boot
|
onie_mount=/mnt/onie-boot
|
||||||
|
onie_lib=/lib/onie
|
||||||
os_boot=/host
|
os_boot=/host
|
||||||
onie_partition=
|
|
||||||
|
|
||||||
export ONIE_FWPKG_PROGRAM_NAME=$(basename $(realpath $0))
|
print_help() {
|
||||||
|
|
||||||
usage()
|
|
||||||
{
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
update
|
update
|
||||||
The 'update' command will reboot system to ONIE update mode
|
The 'update' command will reboot system to ONIE update mode
|
||||||
and ONIE will perform automatically update of previously
|
and ONIE will perform automatically update of previously
|
||||||
added (i.e. pending) FW (ONIE itself, BIOS or CPLD) image.
|
added (i.e. pending) FW (ONIE itself, BIOS or CPLD) image.
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_onie_access()
|
enable_onie_access() {
|
||||||
{
|
if [[ ! -d "${onie_mount}" ]]; then
|
||||||
onie_partition=$(fdisk -l | grep "ONIE boot" | awk '{print $1}')
|
mkdir ${onie_mount}
|
||||||
if [ ! -d $onie_mount ]; then
|
fi
|
||||||
mkdir /mnt/onie-boot
|
|
||||||
fi
|
if ! mountpoint -q "${onie_mount}"; then
|
||||||
mount $onie_partition /mnt/onie-boot
|
mount LABEL="ONIE-BOOT" ${onie_mount}
|
||||||
if [ ! -e /lib/onie ]; then
|
fi
|
||||||
ln -s /mnt/onie-boot/onie/tools/lib/onie /lib/onie
|
|
||||||
fi
|
if [[ ! -e "${onie_lib}" ]]; then
|
||||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$onie_mount/onie/tools/bin/
|
ln -s ${onie_mount}/onie/tools/lib/onie ${onie_lib}
|
||||||
export PATH
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
clean_onie_access()
|
disable_onie_access() {
|
||||||
{
|
if [[ -e "${onie_lib}" ]]; then
|
||||||
rm -f /lib/onie
|
unlink ${onie_lib}
|
||||||
umount $onie_partition
|
fi
|
||||||
|
|
||||||
|
if mountpoint -q "${onie_mount}"; then
|
||||||
|
umount -rf ${onie_mount}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "${onie_mount}" ]]; then
|
||||||
|
rmdir ${onie_mount}
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
change_grub_boot_order()
|
enable_onie_fw_update_mode() {
|
||||||
{
|
if [[ ! -f ${os_boot}/grub/grubenv || ! -f ${onie_mount}/grub/grubenv ]]; then
|
||||||
grub-editenv $os_boot/grub/grubenv set onie_entry=ONIE
|
return 1
|
||||||
grub-editenv $onie_mount/grub/grubenv set onie_mode=update
|
fi
|
||||||
return 0
|
|
||||||
|
register_terminate_handler
|
||||||
|
|
||||||
|
grub-editenv ${os_boot}/grub/grubenv set onie_entry="ONIE" || return $?
|
||||||
|
grub-editenv ${onie_mount}/grub/grubenv set onie_mode="update" || return $?
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
show_pending()
|
disable_onie_fw_update_mode() {
|
||||||
{
|
if [[ ! -f ${os_boot}/grub/grubenv || ! -f ${onie_mount}/grub/grubenv ]]; then
|
||||||
curr_dir=$(pwd)
|
return 1
|
||||||
cd $onie_mount/onie/update/pending || return 0
|
fi
|
||||||
num=$(find . -type f | wc -l)
|
|
||||||
if [ $num -ge 1 ]; then
|
|
||||||
echo "Number of FW update pending files are: "$num
|
|
||||||
ls -l * | awk {'print $9" "$5" "$7"-"$6" "$8'}
|
|
||||||
else
|
|
||||||
echo "There is no pending files for FW update."
|
|
||||||
fi
|
|
||||||
cd $curr_dir
|
|
||||||
|
|
||||||
return $num
|
grub-editenv ${os_boot}/grub/grubenv unset onie_entry || return $?
|
||||||
|
grub-editenv ${onie_mount}/grub/grubenv set onie_mode="install" || return $?
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_pending() {
|
||||||
|
if [[ ! -d ${onie_mount}/onie/update/pending ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
num=$(find ${onie_mount}/onie/update/pending -type f | wc -l)
|
||||||
|
if [[ ${num} -ge 1 ]]; then
|
||||||
|
${onie_mount}/onie/tools/bin/onie-fwpkg show-pending
|
||||||
|
fi
|
||||||
|
|
||||||
|
return ${num}
|
||||||
|
}
|
||||||
|
|
||||||
|
system_reboot() {
|
||||||
|
echo "INFO: Rebooting in 5 sec..."
|
||||||
|
|
||||||
|
# Give user some time to cancel the update
|
||||||
|
sleep 5s
|
||||||
|
|
||||||
|
# Use SONiC reboot scenario
|
||||||
|
/usr/bin/reboot
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate_handler() {
|
||||||
|
local -r _rc="$?"
|
||||||
|
local -r _sig="${1}"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "WARNING: Interrupted by ${_sig}: disable ONIE firmware update mode"
|
||||||
|
echo
|
||||||
|
|
||||||
|
enable_onie_access
|
||||||
|
disable_onie_fw_update_mode
|
||||||
|
rc=$?
|
||||||
|
disable_onie_access
|
||||||
|
|
||||||
|
if [[ ${rc} -ne 0 ]]; then
|
||||||
|
echo "ERROR: failed to disable ONIE firmware update mode"
|
||||||
|
exit ${rc}
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit ${_rc}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_terminate_handler() {
|
||||||
|
trap "terminate_handler SIGHUP" SIGHUP
|
||||||
|
trap "terminate_handler SIGINT" SIGINT
|
||||||
|
trap "terminate_handler SIGQUIT" SIGQUIT
|
||||||
|
trap "terminate_handler SIGTERM" SIGTERM
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_handler() {
|
||||||
|
/usr/bin/flock -u ${1}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_unlock_handler() {
|
||||||
|
trap "unlock_handler ${1}" EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_script_state_change() {
|
||||||
|
/usr/bin/flock -u ${lock_fd}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_script_state_change(){
|
||||||
|
exec {lock_fd}>${lock_file}
|
||||||
|
/usr/bin/flock -x ${lock_fd}
|
||||||
|
register_unlock_handler ${lock_fd}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Multiprocessing synchronization
|
||||||
|
lock_script_state_change
|
||||||
|
|
||||||
# Process command arguments
|
# Process command arguments
|
||||||
cmd=$1
|
cmd="${1}"
|
||||||
# optional argument
|
|
||||||
name="$2"
|
|
||||||
|
|
||||||
if [ -z "$cmd" ] ; then
|
# Optional argument
|
||||||
# Default to 'show' if no command is specified.
|
arg="${2}"
|
||||||
cmd="show"
|
|
||||||
|
if [[ -z "${cmd}" ]]; then
|
||||||
|
# Default to 'show' if no command is specified.
|
||||||
|
cmd="show"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$cmd" in
|
case "${cmd}" in
|
||||||
add | remove)
|
add|remove)
|
||||||
[ -z "$name" ] && {
|
if [[ -z "${arg}" ]]; then
|
||||||
echo "ERROR: This command requires a firmware update file name."
|
echo "ERROR: This command requires a firmware update file name"
|
||||||
echo "Run '$this_script help' for complete details."
|
echo "Run: '${this_script} help' for complete details"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
fi
|
||||||
;;
|
;;
|
||||||
update)
|
update)
|
||||||
enable_onie_access
|
enable_onie_access
|
||||||
show_pending
|
show_pending
|
||||||
rc=$?
|
rc=$?
|
||||||
if [ $rc -ne 0 ]; then
|
if [[ ${rc} -ne 0 ]]; then
|
||||||
change_grub_boot_order
|
enable_onie_fw_update_mode
|
||||||
rc=$?
|
rc=$?
|
||||||
clean_onie_access
|
disable_onie_access
|
||||||
exit $rc
|
if [[ ${rc} -eq 0 ]]; then
|
||||||
else
|
system_reboot
|
||||||
echo "ERROR: NO FW images for update."
|
else
|
||||||
echo "Run: $this_script add <image> before update."
|
echo "ERROR: failed to enable ONIE firmware update mode"
|
||||||
clean_onie_access
|
exit ${rc}
|
||||||
exit 1
|
fi
|
||||||
fi
|
else
|
||||||
;;
|
echo "ERROR: No firmware images for update"
|
||||||
purge | show | show-results | show-log | show-pending | help)
|
echo "Run: '${this_script} add <image>' before update"
|
||||||
;;
|
disable_onie_access
|
||||||
*)
|
exit 1
|
||||||
echo "Unknown command: $cmd"
|
fi
|
||||||
exit 1
|
;;
|
||||||
;;
|
purge|show-pending|show-results|show|show-log|help)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: Unknown command: ${cmd}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
enable_onie_access
|
enable_onie_access
|
||||||
$onie_mount/onie/tools/bin/onie-fwpkg "$@"
|
${onie_mount}/onie/tools/bin/onie-fwpkg "$@"
|
||||||
rc=$?
|
rc=$?
|
||||||
if [ $cmd = "help" ]; then
|
if [[ "${cmd}" = "help" ]]; then
|
||||||
usage
|
print_help
|
||||||
fi
|
fi
|
||||||
clean_onie_access
|
disable_onie_access
|
||||||
|
|
||||||
exit $rc
|
exit ${rc}
|
||||||
|
@ -149,7 +149,9 @@ class Chassis(ChassisBase):
|
|||||||
|
|
||||||
def initialize_components(self):
|
def initialize_components(self):
|
||||||
# Initialize component list
|
# Initialize component list
|
||||||
from sonic_platform.component import ComponentBIOS, ComponentCPLD
|
from sonic_platform.component import ComponentONIE, ComponentSSD, ComponentBIOS, ComponentCPLD
|
||||||
|
self._component_list.append(ComponentONIE())
|
||||||
|
self._component_list.append(ComponentSSD())
|
||||||
self._component_list.append(ComponentBIOS())
|
self._component_list.append(ComponentBIOS())
|
||||||
self._component_list.extend(ComponentCPLD.get_component_list())
|
self._component_list.extend(ComponentCPLD.get_component_list())
|
||||||
|
|
||||||
|
@ -6,19 +6,308 @@
|
|||||||
# implementation of new platform api
|
# implementation of new platform api
|
||||||
#############################################################################
|
#############################################################################
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
|
||||||
try:
|
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 os
|
||||||
|
import io
|
||||||
import re
|
import re
|
||||||
|
import glob
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
import ConfigParser
|
||||||
|
|
||||||
|
from sonic_platform_base.component_base import ComponentBase
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
ZERO = '0'
|
|
||||||
NEWLINE = '\n'
|
class MPFAManager(object):
|
||||||
|
MPFA_EXTENSION = '.mpfa'
|
||||||
|
|
||||||
|
MPFA_EXTRACT_COMMAND = 'tar xzf {} -C {}'
|
||||||
|
MPFA_CLEANUP_COMMAND = 'rm -rf {}'
|
||||||
|
|
||||||
|
def __init__(self, mpfa_path):
|
||||||
|
self.__mpfa_path = mpfa_path
|
||||||
|
self.__contents_path = None
|
||||||
|
self.__metadata = None
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.extract()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
def __validate_path(self, mpfa_path):
|
||||||
|
if not os.path.isfile(mpfa_path):
|
||||||
|
raise RuntimeError("MPFA doesn't exist: path={}".format(mpfa_path))
|
||||||
|
|
||||||
|
name, ext = os.path.splitext(mpfa_path)
|
||||||
|
if ext != self.MPFA_EXTENSION:
|
||||||
|
raise RuntimeError("MPFA doesn't have valid extension: path={}".format(mpfa_path))
|
||||||
|
|
||||||
|
def __extract_contents(self, mpfa_path):
|
||||||
|
contents_path = tempfile.mkdtemp(prefix='mpfa-')
|
||||||
|
|
||||||
|
cmd = self.MPFA_EXTRACT_COMMAND.format(mpfa_path, contents_path)
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
|
||||||
|
self.__contents_path = contents_path
|
||||||
|
|
||||||
|
def __parse_metadata(self, contents_path):
|
||||||
|
metadata_path = os.path.join(contents_path, 'metadata.ini')
|
||||||
|
|
||||||
|
if not os.path.isfile(metadata_path):
|
||||||
|
raise RuntimeError("MPFA metadata doesn't exist: path={}".format(metadata_path))
|
||||||
|
|
||||||
|
cp = ConfigParser.ConfigParser()
|
||||||
|
with io.open(metadata_path, 'r') as metadata_ini:
|
||||||
|
cp.readfp(metadata_ini)
|
||||||
|
|
||||||
|
self.__metadata = cp
|
||||||
|
|
||||||
|
def extract(self):
|
||||||
|
if self.is_extracted():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__validate_path(self.__mpfa_path)
|
||||||
|
self.__extract_contents(self.__mpfa_path)
|
||||||
|
self.__parse_metadata(self.__contents_path)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
if os.path.exists(self.__contents_path):
|
||||||
|
cmd = self.MPFA_CLEANUP_COMMAND.format(self.__contents_path)
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
|
||||||
|
self.__contents_path = None
|
||||||
|
self.__metadata = None
|
||||||
|
|
||||||
|
def get_path(self):
|
||||||
|
return self.__contents_path
|
||||||
|
|
||||||
|
def get_metadata(self):
|
||||||
|
return self.__metadata
|
||||||
|
|
||||||
|
def is_extracted(self):
|
||||||
|
return self.__contents_path is not None and os.path.exists(self.__contents_path)
|
||||||
|
|
||||||
|
|
||||||
|
class ONIEUpdater(object):
|
||||||
|
ONIE_FW_UPDATE_CMD_ADD = '/usr/bin/mlnx-onie-fw-update.sh add {}'
|
||||||
|
ONIE_FW_UPDATE_CMD_REMOVE = '/usr/bin/mlnx-onie-fw-update.sh remove {}'
|
||||||
|
ONIE_FW_UPDATE_CMD_UPDATE = '/usr/bin/mlnx-onie-fw-update.sh update'
|
||||||
|
ONIE_FW_UPDATE_CMD_SHOW_PENDING = '/usr/bin/mlnx-onie-fw-update.sh show-pending'
|
||||||
|
|
||||||
|
ONIE_VERSION_PARSE_PATTERN = '([0-9]{4})\.([0-9]{2})-([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)'
|
||||||
|
ONIE_VERSION_BASE_PARSE_PATTERN = '([0-9]+)\.([0-9]+)\.([0-9]+)'
|
||||||
|
ONIE_VERSION_REQUIRED = '5.2.0016'
|
||||||
|
|
||||||
|
ONIE_VERSION_ATTR = 'onie_version'
|
||||||
|
ONIE_NO_PENDING_UPDATES_ATTR = 'No pending firmware updates present'
|
||||||
|
|
||||||
|
ONIE_IMAGE_INFO_COMMAND = '/bin/bash {} -q -i'
|
||||||
|
|
||||||
|
def __mount_onie_fs(self):
|
||||||
|
fs_mountpoint = '/mnt/onie-fs'
|
||||||
|
onie_path = '/lib/onie'
|
||||||
|
|
||||||
|
if os.path.lexists(onie_path) or os.path.exists(fs_mountpoint):
|
||||||
|
self.__umount_onie_fs()
|
||||||
|
|
||||||
|
cmd = "fdisk -l | grep 'ONIE boot' | awk '{print $1}'"
|
||||||
|
fs_path = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).rstrip('\n')
|
||||||
|
|
||||||
|
os.mkdir(fs_mountpoint)
|
||||||
|
cmd = "mount -n -r -t ext4 {} {}".format(fs_path, fs_mountpoint)
|
||||||
|
subprocess.check_call(cmd, shell=True)
|
||||||
|
|
||||||
|
fs_onie_path = os.path.join(fs_mountpoint, 'onie/tools/lib/onie')
|
||||||
|
os.symlink(fs_onie_path, onie_path)
|
||||||
|
|
||||||
|
return fs_mountpoint
|
||||||
|
|
||||||
|
def __umount_onie_fs(self):
|
||||||
|
fs_mountpoint = '/mnt/onie-fs'
|
||||||
|
onie_path = '/lib/onie'
|
||||||
|
|
||||||
|
if os.path.islink(onie_path):
|
||||||
|
os.unlink(onie_path)
|
||||||
|
|
||||||
|
if os.path.ismount(fs_mountpoint):
|
||||||
|
cmd = "umount -rf {}".format(fs_mountpoint)
|
||||||
|
subprocess.check_call(cmd, shell=True)
|
||||||
|
|
||||||
|
if os.path.exists(fs_mountpoint):
|
||||||
|
os.rmdir(fs_mountpoint)
|
||||||
|
|
||||||
|
def __stage_update(self, image_path):
|
||||||
|
cmd = self.ONIE_FW_UPDATE_CMD_ADD.format(image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to stage firmware update: {}".format(str(e)))
|
||||||
|
|
||||||
|
def __unstage_update(self, image_path):
|
||||||
|
cmd = self.ONIE_FW_UPDATE_CMD_REMOVE.format(os.path.basename(image_path))
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to unstage firmware update: {}".format(str(e)))
|
||||||
|
|
||||||
|
def __trigger_update(self):
|
||||||
|
cmd = self.ONIE_FW_UPDATE_CMD_UPDATE
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to trigger firmware update: {}".format(str(e)))
|
||||||
|
|
||||||
|
def __is_update_staged(self, image_path):
|
||||||
|
cmd = self.ONIE_FW_UPDATE_CMD_SHOW_PENDING
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get pending firmware updates: {}".format(str(e)))
|
||||||
|
|
||||||
|
basename = os.path.basename(image_path)
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(basename):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def parse_onie_version(self, version, is_base=False):
|
||||||
|
onie_year = None
|
||||||
|
onie_month = None
|
||||||
|
onie_major = None
|
||||||
|
onie_minor = None
|
||||||
|
onie_release = None
|
||||||
|
onie_baudrate = None
|
||||||
|
|
||||||
|
if is_base:
|
||||||
|
pattern = self.ONIE_VERSION_BASE_PARSE_PATTERN
|
||||||
|
|
||||||
|
m = re.search(pattern, version)
|
||||||
|
if not m:
|
||||||
|
raise RuntimeError("Failed to parse ONIE version: pattern={}, version={}".format(pattern, version))
|
||||||
|
|
||||||
|
onie_major = m.group(1)
|
||||||
|
onie_minor = m.group(2)
|
||||||
|
onie_release = m.group(3)
|
||||||
|
|
||||||
|
return onie_year, onie_month, onie_major, onie_minor, onie_release, onie_baudrate
|
||||||
|
|
||||||
|
pattern = self.ONIE_VERSION_PARSE_PATTERN
|
||||||
|
|
||||||
|
m = re.search(pattern, version)
|
||||||
|
if not m:
|
||||||
|
raise RuntimeError("Failed to parse ONIE version: pattern={}, version={}".format(pattern, version))
|
||||||
|
|
||||||
|
onie_year = m.group(1)
|
||||||
|
onie_month = m.group(2)
|
||||||
|
onie_major = m.group(3)
|
||||||
|
onie_minor = m.group(4)
|
||||||
|
onie_release = m.group(5)
|
||||||
|
onie_baudrate = m.group(6)
|
||||||
|
|
||||||
|
return onie_year, onie_month, onie_major, onie_minor, onie_release, onie_baudrate
|
||||||
|
|
||||||
|
def get_onie_required_version(self):
|
||||||
|
return self.ONIE_VERSION_REQUIRED
|
||||||
|
|
||||||
|
def get_onie_version(self):
|
||||||
|
version = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
fs_mountpoint = self.__mount_onie_fs()
|
||||||
|
machine_conf_path = os.path.join(fs_mountpoint, 'onie/grub/grub-machine.cfg')
|
||||||
|
|
||||||
|
with open(machine_conf_path, 'r') as machine_conf:
|
||||||
|
for line in machine_conf:
|
||||||
|
if line.startswith(self.ONIE_VERSION_ATTR):
|
||||||
|
items = line.rstrip('\n').split('=')
|
||||||
|
|
||||||
|
if len(items) != 2:
|
||||||
|
raise RuntimeError("Failed to parse ONIE info: line={}".format(line))
|
||||||
|
|
||||||
|
version = items[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
if version is None:
|
||||||
|
raise RuntimeError("Failed to parse ONIE version")
|
||||||
|
finally:
|
||||||
|
self.__umount_onie_fs()
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
def get_onie_firmware_info(self, image_path):
|
||||||
|
firmware_info = { }
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__mount_onie_fs()
|
||||||
|
|
||||||
|
cmd = self.ONIE_IMAGE_INFO_COMMAND.format(image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get ONIE firmware info: {}".format(str(e)))
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
items = line.split('=')
|
||||||
|
|
||||||
|
if len(items) != 2:
|
||||||
|
raise RuntimeError("Failed to parse ONIE firmware info: line={}".format(line))
|
||||||
|
|
||||||
|
firmware_info[items[0]] = items[1]
|
||||||
|
finally:
|
||||||
|
self.__umount_onie_fs()
|
||||||
|
|
||||||
|
return firmware_info
|
||||||
|
|
||||||
|
def update_firmware(self, image_path):
|
||||||
|
cmd = self.ONIE_FW_UPDATE_CMD_SHOW_PENDING
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get pending firmware updates: {}".format(str(e)))
|
||||||
|
|
||||||
|
no_pending_updates = False
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(self.ONIE_NO_PENDING_UPDATES_ATTR):
|
||||||
|
no_pending_updates = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not no_pending_updates:
|
||||||
|
raise RuntimeError("Failed to complete firmware update: pending updates are present")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__stage_update(image_path)
|
||||||
|
self.__trigger_update()
|
||||||
|
except:
|
||||||
|
if self.__is_update_staged(image_path):
|
||||||
|
self.__unstage_update(image_path)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def is_non_onie_firmware_update_supported(self):
|
||||||
|
current_version = self.get_onie_version()
|
||||||
|
_, _, major1, minor1, release1, _ = self.parse_onie_version(current_version)
|
||||||
|
version1 = int("{}{}{}".format(major1, minor1, release1))
|
||||||
|
|
||||||
|
required_version = self.get_onie_required_version()
|
||||||
|
_, _, major2, minor2, release2, _ = self.parse_onie_version(required_version, True)
|
||||||
|
version2 = int("{}{}{}".format(major2, minor2, release2))
|
||||||
|
|
||||||
|
return version1 >= version2
|
||||||
|
|
||||||
|
|
||||||
class Component(ComponentBase):
|
class Component(ComponentBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -26,27 +315,12 @@ class Component(ComponentBase):
|
|||||||
self.description = None
|
self.description = None
|
||||||
self.image_ext_name = None
|
self.image_ext_name = None
|
||||||
|
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""
|
|
||||||
Retrieves the name of the component
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A string containing the name of the component
|
|
||||||
"""
|
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
def get_description(self):
|
def get_description(self):
|
||||||
"""
|
|
||||||
Retrieves the description of the component
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A string containing the description of the component
|
|
||||||
"""
|
|
||||||
return self.description
|
return self.description
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _read_generic_file(filename, len, ignore_errors=False):
|
def _read_generic_file(filename, len, ignore_errors=False):
|
||||||
"""
|
"""
|
||||||
@ -63,7 +337,6 @@ class Component(ComponentBase):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_command_result(cmdline):
|
def _get_command_result(cmdline):
|
||||||
try:
|
try:
|
||||||
@ -79,149 +352,246 @@ class Component(ComponentBase):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _check_file_validity(self, image_path):
|
def _check_file_validity(self, image_path):
|
||||||
# check whether the image file exists
|
|
||||||
if not os.path.isfile(image_path):
|
if not os.path.isfile(image_path):
|
||||||
print("ERROR: 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
|
return False
|
||||||
|
|
||||||
|
name_list = os.path.splitext(image_path)
|
||||||
if self.image_ext_name is not None:
|
if self.image_ext_name is not None:
|
||||||
name_list = os.path.splitext(image_path)
|
|
||||||
if name_list[1] != self.image_ext_name:
|
if name_list[1] != 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))
|
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 False
|
||||||
|
else:
|
||||||
|
if name_list[1]:
|
||||||
|
print("ERROR: Extend name of file {} is wrong. Image for {} shouldn't have extension".format(image_path, self.name))
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentONIE(Component):
|
||||||
|
COMPONENT_NAME = 'ONIE'
|
||||||
|
COMPONENT_DESCRIPTION = 'ONIE - Open Network Install Environment'
|
||||||
|
|
||||||
|
ONIE_IMAGE_VERSION_ATTR = 'image_version'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ComponentONIE, self).__init__()
|
||||||
|
|
||||||
|
self.name = self.COMPONENT_NAME
|
||||||
|
self.description = self.COMPONENT_DESCRIPTION
|
||||||
|
self.onie_updater = ONIEUpdater()
|
||||||
|
|
||||||
|
def __install_firmware(self, image_path):
|
||||||
|
if not self._check_file_validity(image_path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("INFO: Staging {} firmware update with ONIE updater".format(self.name))
|
||||||
|
self.onie_updater.update_firmware(image_path)
|
||||||
|
except Exception as e:
|
||||||
|
print("ERROR: Failed to update {} firmware: {}".format(self.name, str(e)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_firmware_version(self):
|
||||||
|
return self.onie_updater.get_onie_version()
|
||||||
|
|
||||||
|
def get_available_firmware_version(self, image_path):
|
||||||
|
firmware_info = self.onie_updater.get_onie_firmware_info(image_path)
|
||||||
|
if self.ONIE_IMAGE_VERSION_ATTR not in firmware_info:
|
||||||
|
raise RuntimeError("Failed to get {} available firmware version".format(self.name))
|
||||||
|
|
||||||
|
return firmware_info[self.ONIE_IMAGE_VERSION_ATTR]
|
||||||
|
|
||||||
|
def get_firmware_update_notification(self, image_path):
|
||||||
|
return "Immediate cold reboot is required to complete {} firmware update".format(self.name)
|
||||||
|
|
||||||
|
def install_firmware(self, image_path):
|
||||||
|
return self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
def update_firmware(self, image_path):
|
||||||
|
self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentSSD(Component):
|
||||||
|
COMPONENT_NAME = 'SSD'
|
||||||
|
COMPONENT_DESCRIPTION = 'SSD - Solid-State Drive'
|
||||||
|
COMPONENT_FIRMWARE_EXTENSION = '.pkg'
|
||||||
|
|
||||||
|
FIRMWARE_VERSION_ATTR = 'Firmware Version'
|
||||||
|
AVAILABLE_FIRMWARE_VERSION_ATTR = 'Available Firmware Version'
|
||||||
|
POWER_CYCLE_REQUIRED_ATTR = 'Power Cycle Required'
|
||||||
|
UPGRADE_REQUIRED_ATTR = 'Upgrade Required'
|
||||||
|
|
||||||
|
SSD_INFO_COMMAND = "/usr/bin/mlnx-ssd-fw-update.sh -q"
|
||||||
|
SSD_FIRMWARE_INFO_COMMAND = "/usr/bin/mlnx-ssd-fw-update.sh -q -i {}"
|
||||||
|
SSD_FIRMWARE_UPDATE_COMMAND = "/usr/bin/mlnx-ssd-fw-update.sh -y -u -i {}"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ComponentSSD, self).__init__()
|
||||||
|
|
||||||
|
self.name = self.COMPONENT_NAME
|
||||||
|
self.description = self.COMPONENT_DESCRIPTION
|
||||||
|
self.image_ext_name = self.COMPONENT_FIRMWARE_EXTENSION
|
||||||
|
|
||||||
|
def __install_firmware(self, image_path):
|
||||||
|
if not self._check_file_validity(image_path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
cmd = self.SSD_FIRMWARE_UPDATE_COMMAND.format(image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("INFO: Installing {} firmware update".format(self.name))
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print("ERROR: Failed to update {} firmware: {}".format(self.name, str(e)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_firmware_version(self):
|
||||||
|
cmd = self.SSD_INFO_COMMAND
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get {} info: {}".format(self.name, str(e)))
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(self.FIRMWARE_VERSION_ATTR):
|
||||||
|
return line.split(':')[1].lstrip(' \t')
|
||||||
|
|
||||||
|
raise RuntimeError("Failed to parse {} version".format(self.name))
|
||||||
|
|
||||||
|
def get_available_firmware_version(self, image_path):
|
||||||
|
cmd = self.SSD_FIRMWARE_INFO_COMMAND.format(image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get {} firmware info: {}".format(self.name, str(e)))
|
||||||
|
|
||||||
|
current_firmware_version = None
|
||||||
|
available_firmware_version = None
|
||||||
|
upgrade_required = None
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(self.FIRMWARE_VERSION_ATTR):
|
||||||
|
current_firmware_version = line.split(':')[1].lstrip(' \t')
|
||||||
|
if line.startswith(self.AVAILABLE_FIRMWARE_VERSION_ATTR):
|
||||||
|
available_firmware_version = line.split(':')[1].lstrip(' \t')
|
||||||
|
if line.startswith(self.UPGRADE_REQUIRED_ATTR):
|
||||||
|
upgrade_required = line.split(':')[1].lstrip(' \t')
|
||||||
|
|
||||||
|
if upgrade_required is None or upgrade_required not in ['yes', 'no']:
|
||||||
|
raise RuntimeError("Failed to parse {} firmware upgrade status".format(self.name))
|
||||||
|
|
||||||
|
if upgrade_required == 'no':
|
||||||
|
if current_firmware_version is None:
|
||||||
|
raise RuntimeError("Failed to parse {} current firmware version".format(self.name))
|
||||||
|
return current_firmware_version
|
||||||
|
|
||||||
|
if available_firmware_version is None:
|
||||||
|
raise RuntimeError("Failed to parse {} available firmware version".format(self.name))
|
||||||
|
|
||||||
|
return available_firmware_version
|
||||||
|
|
||||||
|
def get_firmware_update_notification(self, image_path):
|
||||||
|
cmd = self.SSD_FIRMWARE_INFO_COMMAND.format(image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get {} firmware info: {}".format(self.name, str(e)))
|
||||||
|
|
||||||
|
power_cycle_required = None
|
||||||
|
upgrade_required = None
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith(self.POWER_CYCLE_REQUIRED_ATTR):
|
||||||
|
power_cycle_required = line.split(':')[1].lstrip(' \t')
|
||||||
|
if line.startswith(self.UPGRADE_REQUIRED_ATTR):
|
||||||
|
upgrade_required = line.split(':')[1].lstrip(' \t')
|
||||||
|
|
||||||
|
if upgrade_required is None or upgrade_required not in ['yes', 'no']:
|
||||||
|
raise RuntimeError("Failed to parse {} firmware upgrade status".format(self.name))
|
||||||
|
|
||||||
|
if upgrade_required == 'no':
|
||||||
|
return None
|
||||||
|
|
||||||
|
if power_cycle_required is None or power_cycle_required not in ['yes', 'no']:
|
||||||
|
raise RuntimeError("Failed to parse {} firmware power policy".format(self.name))
|
||||||
|
|
||||||
|
notification = None
|
||||||
|
|
||||||
|
if power_cycle_required == 'yes':
|
||||||
|
notification = "Immediate power cycle is required to complete {} firmware update".format(self.name)
|
||||||
|
|
||||||
|
return notification
|
||||||
|
|
||||||
|
def install_firmware(self, image_path):
|
||||||
|
return self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
def update_firmware(self, image_path):
|
||||||
|
self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
|
||||||
class ComponentBIOS(Component):
|
class ComponentBIOS(Component):
|
||||||
COMPONENT_NAME = 'BIOS'
|
COMPONENT_NAME = 'BIOS'
|
||||||
COMPONENT_DESCRIPTION = 'BIOS - Basic Input/Output System'
|
COMPONENT_DESCRIPTION = 'BIOS - Basic Input/Output System'
|
||||||
COMPONENT_FIRMWARE_EXTENSION = '.rom'
|
COMPONENT_FIRMWARE_EXTENSION = '.rom'
|
||||||
|
|
||||||
# To update BIOS requires the ONIE with version 5.2.0016 or upper
|
BIOS_VERSION_COMMAND = 'dmidecode --oem-string 1'
|
||||||
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'
|
|
||||||
|
|
||||||
ONIE_FW_UPDATE_CMD_ADD = '/usr/bin/mlnx-onie-fw-update.sh add {}'
|
|
||||||
ONIE_FW_UPDATE_CMD_REMOVE = '/usr/bin/mlnx-onie-fw-update.sh remove {}'
|
|
||||||
ONIE_FW_UPDATE_CMD_UPDATE = '/usr/bin/mlnx-onie-fw-update.sh update'
|
|
||||||
ONIE_FW_UPDATE_CMD_SHOW = '/usr/bin/mlnx-onie-fw-update.sh show-pending'
|
|
||||||
|
|
||||||
BIOS_QUERY_VERSION_COMMAND = 'dmidecode -t 11'
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super(ComponentBIOS, self).__init__()
|
||||||
|
|
||||||
self.name = self.COMPONENT_NAME
|
self.name = self.COMPONENT_NAME
|
||||||
self.description = self.COMPONENT_DESCRIPTION
|
self.description = self.COMPONENT_DESCRIPTION
|
||||||
self.image_ext_name = self.COMPONENT_FIRMWARE_EXTENSION
|
self.image_ext_name = self.COMPONENT_FIRMWARE_EXTENSION
|
||||||
|
self.onie_updater = ONIEUpdater()
|
||||||
|
|
||||||
|
def __install_firmware(self, image_path):
|
||||||
def get_firmware_version(self):
|
if not self.onie_updater.is_non_onie_firmware_update_supported():
|
||||||
"""
|
print("ERROR: ONIE {} or later is required".format(self.onie_updater.get_onie_required_version()))
|
||||||
Retrieves the firmware version of the component
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A string containing the firmware version of the component
|
|
||||||
|
|
||||||
BIOS version is retrieved via command 'dmidecode -t 11'
|
|
||||||
which should return result in the following convention
|
|
||||||
# dmidecode 3.0
|
|
||||||
Getting SMBIOS data from sysfs.
|
|
||||||
SMBIOS 2.7 present.
|
|
||||||
|
|
||||||
Handle 0x0022, DMI type 11, 5 bytes
|
|
||||||
OEM Strings
|
|
||||||
String 1:*0ABZS017_02.02.002*
|
|
||||||
String 2: To Be Filled By O.E.M.
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
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, RuntimeError) as e:
|
|
||||||
raise RuntimeError("Failed to parse BIOS version due to {}".format(repr(e)))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _check_onie_version(self):
|
|
||||||
# check ONIE version. To update ONIE requires version 5.2.0016 or later.
|
|
||||||
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("ERROR: Failed to parse ONIE version by {} from {} due to {}".format(
|
|
||||||
self.ONIE_VERSION_PARSE_PATTERN, machine_conf, repr(e)))
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if onie_major < self.ONIE_REQUIRED_MAJOR or onie_minor < self.ONIE_REQUIRED_MINOR or onie_release < 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
|
|
||||||
|
|
||||||
|
|
||||||
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 requirement
|
|
||||||
if not self._check_onie_version():
|
|
||||||
return False
|
|
||||||
|
|
||||||
# check whether the file exists
|
|
||||||
if not self._check_file_validity(image_path):
|
if not self._check_file_validity(image_path):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# do the real work
|
|
||||||
try:
|
try:
|
||||||
# check whether there has already been some images pending
|
print("INFO: Staging {} firmware update with ONIE updater".format(self.name))
|
||||||
# if yes, remove them
|
self.onie_updater.update_firmware(image_path)
|
||||||
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(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.check_call(self.ONIE_FW_UPDATE_CMD_ADD.format(image_path).split())
|
|
||||||
if result:
|
|
||||||
return False
|
|
||||||
result = subprocess.check_call(self.ONIE_FW_UPDATE_CMD_UPDATE.split())
|
|
||||||
if result:
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("ERROR: Installing BIOS failed due to {}".format(repr(e)))
|
print("ERROR: Failed to update {} firmware: {}".format(self.name, str(e)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("INFO: Reboot is required to finish BIOS installation")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_firmware_version(self):
|
||||||
|
cmd = self.BIOS_VERSION_COMMAND
|
||||||
|
|
||||||
|
try:
|
||||||
|
version = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).rstrip('\n')
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise RuntimeError("Failed to get {} version: {}".format(self.name, str(e)))
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
def get_available_firmware_version(self, image_path):
|
||||||
|
raise NotImplementedError("{} component doesn't support firmware version query".format(self.name))
|
||||||
|
|
||||||
|
def get_firmware_update_notification(self, image_path):
|
||||||
|
return "Immediate cold reboot is required to complete {} firmware update".format(self.name)
|
||||||
|
|
||||||
|
def install_firmware(self, image_path):
|
||||||
|
return self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
def update_firmware(self, image_path):
|
||||||
|
self.__install_firmware(image_path)
|
||||||
|
|
||||||
|
|
||||||
class ComponentCPLD(Component):
|
class ComponentCPLD(Component):
|
||||||
@ -229,6 +599,9 @@ class ComponentCPLD(Component):
|
|||||||
COMPONENT_DESCRIPTION = 'CPLD - Complex Programmable Logic Device'
|
COMPONENT_DESCRIPTION = 'CPLD - Complex Programmable Logic Device'
|
||||||
COMPONENT_FIRMWARE_EXTENSION = '.vme'
|
COMPONENT_FIRMWARE_EXTENSION = '.vme'
|
||||||
|
|
||||||
|
MST_DEVICE_PATH = '/dev/mst'
|
||||||
|
MST_DEVICE_PATTERN = 'mt[0-9]*_pci_cr0'
|
||||||
|
|
||||||
CPLD_NUMBER_FILE = '/var/run/hw-management/config/cpld_num'
|
CPLD_NUMBER_FILE = '/var/run/hw-management/config/cpld_num'
|
||||||
CPLD_PART_NUMBER_FILE = '/var/run/hw-management/system/cpld{}_pn'
|
CPLD_PART_NUMBER_FILE = '/var/run/hw-management/system/cpld{}_pn'
|
||||||
CPLD_VERSION_FILE = '/var/run/hw-management/system/cpld{}_version'
|
CPLD_VERSION_FILE = '/var/run/hw-management/system/cpld{}_version'
|
||||||
@ -239,28 +612,54 @@ class ComponentCPLD(Component):
|
|||||||
CPLD_VERSION_MAX_LENGTH = 2
|
CPLD_VERSION_MAX_LENGTH = 2
|
||||||
CPLD_VERSION_MINOR_MAX_LENGTH = 2
|
CPLD_VERSION_MINOR_MAX_LENGTH = 2
|
||||||
|
|
||||||
CPLD_PART_NUMBER_DEFAULT = ZERO
|
CPLD_PART_NUMBER_DEFAULT = '0'
|
||||||
CPLD_VERSION_MINOR_DEFAULT = ZERO
|
CPLD_VERSION_MINOR_DEFAULT = '0'
|
||||||
|
|
||||||
CPLD_UPDATE_COMMAND = 'cpldupdate --dev {} --print-progress {}'
|
CPLD_FIRMWARE_UPDATE_COMMAND = 'cpldupdate --dev {} --print-progress {}'
|
||||||
|
|
||||||
MST_DEVICE_PATTERN = '/dev/mst/mt[0-9]*_pci_cr0'
|
|
||||||
|
|
||||||
def __init__(self, idx):
|
def __init__(self, idx):
|
||||||
|
super(ComponentCPLD, self).__init__()
|
||||||
|
|
||||||
self.idx = idx
|
self.idx = idx
|
||||||
self.name = self.COMPONENT_NAME.format(self.idx)
|
self.name = self.COMPONENT_NAME.format(self.idx)
|
||||||
self.description = self.COMPONENT_DESCRIPTION
|
self.description = self.COMPONENT_DESCRIPTION
|
||||||
self.image_ext_name = self.COMPONENT_FIRMWARE_EXTENSION
|
self.image_ext_name = self.COMPONENT_FIRMWARE_EXTENSION
|
||||||
|
|
||||||
|
def __get_mst_device(self):
|
||||||
|
if not os.path.exists(self.MST_DEVICE_PATH):
|
||||||
|
print("ERROR: mst driver is not loaded")
|
||||||
|
return None
|
||||||
|
|
||||||
|
pattern = os.path.join(self.MST_DEVICE_PATH, self.MST_DEVICE_PATTERN)
|
||||||
|
|
||||||
|
mst_dev_list = glob.glob(pattern)
|
||||||
|
if not mst_dev_list or len(mst_dev_list) != 1:
|
||||||
|
devices = str(os.listdir(self.MST_DEVICE_PATH))
|
||||||
|
print("ERROR: Failed to get mst device: pattern={}, devices={}".format(pattern, devices))
|
||||||
|
return None
|
||||||
|
|
||||||
|
return mst_dev_list[0]
|
||||||
|
|
||||||
|
def __install_firmware(self, image_path):
|
||||||
|
if not self._check_file_validity(image_path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
mst_dev = self.__get_mst_device()
|
||||||
|
if mst_dev is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
cmd = self.CPLD_FIRMWARE_UPDATE_COMMAND.format(mst_dev, image_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("INFO: Installing {} firmware update: path={}".format(self.name, image_path))
|
||||||
|
subprocess.check_call(cmd.split())
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print("ERROR: Failed to update {} firmware: {}".format(self.name, str(e)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def get_firmware_version(self):
|
def get_firmware_version(self):
|
||||||
"""
|
|
||||||
Retrieves the firmware version of the component
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A string containing the firmware version of the component
|
|
||||||
"""
|
|
||||||
|
|
||||||
part_number_file = self.CPLD_PART_NUMBER_FILE.format(self.idx)
|
part_number_file = self.CPLD_PART_NUMBER_FILE.format(self.idx)
|
||||||
version_file = self.CPLD_VERSION_FILE.format(self.idx)
|
version_file = self.CPLD_VERSION_FILE.format(self.idx)
|
||||||
version_minor_file = self.CPLD_VERSION_MINOR_FILE.format(self.idx)
|
version_minor_file = self.CPLD_VERSION_MINOR_FILE.format(self.idx)
|
||||||
@ -275,75 +674,52 @@ class ComponentCPLD(Component):
|
|||||||
if version_minor is None:
|
if version_minor is None:
|
||||||
version_minor = self.CPLD_VERSION_MINOR_DEFAULT
|
version_minor = self.CPLD_VERSION_MINOR_DEFAULT
|
||||||
|
|
||||||
part_number = part_number.rstrip(NEWLINE).zfill(self.CPLD_PART_NUMBER_MAX_LENGTH)
|
part_number = part_number.rstrip('\n').zfill(self.CPLD_PART_NUMBER_MAX_LENGTH)
|
||||||
version = version.rstrip(NEWLINE).zfill(self.CPLD_VERSION_MAX_LENGTH)
|
version = version.rstrip('\n').zfill(self.CPLD_VERSION_MAX_LENGTH)
|
||||||
version_minor = version_minor.rstrip(NEWLINE).zfill(self.CPLD_VERSION_MINOR_MAX_LENGTH)
|
version_minor = version_minor.rstrip('\n').zfill(self.CPLD_VERSION_MINOR_MAX_LENGTH)
|
||||||
|
|
||||||
return "CPLD{}_REV{}{}".format(part_number, version, version_minor)
|
return "CPLD{}_REV{}{}".format(part_number, version, version_minor)
|
||||||
|
|
||||||
|
def get_available_firmware_version(self, image_path):
|
||||||
|
with MPFAManager(image_path) as mpfa:
|
||||||
|
if not mpfa.get_metadata().has_option('version', self.name):
|
||||||
|
raise RuntimeError("Failed to get {} available firmware version".format(self.name))
|
||||||
|
|
||||||
def _get_mst_device(self):
|
return mpfa.get_metadata().get('version', self.name)
|
||||||
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
|
|
||||||
|
|
||||||
|
def get_firmware_update_notification(self, image_path):
|
||||||
|
name, ext = os.path.splitext(os.path.basename(image_path))
|
||||||
|
if ext == self.COMPONENT_FIRMWARE_EXTENSION:
|
||||||
|
return "Power cycle (with 30 sec delay) or refresh image is required to complete {} firmware update".format(self.name)
|
||||||
|
|
||||||
|
return "Immediate power cycle is required to complete {} firmware update".format(self.name)
|
||||||
|
|
||||||
def install_firmware(self, image_path):
|
def install_firmware(self, image_path):
|
||||||
"""
|
return self.__install_firmware(image_path)
|
||||||
Installs firmware to the component
|
|
||||||
|
|
||||||
Args:
|
def update_firmware(self, image_path):
|
||||||
image_path: A string, path to firmware image
|
with MPFAManager(image_path) as mpfa:
|
||||||
|
if not mpfa.get_metadata().has_option('firmware', 'burn'):
|
||||||
|
raise RuntimeError("Failed to get {} burn firmware".format(self.name))
|
||||||
|
if not mpfa.get_metadata().has_option('firmware', 'refresh'):
|
||||||
|
raise RuntimeError("Failed to get {} refresh firmware".format(self.name))
|
||||||
|
|
||||||
Returns:
|
burn_firmware = mpfa.get_metadata().get('firmware', 'burn')
|
||||||
A boolean, True if install was successful, False if not
|
refresh_firmware = mpfa.get_metadata().get('firmware', 'refresh')
|
||||||
|
|
||||||
Details:
|
print("INFO: Processing {} burn file: firmware install".format(self.name))
|
||||||
The command "cpldupdate" is provided to install CPLD. There are two ways to do it:
|
if not self.__install_firmware(os.path.join(mpfa.get_path(), burn_firmware)):
|
||||||
1. To burn CPLD via gpio, which is faster but only supported on new systems, like SN3700, ...
|
return
|
||||||
2. To install CPLD via firmware, which is slower but supported on older systems.
|
|
||||||
This also requires the mst device designated.
|
|
||||||
"cpldupdate --dev <devname> <vme_file>" 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 <devname> <vme_file>"
|
|
||||||
4. Check the result
|
|
||||||
"""
|
|
||||||
# check whether the image file exists
|
|
||||||
if not self._check_file_validity(image_path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
mst_dev_list = self._get_mst_device()
|
|
||||||
if mst_dev_list is None:
|
|
||||||
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)
|
|
||||||
success_flag = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
subprocess.check_call(cmdline, stderr=subprocess.STDOUT, shell=True)
|
|
||||||
success_flag = True
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print("ERROR: Failed to upgrade CPLD: rc={}".format(e.returncode))
|
|
||||||
|
|
||||||
if success_flag:
|
|
||||||
print("INFO: Refresh or power cycle is required to finish CPLD installation")
|
|
||||||
|
|
||||||
return success_flag
|
|
||||||
|
|
||||||
|
print("INFO: Processing {} refresh file: firmware update".format(self.name))
|
||||||
|
self.__install_firmware(os.path.join(mpfa.get_path(), refresh_firmware))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_component_list(cls):
|
def get_component_list(cls):
|
||||||
component_list = [ ]
|
component_list = [ ]
|
||||||
|
|
||||||
cpld_number = cls._read_generic_file(cls.CPLD_NUMBER_FILE, cls.CPLD_NUMBER_MAX_LENGTH)
|
cpld_number = cls._read_generic_file(cls.CPLD_NUMBER_FILE, cls.CPLD_NUMBER_MAX_LENGTH)
|
||||||
cpld_number = cpld_number.rstrip(NEWLINE)
|
cpld_number = cpld_number.rstrip('\n')
|
||||||
|
|
||||||
for cpld_idx in xrange(1, int(cpld_number) + 1):
|
for cpld_idx in xrange(1, int(cpld_number) + 1):
|
||||||
component_list.append(cls(cpld_idx))
|
component_list.append(cls(cpld_idx))
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
#= Global variable #
|
#= Global variable #
|
||||||
#=
|
#=
|
||||||
#=====
|
#=====
|
||||||
VERSION="1.3"
|
VERSION="1.5"
|
||||||
#=====
|
#=====
|
||||||
SWITCH_SSD_DEV="/dev/sda"
|
SWITCH_SSD_DEV="/dev/sda"
|
||||||
UTIL_TITLE="This is MLNX SSD firmware update utility to read and write SSD FW. Version ${VERSION}"
|
UTIL_TITLE="This is MLNX SSD firmware update utility to read and write SSD FW. Version ${VERSION}"
|
||||||
@ -79,6 +79,8 @@ function init_script() {
|
|||||||
else
|
else
|
||||||
LOGGER_UTIL=$FALSE
|
LOGGER_UTIL=$FALSE
|
||||||
fi
|
fi
|
||||||
|
export LC_ALL=
|
||||||
|
export LANG="en_US.UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
#==============================================================================#
|
#==============================================================================#
|
||||||
@ -415,7 +417,7 @@ function check_package_signing() {
|
|||||||
LOG_MSG "checksum_signed_file: ${checksum_signed_file}" ${DEBUG_MSG}
|
LOG_MSG "checksum_signed_file: ${checksum_signed_file}" ${DEBUG_MSG}
|
||||||
LOG_MSG "checksum_unsigned_file: ${checksum_unsigned_file}" ${DEBUG_MSG}
|
LOG_MSG "checksum_unsigned_file: ${checksum_unsigned_file}" ${DEBUG_MSG}
|
||||||
|
|
||||||
gpg --keyring "$public_cert_file" --verify "$checksum_signed_file" "$checksum_unsigned_file" > /dev/null 2>&1
|
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."
|
[ $? -ne 0 ] && LOG_MSG_AND_EXIT "Error: fault package signing."
|
||||||
|
|
||||||
LOG_MSG "cd into: ${package_path}" ${DEBUG_MSG}
|
LOG_MSG "cd into: ${package_path}" ${DEBUG_MSG}
|
||||||
@ -559,7 +561,7 @@ function print_ssd_info() {
|
|||||||
LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL"
|
LOG_MSG "Device Model\t : $SSD_DEVICE_MODEL"
|
||||||
LOG_MSG "Serial Number\t : $SSD_SERIAL"
|
LOG_MSG "Serial Number\t : $SSD_SERIAL"
|
||||||
LOG_MSG "User Capacity\t : $SSD_SIZE GB"
|
LOG_MSG "User Capacity\t : $SSD_SIZE GB"
|
||||||
LOG_MSG "Firmware Version : $SSD_FW_VER"
|
LOG_MSG "Firmware Version : $SSD_FW_VER"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,7 +697,7 @@ elif [ $ARG_UPDATE_FLAG == $TRUE ]; then
|
|||||||
else
|
else
|
||||||
LOG_MSG "SSD FW update completed successfully."
|
LOG_MSG "SSD FW update completed successfully."
|
||||||
|
|
||||||
if [ $ARG_POWER_CYCLE_FLAG == $TRUE ]; then
|
if [[ "yes" == "$power_policy" || $ARG_POWER_CYCLE_FLAG == $TRUE ]]; then
|
||||||
LOG_MSG "Execute power cycle..."
|
LOG_MSG "Execute power cycle..."
|
||||||
sleep 1
|
sleep 1
|
||||||
sync
|
sync
|
||||||
@ -715,7 +717,7 @@ elif [ $ARG_UPDATE_FLAG == $TRUE ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ $UPDATE_DONE == $FALSE ]; then
|
if [ $UPDATE_DONE == $FALSE ]; then
|
||||||
LOG_MSG "SSD FW upgrade not require, based on given package latest version is in used."
|
LOG_MSG "SSD FW upgrade is not required, latest version based on given package is in use."
|
||||||
print_ssd_info "no"
|
print_ssd_info "no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user