2
0
mirror of https://opendev.org/x/pyghmi synced 2026-05-11 17:04:21 +00:00

Rework the relog logic

Various issues have been associated with trying to
reinitiate a session during a 'timeout' scenario, attempt
to clean up the various scenarios where it wasn't originally
designed to survive across a relogin.

Change-Id: Ifd9c8e98070217a1b0f4a35c8e0851a063b77800
This commit is contained in:
Jarrod Johnson
2022-05-16 14:35:40 -04:00
parent 65c8f4ec32
commit 582598c2ef
+52 -28
View File
@@ -494,6 +494,9 @@ class Session(object):
else:
self.iterwaiters.append(onlogon)
return
self.awaitingresponse = False
self.lastresponse = None
self.atomicop = threading.RLock()
self.broken = False
self.socket = None
self.logged = 0
@@ -507,7 +510,7 @@ class Session(object):
self.onlogpayloadtype = None
self.logoutexpiry = None
self.autokeepalive = keepalive
self.maxtimeout = 3 # be aggressive about giving up on initial packet
self.maxtimeout = 2 # be aggressive about giving up on initial packet
self.incommand = False
self.nameonly = 16 # default to name only lookups in RAKP exchange
self.servermode = False
@@ -584,14 +587,16 @@ class Session(object):
def onlogon(self, parameter):
if 'error' in parameter:
self._mark_broken(parameter['error'])
elif self.onlogpayload:
self._cmdwait()
payload = self.onlogpayload
payload_type = self.onlogpayloadtype
self.incommand = _monotonic_time() + self._getmaxtimeout()
self.onlogpayload = None
self.send_payload(payload, payload_type)
while self.logonwaiters:
waiter = self.logonwaiters.pop()
waiter(parameter)
if self.onlogpayload:
self.lastpayload = self.onlogpayload
self.last_payload_type = self.onlogpayloadtype
self.onlogpayload = None
self.send_payload()
def _initsession(self):
# NOTE(jbjohnso): this number can be whatever we want.
@@ -751,26 +756,36 @@ class Session(object):
while incrementtime < self.maxtimeout:
cumulativetime += incrementtime
incrementtime += 1
return cumulativetime + 1
return (cumulativetime + 1) * (self.logontries + 1)
def _cmdwait(self):
while self._isincommand():
_io_wait(self._isincommand(), self.sockaddr, self.evq)
def awaitresponse(self, retry, netfn, command):
while (retry and unmatched(self.lastresponse, netfn, command)
and (self.logged or self.onlogpayload)):
timeout = self.expiration - _monotonic_time()
_io_wait(timeout, self.sockaddr)
while self.iterwaiters:
waiter = self.iterwaiters.pop()
waiter({'success': True})
self.process_pktqueue()
with util.protect(WAITING_SESSIONS):
if (self in self.waiting_sessions
and self.expiration < _monotonic_time()):
self.waiting_sessions.pop(self, None)
self._timedout()
self.awaitingresponse = True
try:
alltimeout = _monotonic_time() + (self._getmaxtimeout() * 2)
while (retry and unmatched(self.lastresponse, netfn, command)
and (self.logging or self.logged or self.onlogpayload)):
timeout = self.expiration - _monotonic_time()
_io_wait(timeout, self.sockaddr)
while self.iterwaiters:
waiter = self.iterwaiters.pop()
waiter({'success': True})
self.process_pktqueue()
if _monotonic_time() > alltimeout:
self._mark_broken()
raise exc.IpmiException('Session no longer connected')
with util.protect(WAITING_SESSIONS):
if (self in self.waiting_sessions
and self.expiration < _monotonic_time()):
self.waiting_sessions.pop(self, None)
if not self.lastpayload and not self.logging:
return
self._timedout()
finally:
self.awaitingresponse = False
def raw_command(self,
netfn,
@@ -782,15 +797,19 @@ class Session(object):
timeout=None,
callback=None,
rslun=0):
while (self.logging and not self.logged
and _monotonic_time() < self.logoutexpiry):
self.pause(1)
if not self.logged:
if (self.logoutexpiry is not None
and _monotonic_time() > self.logoutexpiry):
self._mark_broken()
raise exc.IpmiException('Session no longer connected')
self._cmdwait()
if not self.logged:
raise exc.IpmiException('Session no longer connected')
self.incommand = _monotonic_time() + self._getmaxtimeout()
with util.protect(self.atomicop):
self._cmdwait()
if not self.logged:
raise exc.IpmiException('Session no longer connected')
self.incommand = _monotonic_time() + self._getmaxtimeout()
self.lastresponse = None
if callback is None:
self.ipmicallback = self._generic_callback
@@ -1038,7 +1057,7 @@ class Session(object):
def _req_priv_level(self):
self.logged = 1
self.logoutexpiry = None
self.maxtimeout = 2 # Switch quickly to the relogin recovery
self.maxtimeout = 4 # Switch quickly to the relogin recovery
self.logontries = 1 # have one relogin waiting to heal
response = self.raw_command(netfn=0x6, command=0x3b,
data=[self.privlevel])
@@ -1128,7 +1147,7 @@ class Session(object):
data=[0x8e, self.privlevel])
def login(self):
self.logontries = 2
self.logontries = 1
self._initsession()
self._get_channel_auth_cap()
@@ -1271,6 +1290,7 @@ class Session(object):
def _keptalive(response):
self._generic_callback(response)
response = self.lastresponse
self.lastresponse = None
self.incommand = False
while self.evq:
self.evq.popleft().set()
@@ -1281,7 +1301,8 @@ class Session(object):
def _keepalive(self):
"""Performs a keepalive to avoid idle disconnect"""
if self.awaitingresponse:
return
try:
keptalive = False
if self._customkeepalives:
@@ -1594,6 +1615,7 @@ class Session(object):
payload=payload, payload_type=constants.payload_types['rakp3'])
def _relog(self):
self.logging = True
self._initsession()
self.logontries -= 1
return self._get_channel_auth_cap()
@@ -1743,9 +1765,12 @@ class Session(object):
else:
self.onlogpayload = self.lastpayload
self.onlogpayloadtype = self.last_payload_type
self.maxtimeout = 6
self._relog()
return
elif self.sessioncontext == 'FAILED':
self.lastpayload = None
self.onlogpayload = None
self.nowait = False
return
if self.sessioncontext == 'OPENSESSION':
@@ -1759,7 +1784,6 @@ class Session(object):
# If we can't be sure which RAKP was dropped or if RAKP3/4 was just
# delayed, the most reliable thing to do is rewind and start over
# bmcs do not take kindly to receiving RAKP1 or RAKP3 twice
self.lastpayload = None
self._relog()
else: # in IPMI case, the only recourse is to act as if the packet is
# idempotent. SOL has more sophisticated retry handling