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": "Switch CPLD 2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SSD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PCIe"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fans": [
|
"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/pcisysfs.py usr/bin
|
||||||
common/fw-updater usr/local/bin
|
common/fw-updater usr/local/bin
|
||||||
common/onie_mode_set 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_FAN = 2
|
||||||
MAX_Z9332F_PSU = 2
|
MAX_Z9332F_PSU = 2
|
||||||
MAX_Z9332F_THERMAL = 14
|
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([ \
|
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",
|
"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:
|
try:
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
from sonic_platform_base.component_base import ComponentBase
|
from sonic_platform_base.component_base import ComponentBase
|
||||||
import sonic_platform.hwaccess as hwaccess
|
import sonic_platform.hwaccess as hwaccess
|
||||||
|
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
@ -24,8 +28,8 @@ def get_bios_version():
|
|||||||
|
|
||||||
def get_fpga_version():
|
def get_fpga_version():
|
||||||
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:09:00.0/resource0', 0)
|
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():
|
def get_bmc_version():
|
||||||
return subprocess.check_output(
|
return subprocess.check_output(
|
||||||
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
|
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
|
||||||
@ -43,6 +47,31 @@ def get_cpld1_version():
|
|||||||
def get_cpld2_version():
|
def get_cpld2_version():
|
||||||
return get_cpld_version(4, 0x31)
|
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):
|
class Component(ComponentBase):
|
||||||
@ -77,17 +106,83 @@ class Component(ComponentBase):
|
|||||||
['Switch CPLD 2',
|
['Switch CPLD 2',
|
||||||
'Used for managing QSFP-DD/QSFP28/SFP port transceivers',
|
'Used for managing QSFP-DD/QSFP28/SFP port transceivers',
|
||||||
get_cpld2_version
|
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)
|
ComponentBase.__init__(self)
|
||||||
self.index = component_index
|
self.index = component_index
|
||||||
self.name = self.CHASSIS_COMPONENTS[self.index][0]
|
self.name = self.CHASSIS_COMPONENTS[self.index][0]
|
||||||
self.description = self.CHASSIS_COMPONENTS[self.index][1]
|
self.description = self.CHASSIS_COMPONENTS[self.index][1]
|
||||||
self.version = self.CHASSIS_COMPONENTS[self.index][2]()
|
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):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the name of the component
|
Retrieves the name of the component
|
||||||
@ -112,16 +207,6 @@ class Component(ComponentBase):
|
|||||||
"""
|
"""
|
||||||
return self.version
|
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):
|
def get_presence(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the presence of the component
|
Retrieves the presence of the component
|
||||||
@ -170,3 +255,171 @@ class Component(ComponentBase):
|
|||||||
bool: True if it is replaceable.
|
bool: True if it is replaceable.
|
||||||
"""
|
"""
|
||||||
return False
|
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