2
0
mirror of https://opendev.org/x/pyghmi synced 2026-03-28 22:03:30 +00:00

Merge "Improve pyghmi performance"

This commit is contained in:
Zuul
2018-07-26 18:50:17 +00:00
committed by Gerrit Code Review
3 changed files with 64 additions and 80 deletions

View File

@@ -412,7 +412,7 @@ class Command(object):
retry=retry, timeout=timeout)
if 'error' in rsp:
raise exc.IpmiException(rsp['error'], rsp['code'])
rsp['data'] = buffer(bytearray(rsp['data']))
rsp['data'] = buffer(rsp['data'])
return rsp
def raw_command(self, netfn, command, bridge_request=(), data=(),
@@ -434,10 +434,13 @@ class Command(object):
:param timeout: A custom amount of time to wait for initial reply
:returns: dict -- The response from IPMI device
"""
return self.ipmi_session.raw_command(netfn=netfn, command=command,
bridge_request=bridge_request,
data=data, delay_xmit=delay_xmit,
retry=retry, timeout=timeout)
rsp = self.ipmi_session.raw_command(netfn=netfn, command=command,
bridge_request=bridge_request,
data=data, delay_xmit=delay_xmit,
retry=retry, timeout=timeout)
if 'data' in rsp:
rsp['data'] = list(rsp['data'])
return rsp
def get_power(self):
"""Get current power state of the managed system

View File

@@ -258,14 +258,12 @@ class Console(object):
breakbyte = 0
if sendbreak:
breakbyte = 0b10000
payload = struct.pack("BBBB", self.myseq, 0, 0, breakbyte)
payload += output
payload = bytearray((self.myseq, 0, 0, breakbyte)) + output
self.lasttextsize = len(output)
needskeepalive = False
if self.lasttextsize == 0:
needskeepalive = True
self.awaitingack = True
payload = struct.unpack("%dB" % len(payload), payload)
self.lastpayload = payload
self.send_payload(payload, retry=False, needskeepalive=needskeepalive)
retries = 5
@@ -344,10 +342,10 @@ class Console(object):
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:])
remdata = bytes(payload[4:])
if newseq == self.remseq: # it is a retry, but could have new data
if remdatalen > self.lastsize:
remdata = remdata[4 + self.lastsize:]
remdata = bytes(remdata[4 + self.lastsize:])
else: # no new data...
remdata = ""
else: # TODO(jbjohnso) what if remote sequence number is wrong??
@@ -355,7 +353,7 @@ class Console(object):
self.lastsize = remdatalen
if remdata: # Do not subject callers to empty data
self._print_data(remdata)
ackpayload = (0, self.remseq, remdatalen, 0)
ackpayload = bytearray((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
@@ -376,7 +374,6 @@ class Console(object):
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:]
newtext = struct.pack("B"*len(newtext), *newtext)
with self.outputlock:
if (self.pendingoutput and
not isinstance(self.pendingoutput[0], dict)):
@@ -474,16 +471,16 @@ class ServerConsole(Console):
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:])
remdata = bytes(payload[4:])
if newseq == self.remseq: # it is a retry, but could have new data
if remdatalen > self.lastsize:
remdata = remdata[4 + self.lastsize:]
remdata = bytes(remdata[4 + self.lastsize:])
else: # no new data...
remdata = ""
else: # TODO(jbjohnso) what if remote sequence number is wrong??
self.remseq = newseq
self.lastsize = remdatalen
ackpayload = (0, self.remseq, remdatalen, flag)
ackpayload = bytearray((0, self.remseq, remdatalen, flag))
# 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
@@ -499,7 +496,6 @@ class ServerConsole(Console):
self.awaitingack = False
if nacked and not breakdetected: # the BMC was in some way unhappy
newtext = self.lastpayload[4 + ackcount:]
newtext = struct.pack("B"*len(newtext), *newtext)
with self.outputlock:
if (self.pendingoutput and
not isinstance(self.pendingoutput[0], dict)):

View File

@@ -248,18 +248,18 @@ def _aespad(data):
"""ipmi demands a certain pad scheme,
per table 13-20 AES-CBC encrypted payload fields.
"""
newdata = list(data)
currlen = len(data) + 1 # need to count the pad length field as well
neededpad = currlen % 16
if neededpad: # if it happens to be zero, hurray, but otherwise invert the
# sense of the padding
neededpad = 16 - neededpad
padval = 1
pad = bytearray(neededpad)
while padval <= neededpad:
newdata.append(padval)
pad[padval - 1] = padval
padval += 1
newdata.append(neededpad)
return newdata
pad.append(neededpad)
return pad
def _checksum(*data): # Two's complement over the data
@@ -628,16 +628,16 @@ class Session(object):
"""This function generate message for bridge request. It is a
part of ipmi payload.
"""
head = [constants.IPMI_BMC_ADDRESS,
constants.netfn_codes['application'] << 2]
head = bytearray((constants.IPMI_BMC_ADDRESS,
constants.netfn_codes['application'] << 2))
check_sum = _checksum(*head)
# NOTE(fengqian): according IPMI Figure 14-11, rqSWID is set to 81h
boday = [0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD,
0x40 | channel]
boday = bytearray((0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD,
0x40 | channel))
# NOTE(fengqian): Track request
self._add_request_entry((constants.netfn_codes['application'] + 1,
self.seqlun, constants.IPMI_SEND_MESSAGE_CMD))
return head + [check_sum] + boday
return head + bytearray((check_sum,)) + boday
def _add_request_entry(self, entry=()):
"""This function record the request with netfn, sequence number and
@@ -691,12 +691,12 @@ class Session(object):
# figure 13-4, first two bytes are rsaddr and
# netfn, for non-bridge request, rsaddr is always 0x20 since we are
# addressing BMC while rsaddr is specified forbridge request
header = [rsaddr, netfn << 2]
header = bytearray((rsaddr, netfn << 2))
reqbody = [rqaddr, self.seqlun, command] + list(data)
headsum = _checksum(*header)
bodysum = _checksum(*reqbody)
payload = header + [headsum] + reqbody + [bodysum]
reqbody = bytearray((rqaddr, self.seqlun, command)) + data
headsum = bytearray((_checksum(*header),))
bodysum = bytearray((_checksum(*reqbody),))
payload = header + headsum + reqbody + bodysum
if bridge_request:
payload = bridge_msg + payload
# NOTE(fengqian): For bridge request, another check sum is needed.
@@ -799,11 +799,13 @@ class Session(object):
if retry is None:
retry = not self.servermode
if self.servermode:
data = [code] + list(data)
data = bytearray((code,)) + bytearray(data)
if netfn is None:
netfn = self.clientnetfn
if command is None:
command = self.clientcommand
else:
data = bytearray(data)
ipmipayload = self._make_ipmi_payload(netfn, command, bridge_request,
data)
payload_type = constants.payload_types['ipmi']
@@ -835,10 +837,12 @@ class Session(object):
payload_type = self.last_payload_type
if not payload:
payload = self.lastpayload
message = [0x6, 0, 0xff, 0x07] # constant RMCP header for IPMI
message = bytearray(b'\x06\x00\xff\x07') # constant IPMI RMCP header
if retry:
self.lastpayload = payload
self.last_payload_type = payload_type
if not isinstance(payload, bytearray):
payload = bytearray(payload)
message.append(self.authtype)
baretype = payload_type
if self.integrityalgo:
@@ -853,10 +857,10 @@ class Session(object):
elif baretype not in constants.payload_types.values():
raise NotImplementedError(
"Unrecognized payload type %d" % baretype)
message += struct.unpack("!4B", struct.pack("<I", self.sessionid))
message += struct.unpack("!4B", struct.pack("<I", self.sequencenumber))
message += struct.pack("<I", self.sessionid)
message += struct.pack("<I", self.sequencenumber)
if self.ipmiversion == 1.5:
message += struct.unpack("!4B", struct.pack("<I", self.sessionid))
message += struct.pack("<I", self.sessionid)
if not self.authtype == 0:
message += self._ipmi15authcode(payload)
message.append(len(payload))
@@ -880,23 +884,20 @@ class Session(object):
message.append(newpsize & 0xff)
message.append(newpsize >> 8)
iv = os.urandom(16)
message += list(struct.unpack("16B", iv))
payloadtocrypt = _aespad(payload)
message += iv
payloadtocrypt = bytes(payload + _aespad(payload))
crypter = Cipher(
algorithm=algorithms.AES(self.aeskey),
mode=modes.CBC(iv),
backend=self._crypto_backend
)
encryptor = crypter.encryptor()
plaintext = struct.pack("%dB" % len(payloadtocrypt),
*payloadtocrypt)
crypted = encryptor.update(plaintext) + encryptor.finalize()
crypted = list(struct.unpack("%dB" % len(crypted), crypted))
message += crypted
message += encryptor.update(payloadtocrypt
) + encryptor.finalize()
else: # no confidetiality algorithm
message.append(psize & 0xff)
message.append(psize >> 8)
message += list(payload)
message += payload
if self.integrityalgo: # see table 13-8,
# RMCP+ packet format
# TODO(jbjohnso): SHA256 which is now
@@ -904,18 +905,15 @@ class Session(object):
neededpad = (len(message) - 2) % 4
if neededpad:
neededpad = 4 - neededpad
message += [0xff] * neededpad
message += b'\xff' * neededpad
message.append(neededpad)
message.append(7) # reserved, 7 is the required value for the
# specification followed
integdata = message[4:]
authcode = hmac.new(self.k1,
struct.pack("%dB" % len(integdata),
*integdata),
message += hmac.new(self.k1,
bytes(message[4:]),
hashlib.sha1).digest()[:12] # SHA1-96
# per RFC2404 truncates to 96 bits
message += struct.unpack("12B", authcode)
self.netpacket = struct.pack("!%dB" % len(message), *message)
self.netpacket = message
# advance idle timer since we don't need keepalive while sending
# packets out naturally
with util.protect(KEEPALIVE_SESSIONS):
@@ -937,19 +935,14 @@ class Session(object):
if padneeded < 0:
raise exc.IpmiException("Password is too long for ipmi 1.5")
password += '\x00' * padneeded
passdata = struct.unpack("16B", password)
if checkremotecode:
seqbytes = struct.unpack("!4B",
struct.pack("<I", self.remsequencenumber))
seqbytes = struct.pack("<I", self.remsequencenumber)
else:
seqbytes = struct.unpack("!4B",
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()
hashdata = struct.unpack("!%dB" % len(dgst), dgst)
return hashdata
seqbytes = struct.pack("<I", self.sequencenumber)
sessdata = struct.pack("<I", self.sessionid)
bodydata = password + sessdata + payload + seqbytes + password
dgst = hashlib.md5(bodydata).digest()
return dgst
def _got_channel_auth_cap(self, response):
if 'error' in response:
@@ -1290,8 +1283,8 @@ class Session(object):
# things off ignore the second reply since we have one
# satisfactory answer
if data[4] in (0, 2): # This is an ipmi 1.5 paylod
remsequencenumber = struct.unpack('<I', bytes(data[5:9]))[0]
remsessid = struct.unpack("<I", bytes(data[9:13]))[0]
remsequencenumber = struct.unpack('<I', data[5:9])[0]
remsessid = struct.unpack("<I", data[9:13])[0]
if (remsequencenumber == 0 and remsessid == 0 and
qent[2] in Session.bmc_handlers):
# So a new ipmi client happens to get a previously seen and
@@ -1314,20 +1307,15 @@ class Session(object):
if remsessid != self.sessionid:
return -1 # does not match our session id, drop it
# now we need a mutable representation of the packet, rather than
# copying pieces of the packet over and over
rsp = list(struct.unpack("!%dB" % len(data), bytes(data)))
authcode = False
if data[4] == 2: # we have authcode in this ipmi 1.5 packet
authcode = data[13:29]
del rsp[13:29]
del data[13:29]
# this is why we needed a mutable representation
payload = list(rsp[14:14 + rsp[13]])
payload = data[14:14 + data[13]]
if authcode:
expectedauthcode = self._ipmi15authcode(payload,
checkremotecode=True)
expectedauthcode = struct.pack("%dB" % len(expectedauthcode),
*expectedauthcode)
if expectedauthcode != authcode:
return
self._parse_ipmi_payload(payload)
@@ -1377,13 +1365,13 @@ class Session(object):
if self.k1 is None: # we are in no shape to process a packet now
return
expectedauthcode = hmac.new(
self.k1, bytes(data[4:-12]), hashlib.sha1).digest()[:12]
self.k1, data[4:-12], hashlib.sha1).digest()[:12]
if authcode != expectedauthcode:
return # BMC failed to assure integrity to us, drop it
sid = struct.unpack("<I", bytes(data[6:10]))[0]
sid = struct.unpack("<I", data[6:10])[0]
if sid != self.localsid: # session id mismatch, drop it
return
remseqnumber = struct.unpack("<I", bytes(data[10:14]))[0]
remseqnumber = struct.unpack("<I", data[10:14])[0]
if (hasattr(self, 'remseqnumber') and
(remseqnumber < self.remseqnumber) and
(self.remseqnumber != 0xffffffff)):
@@ -1399,12 +1387,10 @@ class Session(object):
backend=self._crypto_backend
)
decryptor = crypter.decryptor()
ciphertext = struct.pack("%dB" % len(payload[16:]),
*payload[16:])
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
payload = struct.unpack("%dB" % len(decrypted), decrypted)
payload = bytearray(decryptor.update(bytes(payload[16:])
) + decryptor.finalize())
padsize = payload[-1] + 1
payload = list(payload[:-padsize])
payload = payload[:-padsize]
if ptype == 0:
self._parse_ipmi_payload(payload)
elif ptype == 1: # There should be no other option
@@ -1654,8 +1640,7 @@ class Session(object):
# doing anything, though it shouldn't matter
self.lastpayload = None
self.last_payload_type = None
response = {}
response['netfn'] = payload[1] >> 2
response = {'netfn': payload[1] >> 2}
# ^^ remove header of rsaddr/netfn/lun/checksum/rq/seq/lun
del payload[0:5]
# remove the trailing checksum