2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-11 12:06:26 +00:00

Register an ipmi session before establishing it, not after

initting_sessions exists so a caller can share a session already on its way,
but the entry was added only once the login had finished, leaving the login
itself uncovered.  Two callers asking at once each built a session, both on
the socket the other had not claimed yet, and replies route by bmc address
and local port, so only the last to transmit was ever answered.  That is the
console session failing about one attempt in three.

Register before the login and remove the entry in a finally.  The two old
removals keyed on the encoded credentials while the register is keyed on the
caller's strings, so they never matched and an entry outlived its session.
A session handed over mid login is now waited for rather than returned as
one that answers as though it had been lost.
This commit is contained in:
Markus Hilger
2026-08-14 14:07:49 +02:00
parent c7b6147e74
commit ef608005cf
+33 -21
View File
@@ -422,6 +422,24 @@ class Session(object):
KEEPALIVE_SESSIONS.release()
return not session.broken
@classmethod
async def _await_login(cls, session):
"""Wait out a login that another caller has already started
A session handed out mid login is not usable yet, and the caller
receiving it cannot tell, so wait and give it the same answer as the
caller that built it.
"""
while not (session.logged or session.broken):
# A ceiling, not a wait: with nothing registered to wait for,
# wait_for_rsp returns at once. Traffic still ends it early.
await cls.wait_for_rsp(1)
if session.broken:
raise exc.IpmiException(
getattr(session, 'errormsg', None)
or 'Unable to establish a session with the bmc')
return session
async def __new__(cls,
bmc,
userid,
@@ -432,6 +450,7 @@ class Session(object):
keepalive=True):
trueself = None
forbidsock = []
sesskey = (bmc, userid, password, port, kg)
for res in socket.getaddrinfo(bmc, port, 0, socket.SOCK_DGRAM):
sockaddr = res[4]
if ipv6support and res[0] == socket.AF_INET:
@@ -460,18 +479,23 @@ class Session(object):
# id, however it's easier this way
forbidsock.append(self.socket)
if trueself:
return trueself
i = cls.initting_sessions.get(
(bmc, userid, password, port, kg), False)
return await cls._await_login(trueself)
i = cls.initting_sessions.get(sesskey, False)
if i:
i.initialized = True
i.logging = True
return i
return await cls._await_login(i)
self = super().__new__(cls)
self.forbidsock = forbidsock
await self.__init__(
bmc, userid, password, port, kg, privlevel, keepalive)
cls.initting_sessions[(bmc, userid, password, port, kg)] = self
# Register before establishing, not after: this is where a caller
# asking for a session already on its way finds it, and leaving it
# to the end left the whole login uncovered
cls.initting_sessions[sesskey] = self
try:
await self.__init__(
bmc, userid, password, port, kg, privlevel, keepalive)
finally:
# The only removal, and it has to happen whether the login
# worked or not, or the entry outlives the session
cls.initting_sessions.pop(sesskey, None)
return self
async def __init__(self,
@@ -569,12 +593,6 @@ class Session(object):
Session.waiting_sessions.pop(self, None)
finally:
WAITING_SESSIONS.release()
try:
del Session.initting_sessions[(self.bmc, self.userid,
self.password, self.port,
self.kgo)]
except KeyError:
pass
await self.logout(False)
# self.logging = False
self.errormsg = error
@@ -1890,12 +1908,6 @@ class Session(object):
Session.bmc_handlers[sockaddr] = {}
Session.bmc_handlers[sockaddr][myport] = self
_io_sendto(self.socket, self.netpacket, sockaddr)
try:
del Session.initting_sessions[(self.bmc, self.userid,
self.password, self.port,
self.kgo)]
except KeyError:
pass
except socket.gaierror:
raise exc.IpmiException(
"Unable to transmit to specified address")