From a1e29baf8b2339c3b337443dbaa39d0e932809dc Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 7 Jan 2025 15:45:42 -0500 Subject: [PATCH] Provide update for backup XCC3 bank Similar to XCC2, we must get at the underlying uxz file if we want to steer it. We also must use the multipart form data to pass json with right Content-Type, add a mechanism to auto-json a dict to acheive this. Change-Id: If204d7a05399e39cd43ffd7622559ab10d906b47 --- pyghmi/redfish/oem/generic.py | 4 ++-- pyghmi/redfish/oem/lenovo/xcc3.py | 29 ++++++++++++++++++++++++++++- pyghmi/util/webclient.py | 18 ++++++++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/pyghmi/redfish/oem/generic.py b/pyghmi/redfish/oem/generic.py index d1874ccd..ab4a7bb9 100644 --- a/pyghmi/redfish/oem/generic.py +++ b/pyghmi/redfish/oem/generic.py @@ -931,7 +931,7 @@ class OEMHandler(object): raise exc.UnsupportedFunctionality( 'Remote media upload not supported on this platform') - def update_firmware(self, filename, data=None, progress=None, bank=None): + 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) @@ -952,7 +952,7 @@ class OEMHandler(object): try: uploadthread = webclient.FileUploader( self.webclient, upurl, filename, data, formwrap=ismultipart, - excepterror=False) + excepterror=False, otherfields=otherfields) uploadthread.start() wc = self.webclient while uploadthread.isAlive(): diff --git a/pyghmi/redfish/oem/lenovo/xcc3.py b/pyghmi/redfish/oem/lenovo/xcc3.py index 37c8495b..3810d065 100644 --- a/pyghmi/redfish/oem/lenovo/xcc3.py +++ b/pyghmi/redfish/oem/lenovo/xcc3.py @@ -3,7 +3,8 @@ import json import pyghmi.redfish.oem.generic as generic import pyghmi.exceptions as pygexc import pyghmi.util.webclient as webclient - +import zipfile +import os.path class OEMHandler(generic.OEMHandler): @@ -82,6 +83,32 @@ class OEMHandler(generic.OEMHandler): 'password_lockout_period': 'AccountLockoutDuration', } + def update_firmware(self, filename, data=None, progress=None, bank=None, otherfields=()): + if not otherfields and bank == 'backup': + uxzcount = 0 + otherfields = {'UpdateParameters': {"Targets": ["/redfish/v1/UpdateService/FirmwareInventory/BMC-Backup"]}} + needseek = False + if data and hasattr(data, 'read'): + if zipfile.is_zipfile(data): + needseek = True + z = zipfile.ZipFile(data) + else: + data.seek(0) + elif data is None and zipfile.is_zipfile(filename): + z = zipfile.ZipFile(filename) + if z: + for tmpname in z.namelist(): + if tmpname.startswith('payloads/'): + uxzcount += 1 + if tmpname.endswith('.uxz'): + wrappedfilename = tmpname + if uxzcount == 1 and wrappedfilename: + filename = os.path.basename(wrappedfilename) + data = z.open(wrappedfilename) + elif needseek: + data.seek(0) + super().update_firmware(filename, data=data, progress=progress, bank=bank, otherfields=otherfields) + def get_bmc_configuration(self): settings = {} acctsrv = self._do_web_request('/redfish/v1/AccountService') diff --git a/pyghmi/util/webclient.py b/pyghmi/util/webclient.py index 55835222..079ac7fb 100644 --- a/pyghmi/util/webclient.py +++ b/pyghmi/util/webclient.py @@ -107,16 +107,22 @@ def get_upload_form(filename, data, formname, otherfields): data = data.read() except AttributeError: pass - form = (b'--' + BND + form = b'' + for ofield in otherfields: + tfield = otherfields[ofield] + xtra='' + if isinstance(tfield, dict): + tfield = json.dumps(tfield) + xtra = '\r\nContent-Type: application/json' + form += (b'--' + BND + + '\r\nContent-Disposition: form-data; ' + 'name="{0}"{1}\r\n\r\n{2}\r\n'.format( + ofield, xtra, tfield).encode('utf-8')) + form += (b'--' + BND + '\r\nContent-Disposition: form-data; ' 'name="{0}"; filename="{1}"\r\n'.format( formname, ffilename).encode('utf-8')) form += b'Content-Type: application/octet-stream\r\n\r\n' + data - for ofield in otherfields: - form += (b'\r\n--' + BND - + '\r\nContent-Disposition: form-data; ' - 'name="{0}"\r\n\r\n{1}'.format( - ofield, otherfields[ofield]).encode('utf-8')) form += b'\r\n--' + BND + b'--\r\n' uploadforms[filename] = form return form