2
0
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:
Jarrod Johnson 2014-05-05 18:14:13 -04:00
parent f17ebf03dd
commit cca6477509
2 changed files with 77 additions and 3 deletions

View File

@ -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

View File

@ -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()