2
0
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:
Jenkins 2014-01-24 14:57:05 +00:00 committed by Gerrit Code Review
commit ba5bbe1fc4
3 changed files with 108 additions and 27 deletions

View File

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

View File

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

View File

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