DellEMC: Z9332f - Component firmware upgrade platform API implementation (#8973)
This commit is contained in:
parent
517d81a57a
commit
4139e06260
@ -19,6 +19,12 @@
|
||||
},
|
||||
{
|
||||
"name": "Switch CPLD 2"
|
||||
},
|
||||
{
|
||||
"name": "SSD"
|
||||
},
|
||||
{
|
||||
"name": "PCIe"
|
||||
}
|
||||
],
|
||||
"fans": [
|
||||
|
70
platform/broadcom/sonic-platform-modules-dell/common/onie_stage_fwpkg
Executable file
70
platform/broadcom/sonic-platform-modules-dell/common/onie_stage_fwpkg
Executable file
@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
ONIE_PATH="/mnt/onie-boot"
|
||||
ONIE_PENDING_DIR="${ONIE_PATH}/onie/update/pending"
|
||||
|
||||
unset FWPKG
|
||||
|
||||
function stage_fwpkg()
|
||||
{
|
||||
local name=$(basename ${FWPKG})
|
||||
local pending="${ONIE_PENDING_DIR}/$name"
|
||||
|
||||
# Exit if not superuser
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
echo "ERROR: This command must be run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mount ONIE partition if not already mounted
|
||||
if ! grep -qs ${ONIE_PATH} /proc/mounts; then
|
||||
mkdir -p ${ONIE_PATH}
|
||||
mount LABEL=ONIE-BOOT ${ONIE_PATH} || {
|
||||
echo "ERROR: Failed to mount ONIE partition"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
[ -f "$pending" ] && {
|
||||
echo "INFO: Firmware update package ${name} is already staged"
|
||||
exit 2
|
||||
}
|
||||
|
||||
${ONIE_PATH}/onie/tools/bin/onie-fwpkg add ${FWPKG} || {
|
||||
echo "ERROR: onie-fwpkg add for ${name} failed"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT=$0
|
||||
|
||||
function show_help_and_exit()
|
||||
{
|
||||
echo "Usage ${SCRIPT} [options]"
|
||||
echo " This script will stage ONIE firmware update package."
|
||||
echo " "
|
||||
echo " Available options:"
|
||||
echo " -h, -? : getting this help"
|
||||
echo " -o [fwpkg] : stages the firmware update package"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
function parse_options()
|
||||
{
|
||||
while getopts ":h?a:" opt; do
|
||||
case $opt in
|
||||
a )
|
||||
FWPKG=$(realpath $OPTARG)
|
||||
stage_fwpkg
|
||||
;;
|
||||
h|\? )
|
||||
show_help_and_exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
parse_options $@
|
||||
exit 0
|
@ -9,3 +9,4 @@ common/platform_reboot usr/share/sonic/device/x86_64-dellemc_z9332f_d1508-r0
|
||||
common/pcisysfs.py usr/bin
|
||||
common/fw-updater usr/local/bin
|
||||
common/onie_mode_set usr/local/bin
|
||||
common/onie_stage_fwpkg usr/local/bin
|
||||
|
@ -28,7 +28,7 @@ MAX_Z9332F_FANTRAY = 7
|
||||
MAX_Z9332F_FAN = 2
|
||||
MAX_Z9332F_PSU = 2
|
||||
MAX_Z9332F_THERMAL = 14
|
||||
MAX_Z9332F_COMPONENT = 6 # BIOS,FPGA,BMC,BB CPLD and 2 Switch CPLDs
|
||||
MAX_Z9332F_COMPONENT = 8 # BIOS,FPGA,BMC,BB CPLD,2 Switch CPLDs,SSD and PCIe
|
||||
|
||||
media_part_num_list = set([ \
|
||||
"8T47V","XTY28","MHVPK","GF76J","J6FGD","F1KMV","9DN5J","H4DHD","6MCNV","0WRX0","X7F70","5R2PT","WTRD1","WTRD1","WTRD1","WTRD1","5250G","WTRD1","C5RNH","C5RNH","FTLX8571D3BCL-FC",
|
||||
|
@ -10,10 +10,14 @@
|
||||
########################################################################
|
||||
|
||||
try:
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
from sonic_platform_base.component_base import ComponentBase
|
||||
import sonic_platform.hwaccess as hwaccess
|
||||
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
@ -24,8 +28,8 @@ def get_bios_version():
|
||||
|
||||
def get_fpga_version():
|
||||
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:09:00.0/resource0', 0)
|
||||
return '{}.{}'.format((val >> 8) & 0xff, val & 0xff)
|
||||
|
||||
return '{}.{}'.format((val >> 16) & 0xffff, val & 0xffff)
|
||||
|
||||
def get_bmc_version():
|
||||
return subprocess.check_output(
|
||||
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
|
||||
@ -43,6 +47,31 @@ def get_cpld1_version():
|
||||
def get_cpld2_version():
|
||||
return get_cpld_version(4, 0x31)
|
||||
|
||||
def get_ssd_version():
|
||||
val = 'NA'
|
||||
try:
|
||||
ssd_ver = subprocess.check_output(['ssdutil','-v'], text=True)
|
||||
except Exception:
|
||||
return val
|
||||
else:
|
||||
version = re.search(r'Firmware\s*:(.*)',ssd_ver)
|
||||
if version:
|
||||
val = version.group(1).strip()
|
||||
|
||||
return val
|
||||
|
||||
def get_pciephy_version():
|
||||
val = 'NA'
|
||||
try:
|
||||
pcie_ver = subprocess.check_output('bcmcmd "pciephy fw version"', shell=True, text=True)
|
||||
except Exception:
|
||||
return val
|
||||
else:
|
||||
version = re.search(r'PCIe FW loader version:\s(.*)', pcie_ver)
|
||||
if version:
|
||||
val = version.group(1).strip()
|
||||
|
||||
return val
|
||||
|
||||
|
||||
class Component(ComponentBase):
|
||||
@ -77,17 +106,83 @@ class Component(ComponentBase):
|
||||
['Switch CPLD 2',
|
||||
'Used for managing QSFP-DD/QSFP28/SFP port transceivers',
|
||||
get_cpld2_version
|
||||
]
|
||||
],
|
||||
|
||||
['SSD',
|
||||
'Solid State Drive that stores data persistently',
|
||||
get_ssd_version
|
||||
],
|
||||
|
||||
['PCIe',
|
||||
'ASIC PCIe firmware',
|
||||
get_pciephy_version
|
||||
]
|
||||
]
|
||||
|
||||
def __init__(self, component_index = 0):
|
||||
def __init__(self, component_index=0):
|
||||
ComponentBase.__init__(self)
|
||||
self.index = component_index
|
||||
self.name = self.CHASSIS_COMPONENTS[self.index][0]
|
||||
self.description = self.CHASSIS_COMPONENTS[self.index][1]
|
||||
self.version = self.CHASSIS_COMPONENTS[self.index][2]()
|
||||
|
||||
@staticmethod
|
||||
def _get_available_firmware_version(image_path):
|
||||
if not os.path.isfile(image_path):
|
||||
return False, "ERROR: File not found"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
cmd = "sed -e '1,/^exit_marker$/d' {} | tar -x -C {} installer/onie-update.tar.xz".format(image_path, tmpdir)
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
return False, "ERROR: Unable to extract firmware updater"
|
||||
|
||||
try:
|
||||
updater = tarfile.open(os.path.join(tmpdir, "installer/onie-update.tar.xz"), "r")
|
||||
except tarfile.ReadError:
|
||||
return False, "ERROR: Unable to extract firmware updater"
|
||||
|
||||
try:
|
||||
ver_info_fd = updater.extractfile("firmware/fw-component-version")
|
||||
except KeyError:
|
||||
updater.close()
|
||||
return False, "ERROR: Version info not available"
|
||||
|
||||
ver_info = json.load(ver_info_fd)
|
||||
ver_info_fd.close()
|
||||
updater.close()
|
||||
|
||||
ver_info = ver_info.get("x86_64-dellemc_z9332f_d1508-r0")
|
||||
if ver_info:
|
||||
return True, ver_info
|
||||
else:
|
||||
return False, "ERROR: Version info not available"
|
||||
|
||||
@staticmethod
|
||||
def _stage_firmware_package(image_path):
|
||||
stage_msg = None
|
||||
cmd = "onie_stage_fwpkg -a {}".format(image_path)
|
||||
try:
|
||||
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT, text=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.returncode != 2:
|
||||
return False, e.output.strip()
|
||||
else:
|
||||
stage_msg = e.output.strip()
|
||||
|
||||
cmd = "onie_mode_set -o update"
|
||||
try:
|
||||
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT, text=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
return False, e.output.strip()
|
||||
|
||||
if stage_msg:
|
||||
return True, stage_msg
|
||||
else:
|
||||
return True, "INFO: Firmware upgrade staged"
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the component
|
||||
@ -112,16 +207,6 @@ class Component(ComponentBase):
|
||||
"""
|
||||
return self.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
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the component
|
||||
@ -170,3 +255,171 @@ class Component(ComponentBase):
|
||||
bool: True if it is replaceable.
|
||||
"""
|
||||
return False
|
||||
|
||||
def get_available_firmware_version(self, image_path):
|
||||
"""
|
||||
Retrieves the available firmware version of the component
|
||||
|
||||
Note: the firmware version will be read from image
|
||||
|
||||
Args:
|
||||
image_path: A string, path to firmware image
|
||||
|
||||
Returns:
|
||||
A string containing the available firmware version of the component
|
||||
"""
|
||||
avail_ver = None
|
||||
valid, version = self._get_available_firmware_version(image_path)
|
||||
if valid:
|
||||
avail_ver = version.get(self.name)
|
||||
if avail_ver:
|
||||
avail_ver = avail_ver.get("version")
|
||||
else:
|
||||
print(version)
|
||||
|
||||
return avail_ver if avail_ver else "NA"
|
||||
|
||||
def get_firmware_update_notification(self, image_path):
|
||||
"""
|
||||
Retrieves a notification on what should be done in order to complete
|
||||
the component firmware update
|
||||
|
||||
Args:
|
||||
image_path: A string, path to firmware image
|
||||
|
||||
Returns:
|
||||
A string containing the component firmware update notification if required.
|
||||
By default 'None' value will be used, which indicates that no actions are required
|
||||
"""
|
||||
valid, version = self._get_available_firmware_version(image_path)
|
||||
if valid:
|
||||
avail_ver = version.get(self.name)
|
||||
if avail_ver:
|
||||
avail_ver = avail_ver.get("version")
|
||||
if avail_ver and avail_ver != self.get_firmware_version():
|
||||
return "Cold reboot is required to perform firmware upgrade"
|
||||
else:
|
||||
print(version)
|
||||
|
||||
return None
|
||||
|
||||
def install_firmware(self, image_path):
|
||||
"""
|
||||
Installs firmware to the component
|
||||
|
||||
This API performs firmware installation only: this may/may not be the same as firmware update.
|
||||
In case platform component requires some extra steps (apart from calling Low Level Utility)
|
||||
to load the installed firmware (e.g, reboot, power cycle, etc.) - this must be done manually by user
|
||||
|
||||
Note: in case immediate actions are required to complete the component firmware update
|
||||
(e.g., reboot, power cycle, etc.) - will be done automatically by API and no return value provided
|
||||
|
||||
Args:
|
||||
image_path: A string, path to firmware image
|
||||
|
||||
Returns:
|
||||
A boolean, True if install was successful, False if not
|
||||
"""
|
||||
valid, version = self._get_available_firmware_version(image_path)
|
||||
if valid:
|
||||
avail_ver = version.get(self.name)
|
||||
if avail_ver:
|
||||
avail_ver = avail_ver.get("version")
|
||||
if avail_ver and avail_ver != self.get_firmware_version():
|
||||
status, msg = self._stage_firmware_package(image_path)
|
||||
print(msg)
|
||||
if status:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
print("INFO: Firmware version up-to-date")
|
||||
return True
|
||||
else:
|
||||
print(version)
|
||||
|
||||
return False
|
||||
|
||||
def update_firmware(self, image_path):
|
||||
"""
|
||||
Updates firmware of the component
|
||||
|
||||
This API performs firmware update: it assumes firmware installation and loading in a single call.
|
||||
In case platform component requires some extra steps (apart from calling Low Level Utility)
|
||||
to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically by API
|
||||
|
||||
Args:
|
||||
image_path: A string, path to firmware image
|
||||
|
||||
Raises:
|
||||
RuntimeError: update failed
|
||||
"""
|
||||
valid, version = self._get_available_firmware_version(image_path)
|
||||
if valid:
|
||||
avail_ver = version.get(self.name)
|
||||
if avail_ver:
|
||||
avail_ver = avail_ver.get("version")
|
||||
if avail_ver and avail_ver != self.get_firmware_version():
|
||||
status, msg = self._stage_firmware_package(image_path)
|
||||
if status:
|
||||
print(msg)
|
||||
subprocess.call("reboot")
|
||||
else:
|
||||
raise RuntimeError(msg)
|
||||
|
||||
print("INFO: Firmware version up-to-date")
|
||||
return None
|
||||
else:
|
||||
raise RuntimeError(version)
|
||||
|
||||
def auto_update_firmware(self, image_path, boot_type):
|
||||
"""
|
||||
Updates firmware of the component
|
||||
|
||||
This API performs firmware update automatically based on boot_type: it assumes firmware installation
|
||||
and/or creating a loading task during the reboot, if needed, in a single call.
|
||||
In case platform component requires some extra steps (apart from calling Low Level Utility)
|
||||
to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically during the reboot.
|
||||
The loading task will be created by API.
|
||||
|
||||
Args:
|
||||
image_path: A string, path to firmware image
|
||||
boot_type: A string, reboot type following the upgrade
|
||||
- none/fast/warm/cold
|
||||
|
||||
Returns:
|
||||
Output: A return code
|
||||
return_code: An integer number, status of component firmware auto-update
|
||||
- return code of a positive number indicates successful auto-update
|
||||
- status_installed = 1
|
||||
- status_updated = 2
|
||||
- status_scheduled = 3
|
||||
- return_code of a negative number indicates failed auto-update
|
||||
- status_err_boot_type = -1
|
||||
- status_err_image = -2
|
||||
- status_err_unknown = -3
|
||||
|
||||
Raises:
|
||||
RuntimeError: auto-update failure cause
|
||||
"""
|
||||
valid, version = self._get_available_firmware_version(image_path)
|
||||
if valid:
|
||||
avail_ver = version.get(self.name)
|
||||
if avail_ver:
|
||||
avail_ver = avail_ver.get("version")
|
||||
if avail_ver and avail_ver != self.get_firmware_version():
|
||||
if boot_type != "cold":
|
||||
return -1
|
||||
|
||||
status, msg = self._stage_firmware_package(image_path)
|
||||
if status:
|
||||
print(msg)
|
||||
return 3
|
||||
else:
|
||||
raise RuntimeError(msg)
|
||||
|
||||
print("INFO: Firmware version up-to-date")
|
||||
return 1
|
||||
else:
|
||||
print(version)
|
||||
return -2
|
||||
|
Loading…
Reference in New Issue
Block a user