Detect and use redfish push update

Use redfish updates for newer XCC firmware, for a more straightforward
update procedure.

Change-Id: I5acfb99f2213ccdd987d7362fe154dae22634cca
This commit is contained in:
Jarrod Johnson
2022-01-27 13:13:05 -05:00
parent a10ed8cca4
commit db281bb4f0
2 changed files with 118 additions and 15 deletions

View File

@@ -1784,7 +1784,96 @@ class XCCClient(IMMClient):
progress({'phase': 'complete'})
self.weblogout()
def grab_redfish_response_emptyonerror(self, url, body=None, method=None):
rsp, status = self.grab_redfish_response_with_status(url, body, method)
if status >= 200 and status < 300:
return rsp
return {}
def grab_redfish_response_with_status(self, url, body=None, method=None):
return self.wc.grab_json_response_with_status(url, body, headers={
'Authorization': 'Basic %s' % base64.b64encode(
(self.username + ':' + self.password).encode('utf8')
).decode('utf8'),
'Content-Type': 'application/json'}, method=method)
def redfish_update_firmware(self, usd, filename, data, progress, bank):
if usd['HttpPushUriTargetsBusy']:
raise pygexc.TemporaryError('Cannot run multiple updates to same '
'target concurrently')
upurl = usd['HttpPushUri']
self.grab_redfish_response_with_status(
'/redfish/v1/UpdateService',
{'HttpPushUriTargetsBusy': True}, method='PATCH')
try:
if bank == 'backup':
self.grab_redfish_response_with_status(
'/redfish/v1/UpdateService',
{'HttpPushUriTargets':
['/redfish/v1/UpdateService'
'/FirmwareInventory/BMC-Backup']}, method='PATCH')
wc = self.wc.dupe()
wc.set_basic_credentials(self.username, self.password)
uploadthread = webclient.FileUploader(wc, upurl, filename,
data, formwrap=False,
excepterror=False)
uploadthread.start()
while uploadthread.isAlive():
uploadthread.join(3)
if progress:
progress({'phase': 'upload',
'progress': 100 * wc.get_upload_progress()})
if uploadthread.rspstatus >= 300 or uploadthread.rspstatus < 200:
rsp = uploadthread.rsp
errmsg = ''
try:
rsp = json.loads(rsp)
errmsg = Exception(
rsp['error']['@Message.ExtendedInfo'][0]['Message'])
except Exception:
raise Exception(uploadthread.rsp)
raise Exception(errmsg)
rsp = json.loads(uploadthread.rsp)
monitorurl = rsp['TaskMonitor']
complete = False
while not complete:
pgress, status = self.grab_redfish_response_with_status(
monitorurl)
if status < 200 or status >= 300:
raise Exception(pgress)
if not pgress:
break
for msg in pgress.get('Messages', []):
if 'Verify failed' in msg.get('Message', ''):
raise Exception(msg['Message'])
state = pgress['TaskState']
if state in ('Cancelled', 'Exception',
'Interrupted', 'Suspended'):
raise Exception(json.dumps(pgress['Messages']))
pct = float(pgress['PercentComplete'])
complete = state == 'Completed'
progress({'phase': 'apply', 'progress': pct})
if not complete:
ipmisession.Session.pause(3)
if bank == 'backup':
return 'complete'
return 'pending'
finally:
self.grab_redfish_response_with_status(
'/redfish/v1/UpdateService',
{'HttpPushUriTargetsBusy': False}, method='PATCH')
self.grab_redfish_response_with_status(
'/redfish/v1/UpdateService',
{'HttpPushUriTargets': []}, method='PATCH')
def update_firmware(self, filename, data=None, progress=None, bank=None):
usd = self.grab_redfish_response_emptyonerror(
'/redfish/v1/UpdateService')
rfishurl = usd.get('HttpPushUri', None)
if rfishurl:
self.weblogout()
return self.redfish_update_firmware(
usd, filename, data, progress, bank)
result = None
if self.updating:
raise pygexc.TemporaryError('Cannot run multiple updates to same '

View File

@@ -48,7 +48,7 @@ uploadforms = {}
class FileUploader(threading.Thread):
def __init__(self, webclient, url, filename, data=None, formname=None,
otherfields=()):
otherfields=(), formwrap=True, excepterror=True):
self.wc = webclient
self.url = url
self.filename = filename
@@ -57,13 +57,17 @@ class FileUploader(threading.Thread):
self.formname = formname
self.rsp = ''
self.rspstatus = 500
self.formwrap = formwrap
self.excepterror = excepterror
super(FileUploader, self).__init__()
def run(self):
try:
self.rsp = self.wc.upload(
self.url, self.filename, self.data, self.formname,
otherfields=self.otherfields)
otherfields=self.otherfields, formwrap=self.formwrap,
excepterror=self.excepterror)
self.rspstatus = self.wc.rspstatus
except Exception:
self.rspstatus = self.wc.rspstatus
raise
@@ -279,7 +283,7 @@ class SecureHTTPConnection(httplib.HTTPConnection, object):
self._currdl.getheader('content-length'))
def upload(self, url, filename, data=None, formname=None,
otherfields=()):
otherfields=(), formwrap=True, excepterror=True):
"""Upload a file to the url
:param url:
@@ -290,24 +294,34 @@ class SecureHTTPConnection(httplib.HTTPConnection, object):
"""
if data is None:
data = open(filename, 'rb')
self._upbuffer = io.BytesIO(get_upload_form(
filename, data, formname, otherfields))
ulheaders = self.stdheaders.copy()
ulheaders['Content-Type'] = b'multipart/form-data; boundary=' + BND
ulheaders['Content-Length'] = len(uploadforms[filename])
self.ulsize = len(uploadforms[filename])
ulhdrs = self.stdheaders.copy()
if formwrap:
self._upbuffer = io.BytesIO(get_upload_form(
filename, data, formname, otherfields))
ulhdrs['Content-Type'] = b'multipart/form-data; boundary=' + BND
ulhdrs['Content-Length'] = len(uploadforms[filename])
self.ulsize = len(uploadforms[filename])
else:
curroff = data.tell()
data.seek(0, 2)
self.ulsize = data.tell()
data.seek(curroff, 0)
self._upbuffer = data
ulhdrs['Content-Type'] = b'application/octet-stream'
ulhdrs['Content-Length'] = self.ulsize
webclient = self.dupe()
webclient.request('POST', url, self._upbuffer, ulheaders)
webclient.request('POST', url, self._upbuffer, ulhdrs)
rsp = webclient.getresponse()
# peer updates in progress should already have pointers,
# subsequent transactions will cause memory to needlessly double,
# but easiest way to keep memory relatively low
try:
del uploadforms[filename]
except KeyError: # something could have already deleted it
pass
if formwrap:
try:
del uploadforms[filename]
except KeyError: # something could have already deleted it
pass
self.rspstatus = rsp.status
if rsp.status != 200:
if excepterror and (rsp.status < 200 or rsp.status >= 300):
raise Exception('Unexpected response in file upload: %s'
% rsp.read())
body = rsp.read()