mirror of
https://opendev.org/x/pyghmi
synced 2025-01-28 11:57:34 +00:00
PEP8 compliance
Change instances in code where flake8 complained Change-Id: I0b125fcff39024f9d4bf3c4c26a863efd0fa5cba
This commit is contained in:
parent
51cf01f6c9
commit
0e8ca33842
@ -81,17 +81,17 @@ class Command(object):
|
||||
# TODO(jbjohnso): accept tuples and lists of each parameter for mass
|
||||
# operations without pushing the async complexities up the stack
|
||||
self.ipmi_session = session.Session(bmc=bmc,
|
||||
userid=userid,
|
||||
password=password,
|
||||
kg=kg)
|
||||
userid=userid,
|
||||
password=password,
|
||||
kg=kg)
|
||||
|
||||
def get_bootdev(self, callback=None, callback_args=None):
|
||||
"""Get current boot device override information.
|
||||
|
||||
Provides the current requested boot device. Be aware that not all IPMI
|
||||
devices support this. Even in BMCs that claim to, occasionally the BIOS
|
||||
or UEFI fail to honor it. This is usually only applicable to the next
|
||||
reboot.
|
||||
devices support this. Even in BMCs that claim to, occasionally the
|
||||
BIOS or UEFI fail to honor it. This is usually only applicable to the
|
||||
next reboot.
|
||||
|
||||
:param callback: optional callback
|
||||
:param callback_args: optional arguments to callback
|
||||
@ -114,13 +114,16 @@ class Command(object):
|
||||
return self.lastresponse
|
||||
return True
|
||||
|
||||
def set_power(self, powerstate, wait=False, callback=None, callback_args=None):
|
||||
def set_power(self, powerstate, wait=False, callback=None,
|
||||
callback_args=None):
|
||||
"""Request power state change
|
||||
|
||||
:param powerstate:
|
||||
* on -- Request system turn on
|
||||
* off -- Request system turn off without waiting for OS to shutdown
|
||||
* shutdown -- Have system request OS proper shutdown
|
||||
* off -- Request system turn off without waiting
|
||||
for OS to shutdown
|
||||
* shutdown -- Have system request OS proper
|
||||
shutdown
|
||||
* reset -- Request system reset without waiting for
|
||||
OS
|
||||
* boot -- If system is off, then 'on', else 'reset'
|
||||
@ -141,11 +144,11 @@ class Command(object):
|
||||
self.wait_for_power = wait
|
||||
self.ipmi_session.raw_command(netfn=0,
|
||||
command=1,
|
||||
callback=self._set_power_with_chassis_info
|
||||
callback=self._set_power_with_chassisinfo
|
||||
)
|
||||
return self._waitifsync()
|
||||
|
||||
def _set_power_with_chassis_info(self, response):
|
||||
def _set_power_with_chassisinfo(self, response):
|
||||
if 'error' in response:
|
||||
_raiseorcall(
|
||||
self.commandcallback, response, self.commandcallbackargs)
|
||||
@ -177,8 +180,8 @@ class Command(object):
|
||||
self.requestpending = False
|
||||
if self.commandcallback:
|
||||
session.call_with_optional_args(self.commandcallback,
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
|
||||
def _power_wait(self, response):
|
||||
if 'error' in response:
|
||||
@ -191,8 +194,8 @@ class Command(object):
|
||||
self.lastresponse = {'powerstate': self.powerstate}
|
||||
if self.commandcallback:
|
||||
session.call_with_optional_args(self.commandcallback,
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
return
|
||||
self.ipmi_session.raw_command(netfn=0,
|
||||
command=1,
|
||||
@ -211,9 +214,11 @@ class Command(object):
|
||||
*hd -- Boot from hard drive
|
||||
*optical -- boot from CD or DVD drive
|
||||
*setup -- Boot into setup utility
|
||||
*default -- remove any IPMI directed boot device request
|
||||
:param persist: If true, ask that system firmware use this device beyond
|
||||
next boot. Be aware many systems do not honor this
|
||||
*default -- remove any IPMI directed boot device
|
||||
request
|
||||
:param persist: If true, ask that system firmware use this device
|
||||
beyond next boot. Be aware many systems do not honor
|
||||
this
|
||||
:param uefiboot: If true, request UEFI boot explicitly. Strictly
|
||||
speaking, the spec sugests that if not set, the system
|
||||
should BIOS boot and offers no "don't care" option.
|
||||
@ -321,8 +326,8 @@ class Command(object):
|
||||
self.lastresponse = {'bootdev': bootnum}
|
||||
if self.commandcallback:
|
||||
session.call_with_optional_args(self.commandcallback,
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
|
||||
def get_power(self, callback=None, callback_args=None):
|
||||
"""Get current power state of the managed system
|
||||
@ -352,5 +357,5 @@ class Command(object):
|
||||
self.lastresponse = {'powerstate': self.powerstate}
|
||||
if self.commandcallback:
|
||||
session.call_with_optional_args(self.commandcallback,
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
self.lastresponse,
|
||||
self.commandcallbackargs)
|
||||
|
@ -19,9 +19,11 @@
|
||||
import fcntl
|
||||
import os
|
||||
import struct
|
||||
import types
|
||||
|
||||
from ipmi.private import session
|
||||
from ipmi.private import constants
|
||||
from ipmi.private import session
|
||||
|
||||
|
||||
class Console(object):
|
||||
"""IPMI SOL class.
|
||||
@ -42,13 +44,13 @@ class Console(object):
|
||||
def __init__(self, bmc, userid, password,
|
||||
iohandler=None,
|
||||
force=False, kg=None):
|
||||
if type(iohandler) == tuple: #two file handles
|
||||
if type(iohandler) == tuple: # two file handles
|
||||
self.console_in = iohandler[0]
|
||||
self.console_out = iohandler[1]
|
||||
elif type(iohandler) == file: # one full duplex file handle
|
||||
elif type(iohandler) == file: # one full duplex file handle
|
||||
self.console_out = iohandler
|
||||
self.console_in = iohandler
|
||||
elif type(iohander) == types.FunctionType:
|
||||
elif isinstance(iohandler, types.FunctionType):
|
||||
self.console_out = None
|
||||
self.console_in = None
|
||||
self.out_handler = iohandler
|
||||
@ -63,15 +65,14 @@ class Console(object):
|
||||
self.ackedcount = 0
|
||||
self.ackedseq = 0
|
||||
self.retriedpayload = 0
|
||||
self.pendingoutput=""
|
||||
self.awaitingack=False
|
||||
self.pendingoutput = ""
|
||||
self.awaitingack = False
|
||||
self.force_session = force
|
||||
self.ipmi_session = session.Session(bmc=bmc,
|
||||
userid=userid,
|
||||
password=password,
|
||||
kg=kg,
|
||||
onlogon=self._got_session
|
||||
)
|
||||
userid=userid,
|
||||
password=password,
|
||||
kg=kg,
|
||||
onlogon=self._got_session)
|
||||
|
||||
def _got_session(self, response):
|
||||
"""Private function to navigate SOL payload activation
|
||||
@ -104,15 +105,15 @@ class Console(object):
|
||||
0x82: 'Maximum SOL session count reached',
|
||||
0x83: 'Cannot activate payload with encryption',
|
||||
0x84: 'Cannot activate payload without encryption',
|
||||
}
|
||||
}
|
||||
if response['code']:
|
||||
if response['code'] in constants.ipmi_completion_codes:
|
||||
self._print_data(
|
||||
constants.ipmi_completion_codes[response['code']])
|
||||
constants.ipmi_completion_codes[response['code']])
|
||||
return
|
||||
elif response['code'] == 0x80:
|
||||
if self.force_session and not self.retriedpayload:
|
||||
self.retriedpayload=1
|
||||
self.retriedpayload = 1
|
||||
self.ipmi_session.raw_command(netfn=0x6, command=0x49,
|
||||
data=(1, 1, 0, 0, 0, 0),
|
||||
callback=self._got_session)
|
||||
@ -125,8 +126,8 @@ class Console(object):
|
||||
return
|
||||
else:
|
||||
self._print_data(
|
||||
'SOL encountered Unrecognized error code %d\n' %
|
||||
response['code'])
|
||||
'SOL encountered Unrecognized error code %d\n' %
|
||||
response['code'])
|
||||
return
|
||||
#data[0:3] is reserved except for the test mode, which we don't use
|
||||
data = response['data']
|
||||
@ -138,12 +139,12 @@ class Console(object):
|
||||
raise Exception("TODO(jbjohnso): support atypical SOL port number")
|
||||
#ignore data[10:11] for now, the vlan detail, shouldn't matter to this
|
||||
#code anyway...
|
||||
self.ipmi_session.sol_handler=self._got_sol_payload
|
||||
self.ipmi_session.sol_handler = self._got_sol_payload
|
||||
if self.console_in is not None:
|
||||
self.ipmi_session.register_handle_callback(self.console_in,
|
||||
self._got_cons_input)
|
||||
|
||||
def _got_cons_input(self,handle):
|
||||
def _got_cons_input(self, handle):
|
||||
"""Callback for handle events detected by ipmi session
|
||||
"""
|
||||
self.pendingoutput += handle.read()
|
||||
@ -156,19 +157,19 @@ class Console(object):
|
||||
if self.myseq == 0:
|
||||
self.myseq = 1
|
||||
payload = struct.pack("BBBB",
|
||||
self.myseq,
|
||||
self.ackedseq,
|
||||
self.ackedseq,
|
||||
self.sendbreak)
|
||||
self.myseq,
|
||||
self.ackedseq,
|
||||
self.ackedseq,
|
||||
self.sendbreak)
|
||||
payload += self.pendingoutput
|
||||
self.lasttextsize = len(self.pendingoutput)
|
||||
self.pendingoutput = ""
|
||||
self.awaitingack = True
|
||||
payload = struct.unpack("%dB" % len(payload), payload)
|
||||
self.lastpayload=payload
|
||||
self.lastpayload = payload
|
||||
self.ipmi_session.send_payload(payload, payload_type=1)
|
||||
|
||||
def _print_data(self,data):
|
||||
def _print_data(self, data):
|
||||
"""Convey received data back to caller in the format of their choice.
|
||||
|
||||
Caller may elect to provide this class filehandle(s) or else give a
|
||||
@ -178,7 +179,7 @@ class Console(object):
|
||||
if self.console_out is not None:
|
||||
self.console_out.write(data)
|
||||
self.console_out.flush()
|
||||
elif self.out_handler: #callback style..
|
||||
elif self.out_handler: # callback style..
|
||||
self.out_handler(data)
|
||||
|
||||
def _got_sol_payload(self, payload):
|
||||
@ -197,41 +198,41 @@ class Console(object):
|
||||
#no reason would be treated the same, new payload with partial data
|
||||
remdata = ""
|
||||
remdatalen = 0
|
||||
if newseq != 0: #this packet at least has some data to send to us..
|
||||
if newseq != 0: # this packet at least has some data to send to us..
|
||||
if len(payload) > 4:
|
||||
remdatalen = len(payload[4:]) #store remote data len before dupe
|
||||
remdatalen = len(payload[4:]) # store remote len before dupe
|
||||
#retry logic, we must ack *this* many even if it is
|
||||
#a retry packet with new partial data
|
||||
remdata = struct.pack("%dB" % remdatalen, *payload[4:])
|
||||
if newseq == self.remseq: #it is a retry, but could have new data..
|
||||
if newseq == self.remseq: # it is a retry, but could have new data
|
||||
if remdatalen > self.lastsize:
|
||||
remdata = remdata[4 + self.lastsize:]
|
||||
else: # no new data...
|
||||
else: # no new data...
|
||||
remdata = ""
|
||||
else: #TODO(jbjohnso) what if remote sequence number is wrong??
|
||||
else: # TODO(jbjohnso) what if remote sequence number is wrong??
|
||||
self.remseq = newseq
|
||||
self.lastsize=remdatalen
|
||||
self.lastsize = remdatalen
|
||||
self._print_data(remdata)
|
||||
ackpayload = (0, self.remseq, remdatalen, 0)
|
||||
#Why not put pending data into the ack? because it's rare
|
||||
#and might be hard to decide what to do in the context of
|
||||
#retry situation
|
||||
self.ipmi_session.send_payload(ackpayload,
|
||||
payload_type=1, retry=False)
|
||||
if self.myseq != 0 and ackseq == self.myseq: #the bmc has something to
|
||||
#say about our last xmit
|
||||
self.awaitingack=False
|
||||
if nacked > 0: #the BMC was in some way unhappy
|
||||
payload_type=1, retry=False)
|
||||
if self.myseq != 0 and ackseq == self.myseq: # the bmc has something
|
||||
# to say about last xmit
|
||||
self.awaitingack = False
|
||||
if nacked > 0: # the BMC was in some way unhappy
|
||||
if poweredoff:
|
||||
self._print_data("Remote system is powered down\n")
|
||||
if deactivated:
|
||||
self._print_data("Remote IPMI console disconnected\n")
|
||||
else: #retry all or part of packet, but in a new form
|
||||
#also add pending output for efficiency and ease
|
||||
else: # retry all or part of packet, but in a new form
|
||||
# also add pending output for efficiency and ease
|
||||
newtext = self.lastpayload[4 + ackcount:]
|
||||
self.pendingoutput = newtext + self.pendingoutput
|
||||
self._sendpendingoutput()
|
||||
elif self.awaitingack: #session has marked us as happy, but we are not
|
||||
elif self.awaitingack: # session marked us as happy, but we are not
|
||||
#this does mean that we will occasionally retry a packet
|
||||
#sooner than retry suggests, but that's no big deal
|
||||
self.ipmi_session.send_payload(payload=self.lastpayload,
|
||||
@ -247,7 +248,7 @@ class Console(object):
|
||||
"""
|
||||
#wait_for_rsp promises to return a false value when no sessions are
|
||||
#alive anymore
|
||||
#TODO(jbjohnso): wait_for_rsp is not returning a true value from our own
|
||||
#TODO(jbjohnso): wait_for_rsp is not returning a true value for our own
|
||||
#session
|
||||
while (1):
|
||||
session.Session.wait_for_rsp(timeout=600)
|
||||
|
@ -305,16 +305,16 @@ class Session:
|
||||
else:
|
||||
self.ipmicallback = callback
|
||||
self._send_ipmi_net_payload(netfn, command, data, retry=retry)
|
||||
if retry: #in retry case, let the retry timers auto-indicate wait time
|
||||
timeout=None
|
||||
else: #if not retry, give it a second before surrending
|
||||
timeout=1
|
||||
if retry: # in retry case, let the retry timers indicate wait time
|
||||
timeout = None
|
||||
else: # if not retry, give it a second before surrending
|
||||
timeout = 1
|
||||
#In the synchronous case, wrap the event loop in this call
|
||||
#The event loop is shared amongst python-ipmi session instances
|
||||
#within a process. In this way, synchronous usage of the interface
|
||||
#plays well with asynchronous use. In fact, this produces the behavior
|
||||
#of only the constructor *really* needing a callback. From then on,
|
||||
#synchronous usage of the class acts in a greenthread manner governed by
|
||||
#synchronous usage of the class acts in a greenthread style governed by
|
||||
#order of data on the network
|
||||
if callback is None:
|
||||
while self.lastresponse is None:
|
||||
@ -332,10 +332,10 @@ class Session:
|
||||
#we already have a packet outgoing, make this
|
||||
# a pending payload
|
||||
# this way a simplistic BMC won't get confused
|
||||
# and we also avoid having to do a more complicated
|
||||
# and we also avoid having to do more complicated
|
||||
# retry mechanism where each payload is
|
||||
# retried separately
|
||||
self.pendingpayloads.append((payload,payload_type,retry))
|
||||
self.pendingpayloads.append((payload, payload_type, retry))
|
||||
return
|
||||
if payload_type is None:
|
||||
payload_type = self.last_payload_type
|
||||
@ -389,8 +389,8 @@ class Session:
|
||||
payloadtocrypt = _aespad(payload)
|
||||
crypter = AES.new(self.aeskey, AES.MODE_CBC, iv)
|
||||
crypted = crypter.encrypt(struct.pack("%dB" %
|
||||
len(payloadtocrypt),
|
||||
*payloadtocrypt))
|
||||
len(payloadtocrypt),
|
||||
*payloadtocrypt))
|
||||
crypted = list(struct.unpack("%dB" % len(crypted), crypted))
|
||||
message += crypted
|
||||
else: # no confidetiality algorithm
|
||||
@ -411,21 +411,21 @@ class Session:
|
||||
integdata = message[4:]
|
||||
authcode = HMAC.new(self.k1,
|
||||
struct.pack("%dB" % len(integdata),
|
||||
*integdata),
|
||||
*integdata),
|
||||
SHA).digest()[:12] # SHA1-96
|
||||
# per RFC2404 truncates to 96 bits
|
||||
# per RFC2404 truncates to 96 bits
|
||||
message += struct.unpack("12B", authcode)
|
||||
self.netpacket = struct.pack("!%dB" % len(message), *message)
|
||||
#advance idle timer since we don't need keepalive while slinging packets
|
||||
#advance idle timer since we don't need keepalive while sending packets
|
||||
#out naturally
|
||||
if self in Session.keepalive_sessions:
|
||||
Session.keepalive_sessions[self]['timeout'] = time.time() + 25 + \
|
||||
(random.random() * 4.9)
|
||||
(random.random() * 4.9)
|
||||
self._xmit_packet(retry)
|
||||
|
||||
def _ipmi15authcode(self, payload, checkremotecode=False):
|
||||
if self.authtype == 0: # Only for things prior to auth in ipmi 1.5, not
|
||||
# like 2.0 cipher suite 0
|
||||
if self.authtype == 0: # Only for things before auth in ipmi 1.5, not
|
||||
# like 2.0 cipher suite 0
|
||||
return ()
|
||||
password = self.password
|
||||
padneeded = 16 - len(password)
|
||||
@ -435,14 +435,14 @@ class Session:
|
||||
passdata = struct.unpack("16B", password)
|
||||
if checkremotecode:
|
||||
seqbytes = struct.unpack("!4B",
|
||||
struct.pack("<I", self.remsequencenumber))
|
||||
struct.pack("<I", self.remsequencenumber))
|
||||
else:
|
||||
seqbytes = struct.unpack("!4B",
|
||||
struct.pack("<I", self.sequencenumber))
|
||||
struct.pack("<I", self.sequencenumber))
|
||||
sessdata = struct.unpack("!4B", struct.pack("<I", self.sessionid))
|
||||
bodydata = passdata + sessdata + tuple(payload) + seqbytes + passdata
|
||||
dgst = hashlib.md5(
|
||||
struct.pack("%dB" % len(bodydata), *bodydata)).digest()
|
||||
struct.pack("%dB" % len(bodydata), *bodydata)).digest()
|
||||
hashdata = struct.unpack("!%dB" % len(dgst), dgst)
|
||||
return hashdata
|
||||
|
||||
@ -468,10 +468,11 @@ class Session:
|
||||
self.ipmiversion = 2.0
|
||||
if self.ipmiversion == 1.5:
|
||||
if not (data[1] & 0b100):
|
||||
call_with_optional_args(self.onlogon,
|
||||
{'error':
|
||||
"MD5 is required but not enabled/available on target BMC"},
|
||||
self.onlogonargs)
|
||||
call_with_optional_args(
|
||||
self.onlogon,
|
||||
{'error':
|
||||
"MD5 required but not enabled/available on target BMC"},
|
||||
self.onlogonargs)
|
||||
return
|
||||
self._get_session_challenge()
|
||||
elif self.ipmiversion == 2.0:
|
||||
@ -509,7 +510,8 @@ class Session:
|
||||
return
|
||||
data = response['data']
|
||||
self.sessionid = struct.unpack("<I", struct.pack("4B", *data[1:5]))[0]
|
||||
self.sequencenumber = struct.unpack("<I", struct.pack("4B", *data[5:9]))[0]
|
||||
self.sequencenumber = struct.unpack("<I",
|
||||
struct.pack("4B", *data[5:9]))[0]
|
||||
self._req_priv_level()
|
||||
|
||||
def _req_priv_level(self):
|
||||
@ -531,7 +533,7 @@ class Session:
|
||||
Session.keepalive_sessions[self] = {}
|
||||
Session.keepalive_sessions[self]['ipmisession'] = self
|
||||
Session.keepalive_sessions[self]['timeout'] = time.time() + 25 + \
|
||||
(random.random() * 4.9)
|
||||
(random.random() * 4.9)
|
||||
call_with_optional_args(
|
||||
self.onlogon, {'success': True}, self.onlogonargs)
|
||||
|
||||
@ -547,7 +549,7 @@ class Session:
|
||||
|
||||
def _open_rmcpplus_request(self):
|
||||
self.authtype = 6
|
||||
self.localsid += 1 # have unique local session ids to ignore aborted
|
||||
self.localsid += 1 # have unique local session ids to ignore aborted
|
||||
# login attempts from the past
|
||||
self.rmcptag += 1
|
||||
data = [
|
||||
@ -563,8 +565,9 @@ class Session:
|
||||
#2,0,0,8,0,0,0,0, #no privacy confalgo
|
||||
]
|
||||
self.sessioncontext = 'OPENSESSION'
|
||||
self.send_payload(payload=data,
|
||||
payload_type=constants.payload_types['rmcpplusopenreq'])
|
||||
self.send_payload(
|
||||
payload=data,
|
||||
payload_type=constants.payload_types['rmcpplusopenreq'])
|
||||
|
||||
def _get_channel_auth_cap(self):
|
||||
self.ipmicallback = self._got_channel_auth_cap
|
||||
@ -606,7 +609,7 @@ class Session:
|
||||
# each session with active outbound payload has callback to
|
||||
# handle retry/timout error
|
||||
# each session that is 'alive' wants to send a keepalive ever so often.
|
||||
# We want to make sure the most strict request is honored, and block for
|
||||
# We want to make sure the most strict request is honored and block for
|
||||
# no more time than that, so that whatever part(ies) need to service in
|
||||
# a deadline, will be honored
|
||||
if timeout != 0:
|
||||
@ -614,18 +617,20 @@ class Session:
|
||||
if parms['timeout'] <= curtime:
|
||||
timeout = 0 # exit after one guaranteed pass
|
||||
break
|
||||
if timeout is not None and timeout < parms['timeout'] - curtime:
|
||||
continue # timeout is smaller than the current session needs
|
||||
if (timeout is not None and
|
||||
timeout < parms['timeout'] - curtime):
|
||||
continue # timeout smaller than the current session needs
|
||||
timeout = parms['timeout'] - curtime # set new timeout value
|
||||
for session, parms in cls.keepalive_sessions.iteritems():
|
||||
if parms['timeout'] <= curtime:
|
||||
timeout = 0
|
||||
break
|
||||
if timeout is not None and timeout < parms['timeout'] - curtime:
|
||||
if (timeout is not None and
|
||||
timeout < parms['timeout'] - curtime):
|
||||
continue
|
||||
timeout = parms['timeout'] - curtime
|
||||
# If the loop above found no sessions wanting *and* the caller had no
|
||||
# timeout in mind, exit function. In this case there is no way a session
|
||||
# timeout, exit function. In this case there is no way a session
|
||||
# could be waiting so we can always return 0
|
||||
if timeout is None:
|
||||
return 0
|
||||
@ -643,7 +648,7 @@ class Session:
|
||||
(data, sockaddr) = pktqueue.popleft()
|
||||
cls._route_ipmiresponse(sockaddr, data)
|
||||
while cls.ipmipoller.poll(0): # seems ridiculous, but
|
||||
# between every single callback, check for packets again
|
||||
# between every callback, check for packets again
|
||||
rdata = cls.socket.recvfrom(3000)
|
||||
pktqueue.append(rdata)
|
||||
for handlepair in cls.poller.poll(0):
|
||||
@ -655,20 +660,20 @@ class Session:
|
||||
for session, parms in cls.keepalive_sessions.iteritems():
|
||||
if parms['timeout'] < curtime:
|
||||
cls.keepalive_sessions[session]['timeout'] = 25 + \
|
||||
(random.random() * 4.9)
|
||||
(random.random() * 4.9)
|
||||
session._keepalive()
|
||||
for session, parms in cls.waiting_sessions.iteritems():
|
||||
if parms['timeout'] < curtime: # timeout has expired, time to
|
||||
# give up # on it and trigger timeout
|
||||
# response # in the respective
|
||||
# session
|
||||
if parms['timeout'] < curtime: # timeout has expired, time to
|
||||
# give up on it and trigger timeout
|
||||
# response in the respective
|
||||
# session
|
||||
sessionstodel.append(
|
||||
session) # defer deletion until after loop
|
||||
# to avoid confusing the for loop
|
||||
for session in sessionstodel:
|
||||
cls.pending -= 1
|
||||
session.lastpayload = None
|
||||
cls.waiting_sessions.pop(session,None)
|
||||
cls.waiting_sessions.pop(session, None)
|
||||
session._timedout()
|
||||
return len(cls.waiting_sessions)
|
||||
|
||||
@ -686,10 +691,10 @@ class Session:
|
||||
this class method will register that.
|
||||
|
||||
:param handle: filehandle too watch for input
|
||||
:param callback: function to call when input detected on the handle.
|
||||
:param callback: function to call when input detected on the handle.
|
||||
will receive the handle as an argument
|
||||
"""
|
||||
cls._external_handlers[handle.fileno()]=(callback,handle)
|
||||
cls._external_handlers[handle.fileno()] = (callback, handle)
|
||||
cls.poller.register(handle, select.POLLIN)
|
||||
|
||||
@classmethod
|
||||
@ -729,7 +734,7 @@ class Session:
|
||||
# copying pieces of the packet over and over
|
||||
rsp = list(struct.unpack("!%dB" % len(data), data))
|
||||
authcode = False
|
||||
if data[4] == '\x02': # we have an authcode in this ipmi 1.5 packet
|
||||
if data[4] == '\x02': # we have authcode in this ipmi 1.5 packet
|
||||
authcode = data[13:29]
|
||||
del rsp[13:29]
|
||||
# this is why we needed a mutable representation
|
||||
@ -738,7 +743,7 @@ class Session:
|
||||
expectedauthcode = self._ipmi15authcode(payload,
|
||||
checkremotecode=True)
|
||||
expectedauthcode = struct.pack("%dB" % len(expectedauthcode),
|
||||
*expectedauthcode)
|
||||
*expectedauthcode)
|
||||
if expectedauthcode != authcode:
|
||||
return
|
||||
self._parse_ipmi_payload(payload)
|
||||
@ -760,7 +765,7 @@ class Session:
|
||||
elif ptype == 0x15:
|
||||
return self._got_rakp4(data[16:])
|
||||
elif ptype == 0 or ptype == 1: # good old ipmi payload or sol
|
||||
# If I'm endorsing a shared secret scheme, then at the very least it
|
||||
# If endorsing a shared secret scheme, then at the very least it
|
||||
# needs to do mutual assurance
|
||||
if not (data[5] & 0b01000000): # This would be the line that might
|
||||
# trip up some insecure BMC
|
||||
@ -789,22 +794,22 @@ class Session:
|
||||
iv = rawdata[16:32]
|
||||
decrypter = AES.new(self.aeskey, AES.MODE_CBC, iv)
|
||||
decrypted = decrypter.decrypt(
|
||||
struct.pack("%dB" % len(payload[16:]),
|
||||
*payload[16:]))
|
||||
struct.pack("%dB" % len(payload[16:]),
|
||||
*payload[16:]))
|
||||
payload = struct.unpack("%dB" % len(decrypted), decrypted)
|
||||
padsize = payload[-1] + 1
|
||||
payload = list(payload[:-padsize])
|
||||
if ptype == 0:
|
||||
self._parse_ipmi_payload(payload)
|
||||
elif ptype == 1: #There should be no other option
|
||||
#note that we assume the SOL payload is good enough to avoid
|
||||
elif ptype == 1: # There should be no other option
|
||||
# note that we assume the SOL payload is good enough to avoid
|
||||
# retry SOL logic is sufficiently different, we just
|
||||
# defer that call to the sol handler, it can re submit if it
|
||||
# is unhappy
|
||||
if self.last_payload_type == 1: #but only if SOL was last sent
|
||||
if self.last_payload_type == 1: # but only if SOL was last tx
|
||||
self.lastpayload = None
|
||||
self.last_payload_type = None
|
||||
Session.waiting_sessions.pop(self,None)
|
||||
Session.waiting_sessions.pop(self, None)
|
||||
if self.sol_handler:
|
||||
self.sol_handler(payload)
|
||||
|
||||
@ -829,8 +834,8 @@ class Session:
|
||||
localsid = struct.unpack("<I", struct.pack("4B", *data[4:8]))[0]
|
||||
if self.localsid != localsid:
|
||||
return -9
|
||||
self.pendingsessionid = struct.unpack("<I",
|
||||
struct.pack("4B", *data[8:12]))[0]
|
||||
self.pendingsessionid = struct.unpack(
|
||||
"<I", struct.pack("4B", *data[8:12]))[0]
|
||||
# TODO(jbjohnso): currently, we take it for granted that the responder
|
||||
# accepted our integrity/auth/confidentiality proposal
|
||||
self.lastpayload = None
|
||||
@ -841,11 +846,12 @@ class Session:
|
||||
self.randombytes = os.urandom(16)
|
||||
userlen = len(self.userid)
|
||||
payload = [self.rmcptag, 0, 0, 0] + \
|
||||
list(struct.unpack("4B", struct.pack("<I", self.pendingsessionid))) +\
|
||||
list(struct.unpack("16B", self.randombytes)) +\
|
||||
[self.privlevel, 0, 0] +\
|
||||
[userlen] +\
|
||||
list(struct.unpack("%dB" % userlen, self.userid))
|
||||
list(struct.unpack("4B",
|
||||
struct.pack("<I", self.pendingsessionid))) +\
|
||||
list(struct.unpack("16B", self.randombytes)) +\
|
||||
[self.privlevel, 0, 0] +\
|
||||
[userlen] +\
|
||||
list(struct.unpack("%dB" % userlen, self.userid))
|
||||
self.sessioncontext = "EXPECTINGRAKP2"
|
||||
self.send_payload(
|
||||
payload=payload, payload_type=constants.payload_types['rakp1'])
|
||||
@ -948,10 +954,11 @@ class Session:
|
||||
expectedauthcode = HMAC.new(self.sik, hmacdata, SHA).digest()[:12]
|
||||
authcode = struct.pack("%dB" % len(data[8:]), *data[8:])
|
||||
if authcode != expectedauthcode:
|
||||
call_with_optional_args(self.onlogon,
|
||||
{'error':
|
||||
"Invalid RAKP4 integrity code (wrong Kg?)"},
|
||||
self.onlogonargs)
|
||||
call_with_optional_args(
|
||||
self.onlogon,
|
||||
{'error':
|
||||
"Invalid RAKP4 integrity code (wrong Kg?)"},
|
||||
self.onlogonargs)
|
||||
return
|
||||
self.sessionid = self.pendingsessionid
|
||||
self.integrityalgo = 'sha1'
|
||||
@ -970,28 +977,28 @@ class Session:
|
||||
# TODO(jbjohnso): if implementing other channels, add checksum checks
|
||||
# here
|
||||
if (payload[4] != self.seqlun or
|
||||
payload[1] >> 2 != self.expectednetfn or
|
||||
payload[5] != self.expectedcmd):
|
||||
return -1 # this payload is not a match for our outstanding packet
|
||||
payload[1] >> 2 != self.expectednetfn or
|
||||
payload[5] != self.expectedcmd):
|
||||
return -1 # payload is not a match for our last packet
|
||||
if hasattr(self, 'hasretried') and self.hasretried:
|
||||
self.hasretried = 0
|
||||
self.tabooseq[
|
||||
(self.expectednetfn, self.expectedcmd, self.seqlun)] = 16
|
||||
# try to skip it for at most 16 cycles of overflow
|
||||
# We want to now remember that we do not have an expected packet
|
||||
self.expectednetfn = 0x1ff # bigger than one byte means
|
||||
#it can never match the one byte value by mistake
|
||||
self.expectednetfn = 0x1ff # bigger than one byte means it can never
|
||||
# match the one byte value by mistake
|
||||
self.expectedcmd = 0x1ff
|
||||
self.seqlun += 4 # prepare seqlun for next transmit
|
||||
self.seqlun &= 0xff # when overflowing, wrap around
|
||||
Session.waiting_sessions.pop(self,None)
|
||||
Session.waiting_sessions.pop(self, None)
|
||||
self.lastpayload = None # render retry mechanism utterly incapable of
|
||||
#doing anything, though it shouldn't matter
|
||||
# doing anything, though it shouldn't matter
|
||||
self.last_payload_type = None
|
||||
response = {}
|
||||
response['netfn'] = payload[1] >> 2
|
||||
del payload[0:5]
|
||||
# remove header of rsaddr/netfn/lun/checksum/rq/seq/lun
|
||||
# ^^ remove header of rsaddr/netfn/lun/checksum/rq/seq/lun
|
||||
del payload[-1] # remove the trailing checksum
|
||||
response['command'] = payload[0]
|
||||
response['code'] = payload[1]
|
||||
@ -1003,7 +1010,7 @@ class Session:
|
||||
self.pendingpayloads.popleft()
|
||||
self.send_payload(payload=nextpayload,
|
||||
payload_type=nextpayloadtype,
|
||||
retry=nextretry)
|
||||
retry=retry)
|
||||
call_with_optional_args(self.ipmicallback,
|
||||
response,
|
||||
self.ipmicallbackargs)
|
||||
@ -1036,7 +1043,7 @@ class Session:
|
||||
self._relog()
|
||||
else: # in IPMI case, the only recourse is to act as if the packet is
|
||||
# idempotent. SOL has more sophisticated retry handling
|
||||
# the biggest risks are reset sp, which is often fruitless to retry
|
||||
# the biggest risks are reset sp which is often fruitless to retry
|
||||
# and chassis reset, which sometimes will shoot itself
|
||||
# systematically in the head in a shared port case making replies
|
||||
# impossible
|
||||
@ -1059,7 +1066,7 @@ class Session:
|
||||
Session.waiting_sessions[self] = {}
|
||||
Session.waiting_sessions[self]['ipmisession'] = self
|
||||
Session.waiting_sessions[self]['timeout'] = self.timeout + \
|
||||
time.time()
|
||||
time.time()
|
||||
Session.pending += 1
|
||||
if self.sockaddr:
|
||||
Session.socket.sendto(self.netpacket, self.sockaddr)
|
||||
@ -1070,7 +1077,7 @@ class Session:
|
||||
0,
|
||||
socket.SOCK_DGRAM):
|
||||
sockaddr = res[4]
|
||||
if (res[0] == socket.AF_INET): #convert the sockaddr AF_INET6
|
||||
if (res[0] == socket.AF_INET): # convert the sockaddr AF_INET6
|
||||
newhost = '::ffff:' + sockaddr[0]
|
||||
sockaddr = (newhost, sockaddr[1], 0, 0)
|
||||
Session.bmc_handlers[sockaddr] = self
|
||||
@ -1088,7 +1095,7 @@ class Session:
|
||||
self.raw_command(command=0x3c,
|
||||
netfn=6,
|
||||
data=struct.unpack("4B",
|
||||
struct.pack("I", self.sessionid)),
|
||||
struct.pack("I", self.sessionid)),
|
||||
retry=False,
|
||||
callback=callback,
|
||||
callback_args=callback_args)
|
||||
|
@ -38,12 +38,12 @@ if len(sys.argv) >= 5:
|
||||
args = sys.argv[4:]
|
||||
ipmicmd = Command(bmc=bmc, userid=userid, password=password)
|
||||
if command == 'power':
|
||||
if args[0]:
|
||||
if args:
|
||||
print ipmicmd.set_power(args[0], wait=True)
|
||||
else:
|
||||
print ipmicmd.get_power()
|
||||
elif command == 'bootdev':
|
||||
if args[0]:
|
||||
if args:
|
||||
print ipmicmd.set_bootdev(args[0])
|
||||
else:
|
||||
print ipmicmd.get_bootdev()
|
||||
|
@ -20,16 +20,14 @@ limitations under the License.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import fcntl
|
||||
|
||||
import tty
|
||||
import termios
|
||||
import tty
|
||||
|
||||
from ipmi import console
|
||||
|
||||
tcattr = termios.tcgetattr(sys.stdin)
|
||||
newtcattr = tcattr
|
||||
#TODO: allow ctrl-c and crtl-z to go to remote console, add our own exit handler
|
||||
#TODO(jbjohnso): add our exit handler
|
||||
newtcattr[-1][termios.VINTR] = 0
|
||||
newtcattr[-1][termios.VSUSP] = 0
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, newtcattr)
|
||||
|
Loading…
x
Reference in New Issue
Block a user