mirror of
https://opendev.org/x/pyghmi
synced 2025-10-26 08:55:20 +00:00
Add lenovo firmware update to redfish
The redfish plugin receives a port of the Lenovo OEM implementation for now. Change-Id: I5c175cef0199031b3b4cd03f9e34581899c1e6c6
This commit is contained in:
@@ -1245,6 +1245,18 @@ class Command(object):
|
||||
"""
|
||||
return self.oem.upload_media(filename, progress)
|
||||
|
||||
def update_firmware(self, file, data=None, progress=None, bank=None):
|
||||
"""Send file to BMC to perform firmware update
|
||||
|
||||
:param filename: The filename to upload to the target BMC
|
||||
:param data: The payload of the firmware. Default is to read from
|
||||
specified filename.
|
||||
:param progress: A callback that will be given a dict describing
|
||||
update process. Provide if
|
||||
:param bank: Indicate a target 'bank' of firmware if supported
|
||||
"""
|
||||
return self.oem.update_firmware(file, data, progress, bank)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -56,6 +56,10 @@ 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):
|
||||
raise exc.UnsupportedFunctionality(
|
||||
'Firmware update not supported on this platform')
|
||||
|
||||
def _do_web_request(self, url, payload=None, method=None, cache=True):
|
||||
res = None
|
||||
if cache and payload is None and method is None:
|
||||
|
||||
@@ -17,6 +17,7 @@ from pyghmi.util.parse import parse_time
|
||||
import errno
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
import pyghmi.ipmi.private.util as util
|
||||
@@ -31,6 +32,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
def __init__(self, sysinfo, sysurl, webclient, cache):
|
||||
super(OEMHandler, self).__init__(sysinfo, sysurl, webclient, cache)
|
||||
self._wc = None
|
||||
self.updating = False
|
||||
|
||||
def get_description(self):
|
||||
description = self._do_web_request('/DeviceDescription.json')
|
||||
@@ -480,3 +482,198 @@ class OEMHandler(generic.OEMHandler):
|
||||
if progress:
|
||||
progress({'phase': 'complete'})
|
||||
#self.weblogout()
|
||||
|
||||
def update_firmware(self, filename, data=None, progress=None, bank=None):
|
||||
result = None
|
||||
if self.updating:
|
||||
raise pygexc.TemporaryError('Cannot run multiple updates to same '
|
||||
'target concurrently')
|
||||
self.updating = True
|
||||
try:
|
||||
result = self.update_firmware_backend(filename, data, progress,
|
||||
bank)
|
||||
except Exception:
|
||||
self.updating = False
|
||||
self._refresh_token()
|
||||
self.wc.grab_json_response('/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebCancel': 1}))
|
||||
self.weblogout()
|
||||
raise
|
||||
self.updating = False
|
||||
#self.weblogout()
|
||||
return result
|
||||
|
||||
def update_firmware_backend(self, filename, data=None, progress=None,
|
||||
bank=None):
|
||||
#self.weblogout()
|
||||
self._refresh_token()
|
||||
rsv = self.wc.grab_json_response('/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebReserve': 1}))
|
||||
if rsv['return'] == 103:
|
||||
raise Exception('Update already in progress')
|
||||
if rsv['return'] != 0:
|
||||
raise Exception('Unexpected return to reservation: ' + repr(rsv))
|
||||
xid = random.randint(0, 1000000000)
|
||||
uploadthread = webclient.FileUploader(
|
||||
self.wc, '/upload?X-Progress-ID={0}'.format(xid), filename, data)
|
||||
uploadthread.start()
|
||||
uploadstate = None
|
||||
while uploadthread.isAlive():
|
||||
uploadthread.join(3)
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/upload/progress?X-Progress-ID={0}'.format(xid))
|
||||
if rsp['state'] == 'uploading':
|
||||
progress({'phase': 'upload',
|
||||
'progress': 100.0 * rsp['received'] / rsp['size']})
|
||||
elif rsp['state'] != 'done':
|
||||
if rsp.get('status', None) == 413 or uploadthread.rspstatus == 413:
|
||||
raise Exception('File is larger than supported')
|
||||
raise Exception('Unexpected result:' + repr(rsp))
|
||||
uploadstate = rsp['state']
|
||||
self._refresh_token()
|
||||
while uploadstate != 'done':
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/upload/progress?X-Progress-ID={0}'.format(xid))
|
||||
uploadstate = rsp['state']
|
||||
self._refresh_token()
|
||||
rsp = json.loads(uploadthread.rsp)
|
||||
if rsp['items'][0]['name'] != os.path.basename(filename):
|
||||
raise Exception('Unexpected response: ' + repr(rsp))
|
||||
progress({'phase': 'validating',
|
||||
'progress': 0.0})
|
||||
time.sleep(3)
|
||||
# aggressive timing can cause the next call to occasionally
|
||||
# return 25 and fail
|
||||
self._refresh_token()
|
||||
rsp = self.wc.grab_json_response('/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebSetFileName': rsp['items'][0]['path']}))
|
||||
if rsp.get('return', 0) in (25, 108):
|
||||
raise Exception('Temporary error validating update, try again')
|
||||
if rsp.get('return', -1) != 0:
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception('Unexpected return to set filename: ' + errmsg)
|
||||
self._refresh_token()
|
||||
progress({'phase': 'validating',
|
||||
'progress': 25.0})
|
||||
rsp = self.wc.grab_json_response('/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebVerifyUploadFile': 1}))
|
||||
if rsp.get('return', 0) == 115:
|
||||
raise Exception('Update image not intended for this system')
|
||||
elif rsp.get('return', -1) == 108:
|
||||
raise Exception('Temporary error validating update, try again')
|
||||
elif rsp.get('return', -1) != 0:
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception('Unexpected return to verify: ' + errmsg)
|
||||
verifystatus = 0
|
||||
verifyuploadfilersp = None
|
||||
while verifystatus != 1:
|
||||
self._refresh_token()
|
||||
rsp, status = self.wc.grab_json_response_with_status(
|
||||
'/api/providers/fwupdate',
|
||||
json.dumps({'UPD_WebVerifyUploadFileStatus': 1}))
|
||||
if not rsp or status != 200 or rsp.get('return', -1) == 2:
|
||||
# The XCC firmware predates the FileStatus api
|
||||
verifyuploadfilersp = rsp
|
||||
break
|
||||
if rsp.get('return', -1) != 0:
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception(
|
||||
'Unexpected return to verifystate: {0}'.format(errmsg))
|
||||
verifystatus = rsp['status']
|
||||
if verifystatus == 2:
|
||||
raise Exception('Failed to verify firmware image')
|
||||
if verifystatus != 1:
|
||||
time.sleep(1)
|
||||
if verifystatus not in (0, 1, 255):
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception(
|
||||
'Unexpected reply to verifystate: ' + errmsg)
|
||||
progress({'phase': 'validating',
|
||||
'progress': 99.0})
|
||||
self._refresh_token()
|
||||
rsp = self.wc.grab_json_response('/api/dataset/imm_firmware_success')
|
||||
if len(rsp['items']) != 1:
|
||||
raise Exception('Unexpected result: ' + repr(rsp))
|
||||
firmtype = rsp['items'][0]['firmware_type']
|
||||
if not firmtype:
|
||||
raise Exception('Unknown firmware description returned: ' + repr(
|
||||
rsp['items'][0]) + ' last verify return was: ' + repr(
|
||||
verifyuploadfilersp) + ' with code {0}'.format(status))
|
||||
if firmtype not in (
|
||||
'TDM', 'WINDOWS DRIV', 'LINUX DRIVER', 'UEFI', 'IMM'):
|
||||
# adapter firmware
|
||||
webid = rsp['items'][0]['webfile_build_id']
|
||||
locations = webid[webid.find('[')+1:webid.find(']')]
|
||||
locations = locations.split(':')
|
||||
validselectors = set([])
|
||||
for loc in locations:
|
||||
validselectors.add(loc.replace('#', '-'))
|
||||
self._refresh_token()
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/function/adapter_update?params=pci_GetAdapterListAndFW')
|
||||
foundselectors = []
|
||||
for adpitem in rsp['items']:
|
||||
selector = '{0}-{1}'.format(adpitem['location'],
|
||||
adpitem['slotNo'])
|
||||
if selector in validselectors:
|
||||
foundselectors.append(selector)
|
||||
if len(foundselectors) == len(validselectors):
|
||||
break
|
||||
else:
|
||||
raise Exception('Could not find matching adapter for update')
|
||||
self._refresh_token()
|
||||
rsp = self.wc.grab_json_response('/api/function', json.dumps(
|
||||
{'pci_SetOOBFWSlots': '|'.join(foundselectors)}))
|
||||
if rsp.get('return', -1) != 0:
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception(
|
||||
'Unexpected result from PCI select: ' + errmsg)
|
||||
self.set_property('/v2/ibmc/uefi/force-inventory', 1)
|
||||
else:
|
||||
self._refresh_token()
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/dataset/imm_firmware_update')
|
||||
if rsp['items'][0]['upgrades'][0]['id'] != 1:
|
||||
raise Exception('Unexpected answer: ' + repr(rsp))
|
||||
self._refresh_token()
|
||||
progress({'phase': 'apply',
|
||||
'progress': 0.0})
|
||||
if bank in ('primary', None):
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebStartDefaultAction': 1}))
|
||||
elif bank == 'backup':
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebStartOptionalAction': 2}))
|
||||
|
||||
if rsp.get('return', -1) != 0:
|
||||
errmsg = repr(rsp) if rsp else self.wc.lastjsonerror
|
||||
raise Exception('Unexpected result starting update: ' +
|
||||
errmsg)
|
||||
complete = False
|
||||
while not complete:
|
||||
time.sleep(3)
|
||||
rsp = self.wc.grab_json_response(
|
||||
'/api/dataset/imm_firmware_progress')
|
||||
progress({'phase': 'apply',
|
||||
'progress': rsp['items'][0]['action_percent_complete']})
|
||||
if rsp['items'][0]['action_state'] == 'Idle':
|
||||
complete = True
|
||||
break
|
||||
if rsp['items'][0]['action_state'] == 'Complete OK':
|
||||
complete = True
|
||||
if rsp['items'][0]['action_status'] != 0:
|
||||
raise Exception('Unexpected failure: ' + repr(rsp))
|
||||
break
|
||||
if (rsp['items'][0]['action_state'] == 'In Progress' and
|
||||
rsp['items'][0]['action_status'] == 2):
|
||||
raise Exception('Unexpected failure: ' + repr(rsp))
|
||||
if rsp['items'][0]['action_state'] != 'In Progress':
|
||||
raise Exception(
|
||||
'Unknown condition waiting for '
|
||||
'firmware update: ' + repr(rsp))
|
||||
if bank == 'backup':
|
||||
return 'complete'
|
||||
return 'pending'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user