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:
@@ -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 '
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user