mirror of
https://opendev.org/x/pyghmi
synced 2025-02-20 12:30:48 +00:00
Merge "Add BMC bridge request extension"
This commit is contained in:
commit
ba5bbe1fc4
@ -122,9 +122,7 @@ class Command(object):
|
||||
|
||||
:returns: dict --The response will be provided in the return as a dict
|
||||
"""
|
||||
response = self.ipmi_session.raw_command(netfn=0,
|
||||
command=9,
|
||||
data=(5, 0, 0))
|
||||
response = self.raw_command(netfn=0, command=9, data=(5, 0, 0))
|
||||
# interpret response per 'get system boot options'
|
||||
if 'error' in response:
|
||||
return response
|
||||
@ -167,7 +165,7 @@ class Command(object):
|
||||
raise exc.InvalidParameterValue(
|
||||
"Unknown power state %s requested" % powerstate)
|
||||
self.newpowerstate = powerstate
|
||||
response = self.ipmi_session.raw_command(netfn=0, command=1)
|
||||
response = self.raw_command(netfn=0, command=1)
|
||||
if 'error' in response:
|
||||
raise exc.IpmiException(response['error'])
|
||||
self.powerstate = 'on' if (response['data'][0] & 1) else 'off'
|
||||
@ -175,7 +173,7 @@ class Command(object):
|
||||
return {'powerstate': self.powerstate}
|
||||
if self.newpowerstate == 'boot':
|
||||
self.newpowerstate = 'on' if self.powerstate == 'off' else 'reset'
|
||||
response = self.ipmi_session.raw_command(
|
||||
response = self.raw_command(
|
||||
netfn=0, command=2, data=[power_states[self.newpowerstate]])
|
||||
if 'error' in response:
|
||||
raise exc.IpmiException(response['error'])
|
||||
@ -191,8 +189,7 @@ class Command(object):
|
||||
self.waitpowerstate = self.newpowerstate
|
||||
currpowerstate = None
|
||||
while currpowerstate != self.waitpowerstate and waitattempts > 0:
|
||||
response = self.ipmi_session.raw_command(netfn=0, command=1,
|
||||
delay_xmit=1)
|
||||
response = self.raw_command(netfn=0, command=1, delay_xmit=1)
|
||||
if 'error' in response:
|
||||
return response
|
||||
currpowerstate = 'on' if (response['data'][0] & 1) else 'off'
|
||||
@ -243,8 +240,7 @@ class Command(object):
|
||||
# then move on to set chassis capabilities
|
||||
self.requestpending = True
|
||||
# Set System Boot Options is netfn=0, command=8, data
|
||||
response = self.ipmi_session.raw_command(netfn=0, command=8,
|
||||
data=(3, 8))
|
||||
response = self.raw_command(netfn=0, command=8, data=(3, 8))
|
||||
self.lastresponse = response
|
||||
if 'error' in response:
|
||||
return response
|
||||
@ -256,12 +252,12 @@ class Command(object):
|
||||
if self.bootdev == 0:
|
||||
bootflags = 0
|
||||
data = (5, bootflags, self.bootdev, 0, 0, 0)
|
||||
response = self.ipmi_session.raw_command(netfn=0, command=8, data=data)
|
||||
response = self.raw_command(netfn=0, command=8, data=data)
|
||||
if 'error' in response:
|
||||
return response
|
||||
return {'bootdev': bootdev}
|
||||
|
||||
def raw_command(self, netfn, command, data=()):
|
||||
def raw_command(self, netfn, command, bridge_request={}, data=()):
|
||||
"""Send raw ipmi command to BMC
|
||||
|
||||
This allows arbitrary IPMI bytes to be issued. This is commonly used
|
||||
@ -271,10 +267,13 @@ class Command(object):
|
||||
|
||||
:param netfn: Net function number
|
||||
:param command: Command value
|
||||
:param bridge_request: The target slave address and channel number for
|
||||
the bridge request.
|
||||
:param data: Command data as a tuple or list
|
||||
:returns: dict -- The response from IPMI device
|
||||
"""
|
||||
return self.ipmi_session.raw_command(netfn=netfn, command=command,
|
||||
bridge_request=bridge_request,
|
||||
data=data)
|
||||
|
||||
def get_power(self):
|
||||
@ -285,7 +284,7 @@ class Command(object):
|
||||
|
||||
:returns: dict -- {'powerstate': value}
|
||||
"""
|
||||
response = self.ipmi_session.raw_command(netfn=0, command=1)
|
||||
response = self.raw_command(netfn=0, command=1)
|
||||
if 'error' in response:
|
||||
raise exc.IpmiException(response['error'])
|
||||
assert(response['command'] == 1 and response['netfn'] == 1)
|
||||
|
@ -14,6 +14,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
IPMI_BMC_ADDRESS = 0x20
|
||||
IPMI_SEND_MESSAGE_CMD = 0x34
|
||||
|
||||
import pyghmi.constants as const
|
||||
|
||||
|
@ -227,6 +227,7 @@ class Session(object):
|
||||
self.password = password
|
||||
self.nowait = False
|
||||
self.pendingpayloads = collections.deque([])
|
||||
self.request_entry = []
|
||||
self.kgo = kg
|
||||
if kg is not None:
|
||||
self.kg = kg
|
||||
@ -302,10 +303,42 @@ class Session(object):
|
||||
csum &= 0xff
|
||||
return csum
|
||||
|
||||
def _make_ipmi_payload(self, netfn, command, data=()):
|
||||
def _make_bridge_request_msg(self, channel, netfn, command):
|
||||
"""This function generate message for bridge request. It is a
|
||||
part of ipmi payload.
|
||||
"""
|
||||
head = [constants.IPMI_BMC_ADDRESS,
|
||||
constants.netfn_codes['application'] << 2]
|
||||
check_sum = self._checksum(*head)
|
||||
#NOTE(fengqian): according IPMI Figure 14-11, rqSWID is set to 81h
|
||||
boday = [0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD]
|
||||
#NOTE(fengqian): Track request
|
||||
boday.append(0x40 | channel)
|
||||
self._add_request_entry((constants.netfn_codes['application'] + 1,
|
||||
self.seqlun, constants.IPMI_SEND_MESSAGE_CMD))
|
||||
return head + [check_sum] + boday
|
||||
|
||||
def _add_request_entry(self, entry=()):
|
||||
"""This function record the request with netfn, sequence number and
|
||||
command, which will be used in parse_ipmi_payload.
|
||||
:param entry: a set of netfn, sequence number and command.
|
||||
"""
|
||||
if not self._lookup_request_entry(entry):
|
||||
self.request_entry.append(entry)
|
||||
|
||||
def _lookup_request_entry(self, entry=()):
|
||||
return entry in self.request_entry
|
||||
|
||||
def _remove_request_entry(self, entry=()):
|
||||
if self._lookup_request_entry(entry):
|
||||
self.request_entry.remove(entry)
|
||||
|
||||
def _make_ipmi_payload(self, netfn, command, bridge_request={}, data=()):
|
||||
"""This function generates the core ipmi payload that would be
|
||||
applicable for any channel (including KCS)
|
||||
"""
|
||||
bridge_msg = []
|
||||
payload = []
|
||||
self.expectedcmd = command
|
||||
self.expectednetfn = netfn + \
|
||||
1 # in ipmi, the response netfn is always one
|
||||
@ -320,14 +353,35 @@ class Session(object):
|
||||
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
|
||||
seqincrement -= 1
|
||||
header = [0x20, netfn << 2]
|
||||
#figure 13-4, first two bytes are rsaddr and
|
||||
# netfn, rsaddr is always 0x20 since we are
|
||||
# addressing BMC
|
||||
reqbody = [self.rqaddr, self.seqlun, command] + list(data)
|
||||
|
||||
if bridge_request:
|
||||
addr = bridge_request.get('addr', 0x0)
|
||||
channel = bridge_request.get('channel', 0x0)
|
||||
bridge_msg = self._make_bridge_request_msg(channel, netfn, command)
|
||||
#NOTE(fengqian): For bridge request, rsaddr is specified and
|
||||
# rqaddr is BMC address.
|
||||
rqaddr = constants.IPMI_BMC_ADDRESS
|
||||
rsaddr = addr
|
||||
else:
|
||||
rqaddr = self.rqaddr
|
||||
rsaddr = constants.IPMI_BMC_ADDRESS
|
||||
|
||||
#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]
|
||||
|
||||
reqbody = [rqaddr, self.seqlun, command] + list(data)
|
||||
headsum = self._checksum(*header)
|
||||
bodysum = self._checksum(*reqbody)
|
||||
payload = header + [headsum] + reqbody + [bodysum]
|
||||
if bridge_request:
|
||||
payload = bridge_msg + payload
|
||||
#NOTE(fengqian): For bridge request, another check sum is needed.
|
||||
tail_csum = self._checksum(*payload[3:])
|
||||
payload.append(tail_csum)
|
||||
|
||||
self._add_request_entry((self.expectednetfn, self.seqlun, command))
|
||||
return payload
|
||||
|
||||
def _generic_callback(self, response):
|
||||
@ -339,6 +393,7 @@ class Session(object):
|
||||
def raw_command(self,
|
||||
netfn,
|
||||
command,
|
||||
bridge_request={},
|
||||
data=[],
|
||||
retry=True,
|
||||
delay_xmit=None):
|
||||
@ -347,8 +402,10 @@ class Session(object):
|
||||
self.incommand = True
|
||||
self.lastresponse = None
|
||||
self.ipmicallback = self._generic_callback
|
||||
self._send_ipmi_net_payload(netfn, command, data, retry=retry,
|
||||
delay_xmit=delay_xmit)
|
||||
self._send_ipmi_net_payload(netfn, command, data,
|
||||
bridge_request=bridge_request,
|
||||
retry=retry, delay_xmit=delay_xmit)
|
||||
|
||||
if retry: # in retry case, let the retry timers indicate wait time
|
||||
timeout = None
|
||||
else: # if not retry, give it a second before surrending
|
||||
@ -363,9 +420,10 @@ class Session(object):
|
||||
Session.wait_for_rsp(timeout=timeout)
|
||||
return self.lastresponse
|
||||
|
||||
def _send_ipmi_net_payload(self, netfn, command, data, retry=True,
|
||||
delay_xmit=None):
|
||||
ipmipayload = self._make_ipmi_payload(netfn, command, data)
|
||||
def _send_ipmi_net_payload(self, netfn, command, data, bridge_request={},
|
||||
retry=True, delay_xmit=None):
|
||||
ipmipayload = self._make_ipmi_payload(netfn, command, bridge_request,
|
||||
data)
|
||||
payload_type = constants.payload_types['ipmi']
|
||||
self.send_payload(payload=ipmipayload, payload_type=payload_type,
|
||||
retry=retry, delay_xmit=delay_xmit)
|
||||
@ -1023,10 +1081,32 @@ class Session(object):
|
||||
# For now, skip the checksums since we are in LAN only,
|
||||
# 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 # payload is not a match for our last packet
|
||||
entry = (payload[1] >> 2, payload[4], payload[5])
|
||||
if self._lookup_request_entry(entry):
|
||||
self._remove_request_entry(entry)
|
||||
|
||||
#NOTE(fengqian): for bridge request, we need to handle the response
|
||||
#twice. First response shows if message send correctly, second
|
||||
#response is the real response.
|
||||
#If the message is send crrectly, we will discard the first
|
||||
#response or else error message will be parsed and return.
|
||||
if ((entry[0] in [0x06, 0x07]) and (entry[2] == 0x34)
|
||||
and (payload[-2] == 0x0)):
|
||||
return -1
|
||||
else:
|
||||
self._parse_payload(payload)
|
||||
#NOTE(fengqian): recheck if the certain entry is removed in
|
||||
#case that bridge request failed.
|
||||
if self.request_entry:
|
||||
self._remove_request_entry((self.expectednetfn,
|
||||
self.seqlun, self.expectedcmd))
|
||||
else:
|
||||
# payload is not a match for our last packet
|
||||
# it is also not a bridge request.
|
||||
return -1
|
||||
|
||||
def _parse_payload(self, payload):
|
||||
|
||||
if hasattr(self, 'hasretried') and self.hasretried:
|
||||
self.hasretried = 0
|
||||
self.tabooseq[
|
||||
|
Loading…
x
Reference in New Issue
Block a user