From df43c5336be90365ac6707873d86497b15b8adf2 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Sat, 18 May 2013 14:29:43 -0400 Subject: [PATCH] More work to make it get further on session establishment --- ipmi_constants.py | 28 ++++++++++++++++++++++++ ipmibase.py | 56 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/ipmi_constants.py b/ipmi_constants.py index 9002964d..f7e457eb 100644 --- a/ipmi_constants.py +++ b/ipmi_constants.py @@ -32,3 +32,31 @@ rmcp_codes = { 0x12: 'Illegal or unrecognized parameter', } +ipmi_completion_codes = { + 0x0: "Success", + 0xc0: "Node Busy", + 0xc1: "Invalid command", + 0xc2: "Invalid command for given LUN", + 0xc3: "Timeout while processing command", + 0xc4: "Out of storage space on BMC", + 0xc5: "Reservation canceled or invalid reservation ID", + 0xc6: "Request data truncated", + 0xc7: "Request data length invalid", + 0xc8: "Request data field length limit exceeded", + 0xc9: "Parameter out of range", + 0xca: "Cannot return number of requested data bytes", + 0xcb: "Requested sensor, data, or record not present", + 0xcc: "Invalid data field in request", + 0xcd: "Command illegal for specified sensor or record type", + 0xce: "Command response could not be provided", + 0xcf: "Cannot execute duplicated request", + 0xd0: "SDR repository in update mode", + 0xd1: "Device in firmware update mode", + 0xd2: "BMC initialization in progress", + 0xd3: "Internal destination unavailable", + 0xd4: "Insufficient privilege level or firmware firewall", + 0xd5: "Command not supported in present state", + 0xd6: "Cannot execute command because subfunction disabled or unavailable", + 0xff: "Unspecified", +} + diff --git a/ipmibase.py b/ipmibase.py index fecf915c..740a6165 100644 --- a/ipmibase.py +++ b/ipmibase.py @@ -7,11 +7,16 @@ from collections import deque from time import time from hashlib import md5 from struct import pack, unpack -from ipmi_constants import payload_types +from ipmi_constants import payload_types, ipmi_completion_codes from random import random initialtimeout = 0.5 #minimum timeout for first packet to retry in any given session. This will be randomized to stagger out retries in case of congestion +def get_ipmi_errstr(): + if code in ipmi_completion_codes: + return ipmi_completion_codes[code] + else: + return "Unknown code "+code+" encountered" class IPMISession: poller=select.poll() bmc_handlers={} @@ -40,15 +45,30 @@ class IPMISession: #but does not increase buffers for applications that do less creative things #TODO: perhaps spread sessions across a socket pool when rmem_max is small, still get ~65/socket, but avoid long queues that might happen with #low rmem_max and putting thousands of nodes in line - def __init__(self,bmc,userid,password,port=623): + ''' + This function handles the synchronous caller case in liue of a client provided callback + ''' + def _sync_login(self,response): + if 'error' in response: + raise Exception(response['error']) + + def __init__(self,bmc,userid,password,port=623,onlogon=None): self.bmc=bmc self.userid=userid self.password=password self.port=port + if (onlogon is None): + self.async=False + self.onlogon=self._sync_login + else: + self.async=True + self.onlogon=onlogon if not hasattr(IPMISession,'socket'): self._createsocket() self.login() def _initsession(self): + self.ipmicallback=None + self.ipmicallbackargs=None self.sessioncontext=0 self.sequencenumber=0 self.sessionid=0 @@ -143,13 +163,31 @@ class IPMISession: print hashdata return hashdata - def _got_channel_auth_cap(self): - pass - + def _got_channel_auth_cap(self,response): + if (response['error']): + if self.onlogonargs is not None: + args = (response,self.onlogonargs) + else: + args = (response,) + self.onlogon(*args) + return + code = response['code'] + if code == 0xcc and self.ipmi15only is not None: #tried ipmi 2.0 against a 1.5 which should work, but some bmcs thought 'reserved' meant 'must be zero' + self.ipmi15only=1 + return self._get_channel_auth_cap() + if code != 0: + response['error']=get_ipmi_errstr(code)+" while trying to get channel authentication capabilities" + if self.onlogonargs is not None: + args = (response,self.onlogonargs) + else: + args = (response,) + self.onlogon(*args) + return + def _get_channel_auth_cap(self): - self.callback=self._got_channel_auth_cap + self.ipmicallback=self._got_channel_auth_cap if (self.ipmi15only): self._send_ipmi_net_payload(netfn=0x6,command=0x38,data=[0x0e,0x04]) else: @@ -245,7 +283,11 @@ class IPMISession: del payload[0:2] response['data']=payload self.timeout=initialtimeout+(0.5*random()) - print repr(response) + if self.ipmicallbackargs is not None: + args=(response,self.ipmicallbackargs) + else: + args=(response,) + self.ipmicallback(*args) def _timedout(self): #TODO: retransmit and error handling on lost packets