mirror of
https://opendev.org/x/pyghmi
synced 2025-01-16 04:38:19 +00:00
Implement function to check SOL payload as keepalive
The IPMI layer keepalive is sufficient for most scenarios, but SOL additionally cares about the SOL payload specifically. During SOL session, use an SOL specific scheme for keepalive. Change-Id: I23c5b8da4598696aa936274b3e6b527c8204b4db
This commit is contained in:
parent
f17ebf03dd
commit
cca6477509
@ -51,6 +51,7 @@ class Console(object):
|
||||
self.retriedpayload = 0
|
||||
self.pendingoutput = []
|
||||
self.awaitingack = False
|
||||
self.activated = False
|
||||
self.force_session = force
|
||||
self.ipmi_session = session.Session(bmc=bmc,
|
||||
userid=userid,
|
||||
@ -114,6 +115,7 @@ class Console(object):
|
||||
return
|
||||
if 'error' in response:
|
||||
self._print_error(response['error'])
|
||||
self.activated = True
|
||||
#data[0:3] is reserved except for the test mode, which we don't use
|
||||
data = response['data']
|
||||
self.maxoutcount = (data[5] << 8) + data[4]
|
||||
@ -125,11 +127,37 @@ class Console(object):
|
||||
raise NotImplementedError("Non-standard SOL Port Number")
|
||||
#ignore data[10:11] for now, the vlan detail, shouldn't matter to this
|
||||
#code anyway...
|
||||
#NOTE(jbjohnso):
|
||||
#We will use a special purpose keepalive
|
||||
self.keepaliveid = self.ipmi_session.register_keepalive(
|
||||
cmd={'netfn': 6, 'command': 0x4b, 'data': (1, 1)},
|
||||
callback=self._got_payload_instance_info)
|
||||
self.ipmi_session.sol_handler = self._got_sol_payload
|
||||
self.connected = True
|
||||
if len(self.pendingoutput) > 0:
|
||||
self._sendpendingoutput()
|
||||
|
||||
def _got_payload_instance_info(self, response):
|
||||
if 'error' in response:
|
||||
self.activated = False
|
||||
self.ipmi_session.unregister_keepalive(self.keepaliveid)
|
||||
self._print_error(response['error'])
|
||||
return
|
||||
currowner = struct.unpack(
|
||||
"<I", struct.pack('4B', *response['data'][:4]))
|
||||
if currowner[0] != self.ipmi_session.sessionid:
|
||||
# the session is deactivated or active for something else
|
||||
self.activated = False
|
||||
self.ipmi_session.unregister_keepalive(self.keepaliveid)
|
||||
self._print_error('SOL deactivated')
|
||||
return
|
||||
# ok, still here, that means session is alive, but another
|
||||
# common issue is firmware messing with mux on reboot
|
||||
# this would be a nice thing to check, but the serial channel
|
||||
# number is needed and there isn't an obvious means to reliably
|
||||
# discern which channel or even *if* the serial port in question
|
||||
# correlates at all to an ipmi channel to check mux
|
||||
|
||||
def _addpendingdata(self, data):
|
||||
if isinstance(data, dict):
|
||||
self.pendingoutput.append(data)
|
||||
@ -147,6 +175,14 @@ class Console(object):
|
||||
if not self.awaitingack:
|
||||
self._sendpendingoutput()
|
||||
|
||||
def close(self):
|
||||
"""Shut down an SOL session,
|
||||
"""
|
||||
self.ipmi_session.unregister_keepalive(self.keepaliveid)
|
||||
if self.activated:
|
||||
self.ipmi_session.raw_command(netfn=6, command=0x49,
|
||||
data=(1, 1, 0, 0, 0, 0))
|
||||
|
||||
def send_data(self, data):
|
||||
if self.broken:
|
||||
return
|
||||
@ -248,6 +284,7 @@ class Console(object):
|
||||
#for example, retry with new data, retry with no new data
|
||||
#retry with unexpected sequence number
|
||||
if type(payload) == dict: # we received an error condition
|
||||
self.activated = False
|
||||
self._print_error(payload)
|
||||
return
|
||||
newseq = payload[0] & 0b1111
|
||||
@ -289,6 +326,7 @@ class Console(object):
|
||||
if poweredoff:
|
||||
self._print_info("Remote system is powered down")
|
||||
if deactivated:
|
||||
self.activated = False
|
||||
self._print_error("Remote IPMI console disconnected")
|
||||
else: # retry all or part of packet, but in a new form
|
||||
# also add pending output for efficiency and ease
|
||||
|
@ -336,6 +336,7 @@ class Session(object):
|
||||
self.initialized = True
|
||||
self.cleaningup = False
|
||||
self.lastpayload = None
|
||||
self._customkeepalives = None
|
||||
self.bmc = bmc
|
||||
self.broken = False
|
||||
try:
|
||||
@ -931,13 +932,48 @@ class Session(object):
|
||||
session._timedout()
|
||||
return len(cls.waiting_sessions)
|
||||
|
||||
def register_keepalive(self, cmd, callback):
|
||||
'''Register custom keepalive IPMI command
|
||||
|
||||
This is mostly intended for use by the console code.
|
||||
calling code would have an easier time just scheduling in their
|
||||
own threading scheme. Such a behavior would naturally cause
|
||||
the default keepalive to not occur anyway if the calling code
|
||||
is at least as aggressive about timing as pyghmi
|
||||
:param cmd: A dict of arguments to be passed into raw_command
|
||||
:param callback: A function to be called with results of the keepalive
|
||||
|
||||
:returns: value to identify registration for unregister_keepalive
|
||||
'''
|
||||
regid = random.random()
|
||||
if self._customkeepalives is None:
|
||||
self._customkeepalives = {regid: (cmd, callback)}
|
||||
else:
|
||||
while regid in self._customkeepalives:
|
||||
regid = random.random()
|
||||
self._customkeepalives[regid] = (cmd, callback)
|
||||
return regid
|
||||
|
||||
def unregister_keepalive(self, regid):
|
||||
try:
|
||||
del self._customkeepalives[regid]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _keepalive(self):
|
||||
"""Performs a keepalive to avoid idle disconnect
|
||||
"""
|
||||
if self.incommand: # if currently in command, no cause to keepalive
|
||||
return
|
||||
try:
|
||||
self.raw_command(netfn=6, command=1)
|
||||
if not self._customkeepalives:
|
||||
if self.incommand:
|
||||
# if currently in command, no cause to keepalive
|
||||
return
|
||||
self.raw_command(netfn=6, command=1)
|
||||
else:
|
||||
kaids = list(self._customkeepalives.keys())
|
||||
for keepalive in kaids:
|
||||
cmd, callback = self._customkeepalives[keepalive]
|
||||
callback(self.raw_command(**cmd))
|
||||
except exc.IpmiException:
|
||||
self._mark_broken()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user