From a70194cd394bb56a8cfa2b05fccd6bd940689eb4 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Wed, 13 Aug 2025 12:45:08 -0400 Subject: [PATCH] Fix SMM3 update process First, break up the generic update into distinct pieces. For SMMv3, first supersede getting the upload url, as multipart is advertised, but doesn't work, but simpleupdate (deprecated) works. Then after upload, switch to hard coded logic specific to SMMv3 to determine how to start the update and where to monitor results. Then hand back over to generic to finish out monitoring. Change-Id: I33b2e67603400b920aca4aa11047c11978c3c6f6 --- pyghmi/redfish/oem/generic.py | 52 +++++++++++++++++++------------ pyghmi/redfish/oem/lenovo/smm3.py | 32 +++++++++++++++++++ 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index 2fdb5ee6..ab0b445e 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -21,6 +21,7 @@ import re from datetime import datetime from datetime import timedelta from dateutil import tz +import socket import time import pyghmi.constants as const @@ -1065,22 +1066,7 @@ class OEMHandler(object): def update_firmware(self, filename, data=None, progress=None, bank=None, otherfields=()): # disable cache to make sure we trigger the token renewal logic if needed - usd = self._do_web_request('/redfish/v1/UpdateService', cache=False) - upurl = usd.get('MultipartHttpPushUri', None) - ismultipart = True - if not upurl: - ismultipart = False - if usd.get('HttpPushUriTargetsBusy', False): - raise exc.TemporaryError('Cannot run multtiple updates to ' - 'same target concurrently') - try: - upurl = usd['HttpPushUri'] - except KeyError: - raise exc.UnsupportedFunctionality('Redfish firmware update only supported for implementations with push update support') - if 'HttpPushUriTargetsBusy' in usd: - self._do_web_request( - '/redfish/v1/UpdateService', - {'HttpPushUriTargetsBusy': True}, method='PATCH') + usd, upurl, ismultipart = self.retrieve_firmware_upload_url() try: uploadthread = webclient.FileUploader( self.webclient, upurl, filename, data, formwrap=ismultipart, @@ -1105,8 +1091,19 @@ class OEMHandler(object): except Exception: raise Exception(uploadthread.rsp) raise Exception(errmsg) + return self.continue_update(uploadthread, progress) + finally: + if 'HttpPushUriTargetsBusy' in usd: + self._do_web_request( + '/redfish/v1/UpdateService', + {'HttpPushUriTargetsBusy': False}, method='PATCH') + + def continue_update(self, uploadthread, progress): rsp = json.loads(uploadthread.rsp) monitorurl = rsp['@odata.id'] + return self.monitor_update_progress(monitorurl, progress) + + def monitor_update_progress(self, monitorurl, progress): complete = False phase = "apply" statetype = 'TaskState' @@ -1151,11 +1148,26 @@ class OEMHandler(object): if not retry: raise Exception('Falied to monitor update progress due to excessive timeouts') return 'pending' - finally: + + + def retrieve_firmware_upload_url(self): + usd = self._do_web_request('/redfish/v1/UpdateService', cache=False) + upurl = usd.get('MultipartHttpPushUri', None) + ismultipart = True + if not upurl: + ismultipart = False + if usd.get('HttpPushUriTargetsBusy', False): + raise exc.TemporaryError('Cannot run multtiple updates to ' + 'same target concurrently') + try: + upurl = usd['HttpPushUri'] + except KeyError: + raise exc.UnsupportedFunctionality('Redfish firmware update only supported for implementations with push update support') if 'HttpPushUriTargetsBusy' in usd: - self._do_web_request( - '/redfish/v1/UpdateService', - {'HttpPushUriTargetsBusy': False}, method='PATCH') + self._do_web_request('/redfish/v1/UpdateService', + {'HttpPushUriTargetsBusy': True}, method='PATCH') + + return usd,upurl,ismultipart def _do_bulk_requests(self, urls, cache=True): diff --git a/pyghmi/redfish/oem/lenovo/smm3.py b/pyghmi/redfish/oem/lenovo/smm3.py index 5c9491d6..def98502 100644 --- a/pyghmi/redfish/oem/lenovo/smm3.py +++ b/pyghmi/redfish/oem/lenovo/smm3.py @@ -19,6 +19,7 @@ import pyghmi.constants as pygconst import pyghmi.util.webclient as webclient import pyghmi.exceptions as exc import time +import socket healthlookup = { 'ok': pygconst.Health.Ok, @@ -78,6 +79,37 @@ class OEMHandler(generic.OEMHandler): def get_system_configuration(self, hideadvanced=True, fishclient=None): return {} + def retrieve_firmware_upload_url(self): + # SMMv3 needs to do the non-multipart upload + usd = self._do_web_request('/redfish/v1/UpdateService', cache=False) + if usd.get('HttpPushUriTargetsBusy', False): + raise exc.TemporaryError('Cannot run multtiple updates to ' + 'same target concurrently') + try: + upurl = usd['HttpPushUri'] + except KeyError: + raise exc.UnsupportedFunctionality('Redfish firmware update only supported for implementations with push update support') + if 'HttpPushUriTargetsBusy' in usd: + self._do_web_request( + '/redfish/v1/UpdateService', + {'HttpPushUriTargetsBusy': True}, method='PATCH') + return usd,upurl,False + + def continue_update(self, uploadthread, progress): + # SMMv3 does not provide a response, must hardcode the continuation + # /redfish/v1/UpdateService/FirmwareInventory/fwuimage + rsp = self._do_web_request('/redfish/v1/UpdateService/FirmwareInventory/fwuimage') + for ri in rsp.get('RelatedItem', []): + targ = ri.get('@odata.id', None) + parms = {'Oem': {'Lenovo': {'SecureRollBack': False}}} + rsp = self._do_web_request('/redfish/v1/UpdateService', parms, method='PATCH') + targspec = {'target': targ} + rsp = self._do_web_request('/redfish/v1/UpdateService/Actions/UpdateService.StartUpdate', targspec) + monitorurl = rsp.get('@odata.id', None) + return self.monitor_update_progress(monitorurl, progress) + + + def get_diagnostic_data(self, savefile, progress=None, autosuffix=False): tsk = self._do_web_request( '/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData',