From aa1bc4d183d060f850d5fde0ba9324e4e1afb392 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 3 Mar 2020 08:47:02 -0500 Subject: [PATCH] Rework LUN support Make code more closely resemble the spec mostly for readability. Change-Id: I89e229645b6cea942f1dca6082f57083a935955a --- pyghmi/ipmi/command.py | 12 +++---- pyghmi/ipmi/private/localsession.py | 4 +-- pyghmi/ipmi/private/session.py | 47 +++++++++++++++------------- pyghmi/ipmi/private/simplesession.py | 40 ++++++++++++----------- pyghmi/ipmi/sdr.py | 2 +- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index a401b35e..9442dc7c 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -430,7 +430,7 @@ class Command(object): return {'bootdev': bootdev} def xraw_command(self, netfn, command, bridge_request=(), data=(), - delay_xmit=None, retry=True, timeout=None, lun=0): + delay_xmit=None, retry=True, timeout=None, rslun=0): """Send raw ipmi command to BMC, raising exception on error This is identical to raw_command, except it raises exceptions @@ -454,7 +454,7 @@ class Command(object): bridge_request=bridge_request, data=data, delay_xmit=delay_xmit, retry=retry, timeout=timeout, - lun=lun) + rslun=rslun) if 'error' in rsp: raise exc.IpmiException(rsp['error'], rsp['code']) rsp['data'] = buffer(rsp['data']) @@ -479,7 +479,7 @@ class Command(object): return self._oem.get_description() def raw_command(self, netfn, command, bridge_request=(), data=(), - delay_xmit=None, retry=True, timeout=None, lun=0): + delay_xmit=None, retry=True, timeout=None, rslun=0): """Send raw ipmi command to BMC This allows arbitrary IPMI bytes to be issued. This is commonly used @@ -501,7 +501,7 @@ class Command(object): bridge_request=bridge_request, data=data, delay_xmit=delay_xmit, retry=retry, timeout=timeout, - lun=lun) + rslun=rslun) return rsp def get_power(self): @@ -749,7 +749,7 @@ class Command(object): if self._sdr.sensors[sensor].name == sensorname: currsensor = self._sdr.sensors[sensor] rsp = self.raw_command(command=0x2d, netfn=4, - lun=currsensor.sensor_lun, + rslun=currsensor.sensor_lun, data=(currsensor.sensor_number,)) if 'error' in rsp: raise exc.IpmiException(rsp['error'], rsp['code']) @@ -964,7 +964,7 @@ class Command(object): for sensor in self._sdr.get_sensor_numbers(): currsensor = self._sdr.sensors[sensor] rsp = self.raw_command(command=0x2d, netfn=4, - lun=currsensor.sensor_lun, + rslun=currsensor.sensor_lun, data=(currsensor.sensor_number,)) if 'error' in rsp: if rsp['code'] == 203: # Sensor does not exist, optional dev diff --git a/pyghmi/ipmi/private/localsession.py b/pyghmi/ipmi/private/localsession.py index dfa9be13..eb816403 100644 --- a/pyghmi/ipmi/private/localsession.py +++ b/pyghmi/ipmi/private/localsession.py @@ -113,10 +113,10 @@ class Session(object): retry=True, delay_xmit=None, timeout=None, - waitall=False, lun=0): + waitall=False, rslun=0): self.addr.channel = CURRCHAN self.addr.addr_type = ADDRTYPE - self.addr.lun = lun + self.addr.lun = rslun self.req.addr_len = ctypes.sizeof(IpmiSystemInterfaceAddr) self.req.addr = ctypes.pointer(self.addr) self.req.msg.netfn = netfn diff --git a/pyghmi/ipmi/private/session.py b/pyghmi/ipmi/private/session.py index c685c730..937d2907 100644 --- a/pyghmi/ipmi/private/session.py +++ b/pyghmi/ipmi/private/session.py @@ -598,6 +598,7 @@ class Session(object): self.ipmiversion = 1.5 self.timeout = initialtimeout + (0.5 * random.random()) self.logoutexpiry = _monotonic_time() + self._getmaxtimeout() + self.rqlun = 0 self.seqlun = 0 # NOTE(jbjohnso): per IPMI table 5-4, software ids in the ipmi spec may # be 0x81 through 0x8d. We'll stick with 0x81 for now, @@ -626,12 +627,14 @@ class Session(object): part of ipmi payload. """ + # NOTE(puwen): need to pay attention for this function because the + # structure does not seem to match the specifications. 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 = bytearray((0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD, - 0x40 | channel)) + boday = bytearray((0x81, (self.seqlun << 2) | self.rqlun, + 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)) @@ -654,7 +657,7 @@ class Session(object): self.request_entry.remove(entry) def _make_ipmi_payload(self, netfn, command, bridge_request=None, data=(), - lun=0): + rslun=0): """This function generates the core ipmi payload that would be applicable for any channel (including KCS) @@ -672,8 +675,8 @@ class Session(object): self.tabooseq[(netfn, command, self.seqlun)] and seqincrement): self.tabooseq[(self.expectednetfn, command, self.seqlun)] -= 1 # Allow taboo to eventually expire after a few rounds - self.seqlun += 4 # the last two bits are lun, so add 4 to add 1 - self.seqlun &= 0xff # we only have one byte, wrap when exceeded + self.seqlun += 1 # the last two bits are lun, so add 4 to add 1 + self.seqlun &= 0x3f # we only have one byte, wrap when exceeded seqincrement -= 1 if bridge_request: @@ -692,8 +695,9 @@ 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 = bytearray((rsaddr, (netfn << 2) | lun)) - reqbody = bytearray((rqaddr, self.seqlun, command)) + data + header = bytearray((rsaddr, (netfn << 2) | rslun)) + reqbody = bytearray( + (rqaddr, (self.seqlun << 2) | self.rqlun, command)) + data headsum = bytearray((_checksum(*header),)) bodysum = bytearray((_checksum(*reqbody),)) payload = header + headsum + reqbody + bodysum @@ -757,7 +761,7 @@ class Session(object): delay_xmit=None, timeout=None, callback=None, - lun=0): + rslun=0): if not self.logged: if (self.logoutexpiry is not None and _monotonic_time() > self.logoutexpiry): @@ -775,7 +779,7 @@ class Session(object): self._send_ipmi_net_payload(netfn, command, data, bridge_request=bridge_request, retry=retry, delay_xmit=delay_xmit, - timeout=timeout, lun=lun) + timeout=timeout, rslun=rslun) if retry: # in retry case, let the retry timers indicate wait time timeout = None @@ -802,7 +806,7 @@ class Session(object): def _send_ipmi_net_payload(self, netfn=None, command=None, data=(), code=0, bridge_request=None, retry=None, delay_xmit=None, timeout=None, - lun=0): + rslun=0): if retry is None: retry = not self.servermode if self.servermode: @@ -814,7 +818,7 @@ class Session(object): else: data = bytearray(data) ipmipayload = self._make_ipmi_payload(netfn, command, bridge_request, - data, lun) + data, rslun) payload_type = constants.payload_types['ipmi'] self.send_payload(payload=ipmipayload, payload_type=payload_type, retry=retry, delay_xmit=delay_xmit, timeout=timeout) @@ -943,7 +947,7 @@ class Session(object): raise exc.IpmiException("Password is too long for ipmi 1.5") password += '\x00' * padneeded if checkremotecode: - seqbytes = struct.pack("> 2 self.clientaddr = payload[3] self.clientnetfn = (payload[1] >> 2) + 1 self.clientcommand = payload[5] self._parse_payload(payload) return - entry = (payload[1] >> 2, payload[4] & 0xfc, payload[5]) + # payload[4] is rqSeq + rsLUN, we only need the rqSeq + entry = (payload[1] >> 2, payload[4] >> 2, payload[5]) if self._lookup_request_entry(entry): self._remove_request_entry(entry) @@ -1661,8 +1666,8 @@ class Session(object): self.expectednetfn = 0x1ff self.expectedcmd = 0x1ff if not self.servermode: - self.seqlun += 4 # prepare seqlun for next transmit - self.seqlun &= 0xff # when overflowing, wrap around + self.seqlun += 1 # prepare seqlun for next transmit + self.seqlun &= 0x3f # when overflowing, wrap around with util.protect(WAITING_SESSIONS): Session.waiting_sessions.pop(self, None) # render retry mechanism utterly incapable of diff --git a/pyghmi/ipmi/private/simplesession.py b/pyghmi/ipmi/private/simplesession.py index 5d3b41cd..8987919d 100644 --- a/pyghmi/ipmi/private/simplesession.py +++ b/pyghmi/ipmi/private/simplesession.py @@ -296,6 +296,7 @@ class Session(object): self.ipmiversion = 1.5 self.timeout = initialtimeout + (0.5 * random.random()) self.logoutexpiry = _monotonic_time() + self._getmaxtimeout() + self.rqlun = 0 self.seqlun = 0 # NOTE(jbjohnso): per IPMI table 5-4, software ids in the ipmi spec may # be 0x81 through 0x8d. We'll stick with 0x81 for now, @@ -328,8 +329,8 @@ class Session(object): constants.netfn_codes['application'] << 2)) check_sum = _checksum(*head) # NOTE(fengqian): according IPMI Figure 14-11, rqSWID is set to 81h - boday = bytearray((0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD, - 0x40 | channel)) + boday = bytearray((0x81, (self.seqlun << 2) | self.rqlun, + 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)) @@ -352,7 +353,7 @@ class Session(object): self.request_entry.remove(entry) def _make_ipmi_payload(self, netfn, command, bridge_request=None, data=(), - lun=0): + rslun=0): """This function generates the core ipmi payload that would be applicable for any channel (including KCS) @@ -370,8 +371,8 @@ class Session(object): self.tabooseq[(netfn, command, self.seqlun)] and seqincrement): self.tabooseq[(self.expectednetfn, command, self.seqlun)] -= 1 # Allow taboo to eventually expire after a few rounds - self.seqlun += 4 # the last two bits are lun, so add 4 to add 1 - self.seqlun &= 0xff # we only have one byte, wrap when exceeded + self.seqlun += 1 # the last two bits are lun, so add 4 to add 1 + self.seqlun &= 0x3f # we only have one byte, wrap when exceeded seqincrement -= 1 if bridge_request: @@ -390,9 +391,10 @@ 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 = bytearray((rsaddr, (netfn << 2) | lun)) + header = bytearray((rsaddr, (netfn << 2) | rslun)) - reqbody = bytearray((rqaddr, self.seqlun, command)) + data + reqbody = bytearray(( + rqaddr, (self.seqlun << 2) | self.rqlun, command)) + data headsum = bytearray((_checksum(*header),)) bodysum = bytearray((_checksum(*reqbody),)) payload = header + headsum + reqbody + bodysum @@ -451,7 +453,7 @@ class Session(object): delay_xmit=None, timeout=None, callback=None, - lun=0): + rslun=0): if not self.logged: if (self.logoutexpiry is not None and _monotonic_time() > self.logoutexpiry): @@ -469,7 +471,7 @@ class Session(object): self._send_ipmi_net_payload(netfn, command, data, bridge_request=bridge_request, retry=retry, delay_xmit=delay_xmit, - timeout=timeout, lun=lun) + timeout=timeout, rslun=rslun) if retry: # in retry case, let the retry timers indicate wait time timeout = None @@ -496,7 +498,7 @@ class Session(object): def _send_ipmi_net_payload(self, netfn=None, command=None, data=(), code=0, bridge_request=None, retry=None, delay_xmit=None, timeout=None, - lun=0): + rslun=0): if retry is None: retry = not self.servermode if self.servermode: @@ -508,7 +510,7 @@ class Session(object): else: data = bytearray(data) ipmipayload = self._make_ipmi_payload(netfn, command, bridge_request, - data, lun) + data, rslun) payload_type = constants.payload_types['ipmi'] self.send_payload(payload=ipmipayload, payload_type=payload_type, retry=retry, delay_xmit=delay_xmit, timeout=timeout) @@ -632,7 +634,7 @@ class Session(object): raise exc.IpmiException("Password is too long for ipmi 1.5") password += '\x00' * padneeded if checkremotecode: - seqbytes = struct.pack("> 2, payload[4], payload[5]) + entry = (payload[1] >> 2, payload[4] >> 2, payload[5]) if self._lookup_request_entry(entry): self._remove_request_entry(entry) @@ -1251,8 +1253,8 @@ class Session(object): self.expectednetfn = 0x1ff self.expectedcmd = 0x1ff if not self.servermode: - self.seqlun += 4 # prepare seqlun for next transmit - self.seqlun &= 0xff # when overflowing, wrap around + self.seqlun += 1 # prepare seqlun for next transmit + self.seqlun &= 0x3f # when overflowing, wrap around # render retry mechanism utterly incapable of # doing anything, though it shouldn't matter self.lastpayload = None diff --git a/pyghmi/ipmi/sdr.py b/pyghmi/ipmi/sdr.py index 8e816d43..c141d268 100644 --- a/pyghmi/ipmi/sdr.py +++ b/pyghmi/ipmi/sdr.py @@ -349,7 +349,7 @@ class SDREntry(object): # event only, compact and full are very similar # this function handles the common aspects of compact and full # offsets from spec, minus 6 - self.sensor_lun = entry[1] + self.sensor_lun = entry[1] & 0x03 self.sensor_number = entry[2] self.entity = ipmiconst.entity_ids.get( entry[3], 'Unknown entity {0}'.format(entry[3]))