2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-08-03 16:07:00 +00:00

Hold the SMM web session across long operations

A firmware update posts on one session for the minutes its apply loop
runs, and an FFDC collection downloads on the session it acquired, but
wc() judges a session by its age alone, so a settings call arriving
thirty seconds in logged that session out from underneath them.  Flag
the long operations the way the IMM and XCC handlers already do and
leave their session in place.
This commit is contained in:
Markus Hilger
2026-07-27 14:58:58 +02:00
parent 1031bad407
commit d665064dbd
@@ -385,6 +385,7 @@ class SMMClient(object):
self.password = ipmicmd.ipmi_session.password
self._wc = None
self.weblogging = False
self.updating = False
async def clear_bmc_configuration(self):
await self.ipmicmd.raw_command(0x32, 0xad)
@@ -798,15 +799,20 @@ class SMMClient(object):
url = '/preview/smm-ffdc.tgz?ST1={0}'.format(wc.st1)
if autosuffix and not savefile.endswith('.tgz'):
savefile += '-smm-ffdc.tgz'
fd = webclient.make_downloader(wc, url, savefile)
while not fd.completed():
try:
await fd.join(1)
except asyncio.TimeoutError:
pass
if progress and await fd.get_progress():
progress({'phase': 'download',
'progress': 100 * await fd.get_progress()})
# the download runs on this session, keep wc() from logging it out
self.updating = True
try:
fd = webclient.make_downloader(wc, url, savefile)
while not fd.completed():
try:
await fd.join(1)
except asyncio.TimeoutError:
pass
if progress and await fd.get_progress():
progress({'phase': 'download',
'progress': 100 * await fd.get_progress()})
finally:
self.updating = False
if progress:
progress({'phase': 'complete'})
return savefile
@@ -985,53 +991,63 @@ class SMMClient(object):
break
progress({'phase': 'upload', 'progress': 0.0})
wc = await self.wc()
rsp, status, _ = await wc.grab_response_with_status('/data', 'set=fwType:10') # SMM firmware
if status != 200:
raise Exception(rsp)
url = '/fwupload/fwupload.esp?ST1={0}'.format(wc.st1)
fu = await webclient.make_uploader(
wc, url, filename, data, formname='fileUpload',
otherfields={'preConfig': 'on'})
while not fu.completed():
try:
await fu.join(3)
except asyncio.TimeoutError:
pass
if progress:
progress({'phase': 'upload',
'progress': 100 * await fu.get_progress()})
progress({'phase': 'validating', 'progress': 0.0})
url = '/data'
rsp, status, _ = await wc.grab_response_with_status(url, 'get=fwVersion,spfwInfo')
if status != 200:
raise Exception('Error validating firmware')
progress({'phase': 'apply', 'progress': 0.0})
# only understood by newer SMM2 firmware, ignore rejection by older
await wc.grab_response_with_status('/data', 'set=securityrollback:1')
rsp, status, _ = await wc.grab_response_with_status('/data', 'set=fwUpdate:1')
if status != 200:
raise Exception(rsp)
complete = False
tries = 0
while not complete:
await ipmisession.Session.pause(3)
try:
progdata, status, _ = await wc.grab_response_with_status('/data', 'get=fwProgress,fwUpdate')
except Exception:
if tries > 2:
raise
tries += 1
continue
# the update runs on this session, keep wc() from logging it out
self.updating = True
try:
rsp, status, _ = await wc.grab_response_with_status(
'/data', 'set=fwType:10') # SMM firmware
if status != 200:
raise Exception('Error applying firmware')
progdata = fromstring(progdata)
if progdata.findall('fwUpdate')[0].text == 'invalid signature':
raise Exception('Firmware signature invalid')
percent = float(progdata.findall('fwProgress')[0].text)
raise Exception(rsp)
url = '/fwupload/fwupload.esp?ST1={0}'.format(wc.st1)
fu = await webclient.make_uploader(
wc, url, filename, data, formname='fileUpload',
otherfields={'preConfig': 'on'})
while not fu.completed():
try:
await fu.join(3)
except asyncio.TimeoutError:
pass
if progress:
progress({'phase': 'upload',
'progress': 100 * await fu.get_progress()})
progress({'phase': 'validating', 'progress': 0.0})
url = '/data'
rsp, status, _ = await wc.grab_response_with_status(
url, 'get=fwVersion,spfwInfo')
if status != 200:
raise Exception('Error validating firmware')
progress({'phase': 'apply', 'progress': 0.0})
# only understood by newer SMM2 firmware, ignore rejection by older
await wc.grab_response_with_status(
'/data', 'set=securityrollback:1')
rsp, status, _ = await wc.grab_response_with_status(
'/data', 'set=fwUpdate:1')
if status != 200:
raise Exception(rsp)
complete = False
tries = 0
while not complete:
await ipmisession.Session.pause(3)
try:
progdata, status, _ = await wc.grab_response_with_status(
'/data', 'get=fwProgress,fwUpdate')
except Exception:
if tries > 2:
raise
tries += 1
continue
if status != 200:
raise Exception('Error applying firmware')
progdata = fromstring(progdata)
if progdata.findall('fwUpdate')[0].text == 'invalid signature':
raise Exception('Firmware signature invalid')
percent = float(progdata.findall('fwProgress')[0].text)
progress({'phase': 'apply',
'progress': percent})
complete = percent >= 100.0
progress({'phase': 'apply',
'progress': percent})
complete = percent >= 100.0
finally:
self.updating = False
return 'complete'
async def get_inventory_descriptions(self, ipmicmd, variant):
@@ -1096,8 +1112,10 @@ class SMMClient(object):
try:
if (not self._wc or (self._wc.vintage
and self._wc.vintage < util._monotonic_time() - 30)):
# in case the existing session is still valid, dispose of it
await self.logout()
if not self.updating and self._wc:
# in case the existing session is still valid, dispose
# of it
await self.logout()
self._wc = await self.get_webclient()
finally:
self.weblogging = False