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

Make the SMM web session cache safe to share

Now that the expiry comparison actually caches a client, the session it
holds is shared, so tearing it down and replacing it needs the same care
the IMM handler already takes:

Dispose of an expired session with a logout instead of dropping the
reference, otherwise every refresh leaves an authenticated session
behind on an SMM that only has a handful of slots.  That logout has to
tolerate a session the SMM has already reaped, hence the except.

Guard the login itself, so two coroutines arriving at an empty or
expired cache do not both log in and orphan one of the two sessions.

Stamp the vintage once the login round trips are done rather than
before, so a slow SMM cannot hand back a client that is already expired.
This commit is contained in:
Markus Hilger
2026-07-27 00:28:10 +02:00
parent ca81907d25
commit 5135a6cd3d
@@ -384,6 +384,7 @@ class SMMClient(object):
self.username = ipmicmd.ipmi_session.userid
self.password = ipmicmd.ipmi_session.password
self._wc = None
self.weblogging = False
async def clear_bmc_configuration(self):
await self.ipmicmd.raw_command(0x32, 0xad)
@@ -830,7 +831,6 @@ class SMMClient(object):
cv = self.ipmicmd.certverify
wc = webclient.WebConnection(self.smm, 443, verifycallback=cv)
wc.set_header('Content-Type', 'application/x-www-form-urlencoded')
wc.vintage = util._monotonic_time()
loginform = urlencode(
{
'user': self.username,
@@ -884,6 +884,7 @@ class SMMClient(object):
if not wc.st2:
raise Exception('Unable to locate ST2 token')
wc.set_header('ST2', wc.st2)
wc.vintage = util._monotonic_time()
return wc
async def set_hostname(self, hostname):
@@ -1093,10 +1094,21 @@ class SMMClient(object):
if wc is None:
return
# best effort, a stale session must not fail the caller's operation
await wc.grab_response_with_status('/data/logout', None, method='POST')
try:
await wc.grab_response_with_status('/data/logout', None, method='POST')
except Exception:
pass
async def wc(self):
if (not self._wc or (self._wc.vintage
and self._wc.vintage < util._monotonic_time() - 30)):
self._wc = await self.get_webclient()
while self.weblogging:
await ipmisession.Session.pause(0.25)
self.weblogging = True
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()
self._wc = await self.get_webclient()
finally:
self.weblogging = False
return self._wc