python-pyghmi-0.5.8/0000775000175000017500000000000012220616611013313 5ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/0000775000175000017500000000000012220616611014610 5ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/__init__.py0000664000175000017500000000000012220316104016701 0ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/exceptions.py0000664000175000017500000000146112220616540017346 0ustar chuckchuck # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # The Exceptions that Pyghmi can throw class PyghmiException(Exception): pass class IpmiException(PyghmiException): pass class InvalidParameterValue(PyghmiException): pass python-pyghmi-0.5.8/pyghmi/ipmi/0000775000175000017500000000000012220616611015546 5ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/ipmi/console.py0000664000175000017500000002672412220616540017576 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This represents the low layer message framing portion of IPMI import fcntl import os import struct from pyghmi.ipmi.private import constants from pyghmi.ipmi.private import session class Console(object): """IPMI SOL class. This object represents an SOL channel, multiplexing SOL data with commands issued by ipmi.command. :param bmc: hostname or ip address of BMC :param userid: username to use to connect :param password: password to connect to the BMC :param iohandler: Either a function to call with bytes, a filehandle to use for input and output, or a tuple of (input, output) handles :param kg: optional parameter for BMCs configured to require it """ #TODO(jbjohnso): still need an exit and a data callin function def __init__(self, bmc, userid, password, iohandler, port=623, force=False, kg=None): if type(iohandler) == tuple: # two file handles self.console_in = iohandler[0] self.console_out = iohandler[1] elif type(iohandler) == file: # one full duplex file handle self.console_out = iohandler self.console_in = iohandler elif hasattr(iohandler, '__call__'): self.console_out = None self.console_in = None self.out_handler = iohandler if self.console_in is not None: fcntl.fcntl(self.console_in.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) self.remseq = 0 self.myseq = 0 self.lastsize = 0 self.sendbreak = 0 self.ackedcount = 0 self.ackedseq = 0 self.retriedpayload = 0 self.pendingoutput = "" self.awaitingack = False self.force_session = force self.ipmi_session = session.Session(bmc=bmc, userid=userid, password=password, port=port, kg=kg, onlogon=self._got_session) def _got_session(self, response): """Private function to navigate SOL payload activation """ if 'error' in response: self._print_data(response['error']) return #Send activate sol payload directive #netfn= 6 (application) #command = 0x48 (activate payload) #data = (1, sol payload type # 1, first instance # 0b11000000, -encrypt, authenticate, # disable serial/modem alerts, CTS fine # 0, 0, 0 reserved self.ipmi_session.raw_command(netfn=0x6, command=0x48, data=(1, 1, 192, 0, 0, 0), callback=self._payload_activated) def _payload_activated(self, response): """Check status of activate payload request """ if 'error' in response: self._print_data(response['error']) #given that these are specific to the command, #it's probably best if one can grep the error #here instead of in constants sol_activate_codes = { 0x81: 'SOL is disabled', 0x82: 'Maximum SOL session count reached', 0x83: 'Cannot activate payload with encryption', 0x84: 'Cannot activate payload without encryption', } if response['code']: if response['code'] in constants.ipmi_completion_codes: self._print_data( constants.ipmi_completion_codes[response['code']]) return elif response['code'] == 0x80: if self.force_session and not self.retriedpayload: self.retriedpayload = 1 self.ipmi_session.raw_command(netfn=0x6, command=0x49, data=(1, 1, 0, 0, 0, 0), callback=self._got_session) return else: self._print_data('SOL Session active for another client\n') return elif response['code'] in sol_activate_codes: self._print_data(sol_activate_codes[response['code']]+'\n') return else: self._print_data( 'SOL encountered Unrecognized error code %d\n' % response['code']) return #data[0:3] is reserved except for the test mode, which we don't use data = response['data'] self.maxoutcount = (data[5] << 8) + data[4] #BMC tells us this is the maximum allowed size #data[6:7] is the promise of how small packets are going to be, but we #don't have any reason to worry about it if (data[8] + (data[9] << 8)) != 623: #TODO(jbjohnso): support atypical SOL port number raise NotImplementedError("Non-standard SOL Port Number") #ignore data[10:11] for now, the vlan detail, shouldn't matter to this #code anyway... self.ipmi_session.sol_handler = self._got_sol_payload if self.console_in is not None: self.ipmi_session.register_handle_callback(self.console_in, self._got_cons_input) def _got_cons_input(self, handle): """Callback for handle events detected by ipmi session """ self.pendingoutput += handle.read() if not self.awaitingack: self._sendpendingoutput() def send_data(self, data): self.pendingoutput += data if not self.awaitingack: self._sendpendingoutput() @classmethod def wait_for_rsp(cls, timeout): """Delay for no longer than timeout for next response. This acts like a sleep that exits on activity. :param timeout: Maximum number of seconds before returning """ return session.Session.wait_for_rsp(timeout=timeout) def _sendpendingoutput(self): self.myseq += 1 self.myseq &= 0xf if self.myseq == 0: self.myseq = 1 payload = struct.pack("BBBB", self.myseq, self.ackedseq, self.ackedseq, self.sendbreak) payload += self.pendingoutput self.lasttextsize = len(self.pendingoutput) self.pendingoutput = "" self.awaitingack = True payload = struct.unpack("%dB" % len(payload), payload) self.lastpayload = payload self.ipmi_session.send_payload(payload, payload_type=1) def _print_data(self, data): """Convey received data back to caller in the format of their choice. Caller may elect to provide this class filehandle(s) or else give a callback function that this class will use to convey data back to caller. """ if self.console_out is not None: self.console_out.write(data) self.console_out.flush() elif self.out_handler: # callback style.. self.out_handler(data) def _got_sol_payload(self, payload): """SOL payload callback """ #TODO(jbjohnso) test cases to throw some likely scenarios at functions #for example, retry with new data, retry with no new data #retry with unexpected sequence number newseq = payload[0] & 0b1111 ackseq = payload[1] & 0b1111 ackcount = payload[2] nacked = payload[3] & 0b1000000 poweredoff = payload[3] & 0b100000 deactivated = payload[3] & 0b10000 #for now, ignore overrun. I assume partial NACK for this reason or for #no reason would be treated the same, new payload with partial data remdata = "" remdatalen = 0 if newseq != 0: # this packet at least has some data to send to us.. if len(payload) > 4: 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:]) if newseq == self.remseq: # it is a retry, but could have new data if remdatalen > self.lastsize: remdata = 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 self._print_data(remdata) ackpayload = (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 self.ipmi_session.send_payload(ackpayload, payload_type=1, retry=False) if self.myseq != 0 and ackseq == self.myseq: # the bmc has something # to say about last xmit self.awaitingack = False if nacked > 0: # the BMC was in some way unhappy if poweredoff: self._print_data("Remote system is powered down\n") if deactivated: self._print_data("Remote IPMI console disconnected\n") 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) self.pendingoutput = newtext + self.pendingoutput self._sendpendingoutput() elif self.awaitingack: # session marked us as happy, but we are not #this does mean that we will occasionally retry a packet #sooner than retry suggests, but that's no big deal self.ipmi_session.send_payload(payload=self.lastpayload, payload_type=1) def main_loop(self): """Process all events until no more sessions exist. If a caller is a simple little utility, provide a function to eternally run the event loop. More complicated usage would be expected to provide their own event loop behavior, though this could be used within the greenthread implementation of caller's choice if desired. """ #wait_for_rsp promises to return a false value when no sessions are #alive anymore #TODO(jbjohnso): wait_for_rsp is not returning a true value for our own #session while (1): session.Session.wait_for_rsp(timeout=600) python-pyghmi-0.5.8/pyghmi/ipmi/private/0000775000175000017500000000000012220616611017220 5ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/ipmi/private/constants.py0000664000175000017500000000764012205461765021630 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. payload_types = { 'ipmi': 0x0, 'sol': 0x1, 'rmcpplusopenreq': 0x10, 'rmcpplusopenresponse': 0x11, 'rakp1': 0x12, 'rakp2': 0x13, 'rakp3': 0x14, 'rakp4': 0x15, } rmcp_codes = { 1: ("Insufficient resources to create new session (wait for existing " "sessions to timeout)"), 2: "Invalid Session ID", 3: "Invalid payload type", 4: "Invalid authentication algorithm", 5: "Invalid integrity algorithm", 6: "No matching integrity payload", 7: "No matching integrity payload", 8: "Inactive Session ID", 9: "Invalid role", 0xa: "Unauthorized role or privilege level requested", 0xb: "Insufficient resources to create a session at the requested role", 0xc: "Invalid username length", 0xd: "Unauthorized name", 0xe: "Unauthorized GUID", 0xf: "Invalid integrity check value", 0x10: "Invalid confidentiality algorithm", 0x11: "No Cipher suite match with proposed security algorithms", 0x12: "Illegal or unrecognized parameter", } netfn_codes = { "chassis": 0x0, "bridge": 0x2, "sensorevent": 0x4, "application": 0x6, "firmware": 0x8, "storage": 0xa, "transport": 0xc, } command_completion_codes = { (7, 0x39): { 0x81: "Invalid user name", 0x82: "Null user disabled", }, (7, 0x3a): { 0x81: "No available login slots", 0x82: "No available login slots for requested user", 0x83: "No slot available with requested privilege level", 0x84: "Session sequence number out of range", 0x85: "Invalid session ID", 0x86: ("Requested privilege level exceeds requested user permissions " "on this channel"), }, (7, 0x3b): { # Set session privilege level 0x80: "User is not allowed requested privilege level", 0x81: "Requested privilege level is not allowed over this channel", 0x82: "Cannot disable user level authentication", }, (1, 8): { # set system boot options 0x80: "Parameter not supported", 0x81: "Attempt to set set 'set in progress' when not 'set complete'", 0x82: "Attempt to write read-only parameter", } } ipmi_completion_codes = { 0x00: "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", } python-pyghmi-0.5.8/pyghmi/ipmi/private/session.py0000664000175000017500000014305512220616540021266 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This represents the low layer message framing portion of IPMI import atexit import collections import hashlib import os import random import select import socket import struct import time from Crypto.Cipher import AES from Crypto.Hash import HMAC from Crypto.Hash import SHA import pyghmi.exceptions as exc from pyghmi.ipmi.private import constants 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 _monotonic_time(): """Provides a monotonic timer This code is concerned with relative, not absolute time. This function facilitates that prior to python 3.3 """ # Python does not provide one until 3.3, so we make do # for most OSes, os.times()[4] works well. # for microsoft, GetTickCount64 if (os.name == "posix"): return os.times()[4] else: # last resort, non monotonic time return time.time() #TODO(jbjohnso): Windows variant def _poller(readhandles, timeout=0): rdylist, _, _ = select.select(readhandles, (), (), timeout) return rdylist 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 while padval <= neededpad: newdata.append(padval) padval += 1 newdata.append(neededpad) return newdata def call_with_optional_args(callback, *args): """In order to simplify things, in a number of places there is a callback facility and optional arguments to pass in. An object-oriented caller may find the additional argument needless. Allow them to ignore it by skipping the argument if None. """ newargs = [] for arg in args: if arg is not None: newargs.append(arg) callback(*newargs) def get_ipmi_error(response, suffix=""): if 'error' in response: return response['error'] + suffix code = response['code'] if code == 0: return False command = response['command'] netfn = response['netfn'] if ((netfn, command) in constants.command_completion_codes and code in constants.command_completion_codes[(netfn, command)]): res = constants.command_completion_codes[(netfn, command)][code] res += suffix elif code in constants.ipmi_completion_codes: res = constants.ipmi_completion_codes[code] + suffix else: res = "Unknown code 0x%2x encountered" % code return res class Session: """A class to manage common IPMI session logistics Almost all developers should not worry about this class and instead be looking toward ipmi.Command and ipmi.Console. For those that do have to worry, the main interesting thing is that the event loop can go one of two ways. Either a larger manager can query using class methods the soonest timeout deadline and the filehandles to poll and assume responsibility for the polling, or it can register filehandles to be watched. This is primarily of interest to Console class, which may have an input filehandle to watch and can pass it to Session. :param bmc: hostname or ip address of the BMC :param userid: username to use to connect :param password: password to connect to the BMC :param kg: optional parameter if BMC requires Kg be set :param port: UDP port to communicate with, pretty much always 623 :param onlogon: callback to receive notification of login completion """ _external_handlers = {} bmc_handlers = {} waiting_sessions = {} keepalive_sessions = {} peeraddr_to_nodes = {} # Upon exit of python, make sure we play nice with BMCs by assuring closed # sessions for all that we tracked @classmethod def _cleanup(cls): for session in cls.bmc_handlers.itervalues(): session.logout() @classmethod def _createsocket(cls): atexit.register(cls._cleanup) cls.socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) # INET6 # can do IPv4 if you are nice to it try: # we will try to fixup our receive buffer size if we are smaller # than allowed. maxmf = open("/proc/sys/net/core/rmem_max") rmemmax = int(maxmf.read()) rmemmax = rmemmax / 2 curmax = cls.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) curmax = curmax / 2 if (rmemmax > curmax): cls.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, rmemmax) except Exception: # FIXME: be more selective in catching exceptions pass curmax = cls.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) cls.readersockets = [cls.socket] curmax = curmax / 2 # we throttle such that we never have no more outstanding packets than # our receive buffer should be able to handle cls.pending = 0 cls.maxpending = curmax / 1000 # pessimistically assume 1 kilobyte messages, # which is way larger than almost all ipmi datagrams. # For faster performance, sysadmins may want to examine and tune # /proc/sys/net/core/rmem_max up. This allows the module to request # more, but does not increase buffers for applications that do less # creative things # TODO(jbjohnso): 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 _sync_login(self, response): """Handle synchronous callers in liue of a client-provided callback. """ if 'error' in response: raise exc.IpmiException(response['error']) def __init__(self, bmc, userid, password, port=623, kg=None, onlogon=None): self.lastpayload = None self.bmc = bmc self.userid = userid self.password = password self.nowait = False self.pendingpayloads = collections.deque([]) if kg is not None: self.kg = kg else: self.kg = 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(Session, 'socket'): self._createsocket() self.login() if not self.async: while not self.logged: Session.wait_for_rsp() def _initsession(self): # NOTE(jbjohnso): this number can be whatever we want. # I picked 'xCAT' minus 1 so that a hexdump of packet # would show xCAT self.localsid = 2017673555 # NOTE(jbjohnso): for the moment, assume admin access # TODO(jbjohnso): make flexible self.privlevel = 4 self.confalgo = 0 self.aeskey = None self.integrityalgo = 0 self.k1 = None self.rmcptag = 1 self.ipmicallback = None self.ipmicallbackargs = None self.sessioncontext = None self.sequencenumber = 0 self.sessionid = 0 self.authtype = 0 self.ipmiversion = 1.5 self.timeout = initialtimeout + (0.5 * random.random()) 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, # do not forsee a reason to adjust self.rqaddr = 0x81 self.logged = 0 # NOTE(jbjohnso): when we confirm a working sockaddr, put it here to # skip getaddrinfo self.sockaddr = None # NOTE(jbjohnso): this tracks netfn,command,seqlun combinations that # were retried so that we don't loop around and reuse # the same request data and cause potential ambiguity # in return self.tabooseq = {} # NOTE(jbjohnso): default to supporting ipmi 2.0. Strictly by spec, # this should gracefully be backwards compat, but some # 1.5 implementations checked reserved bits self.ipmi15only = 0 self.sol_handler = None # NOTE(jbjohnso): This is the callback handler for any SOL payload def _checksum(self, *data): # Two's complement over the data csum = sum(data) csum = csum ^ 0xff csum += 1 csum &= 0xff return csum def _make_ipmi_payload(self, netfn, command, data=()): """This function generates the core ipmi payload that would be applicable for any channel (including KCS) """ self.expectedcmd = command self.expectednetfn = netfn + \ 1 # in ipmi, the response netfn is always one # higher than the request payload, we assume # we are always the requestor for now seqincrement = 7 # IPMI spec forbids gaps bigger then 7 in seq number. # Risk the taboo rather than violate the rules while ((netfn, command, self.seqlun) in self.tabooseq and 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 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) headsum = self._checksum(*header) bodysum = self._checksum(*reqbody) payload = header + [headsum] + reqbody + [bodysum] return payload def _generic_callback(self, response): errorstr = get_ipmi_error(response) if errorstr: response['error'] = errorstr self.lastresponse = response def raw_command(self, netfn, command, data=[], retry=True, callback=None, callback_args=None, delay_xmit=None): self.ipmicallbackargs = callback_args if callback is None: self.lastresponse = None self.ipmicallback = self._generic_callback else: self.ipmicallback = callback self._send_ipmi_net_payload(netfn, command, data, 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 timeout = 1 #In the synchronous case, wrap the event loop in this call #The event loop is shared amongst pyghmi session instances #within a process. In this way, synchronous usage of the interface #plays well with asynchronous use. In fact, this produces the behavior #of only the constructor *really* needing a callback. From then on, #synchronous usage of the class acts in a greenthread style governed by #order of data on the network if callback is None: while self.lastresponse is None: 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) payload_type = constants.payload_types['ipmi'] self.send_payload(payload=ipmipayload, payload_type=payload_type, retry=retry, delay_xmit=delay_xmit) def send_payload(self, payload=None, payload_type=None, retry=True, delay_xmit=None): if payload is not None and self.lastpayload is not None: #we already have a packet outgoing, make this # a pending payload # this way a simplistic BMC won't get confused # and we also avoid having to do more complicated # retry mechanism where each payload is # retried separately self.pendingpayloads.append((payload, payload_type, retry)) return if payload_type is None: payload_type = self.last_payload_type if payload is None: payload = self.lastpayload message = [0x6, 0, 0xff, 0x07] # constant RMCP header for IPMI if retry: self.lastpayload = payload self.last_payload_type = payload_type message.append(self.authtype) baretype = payload_type if self.integrityalgo: payload_type |= 0b01000000 if self.confalgo: payload_type |= 0b10000000 if (self.ipmiversion == 2.0): message.append(payload_type) if (baretype == 2): #TODO(jbjohnso): OEM payload types raise NotImplementedError("OEM Payloads") elif baretype not in constants.payload_types.values(): raise NotImplementedError( "Unrecognized payload type %d" % baretype) message += struct.unpack("!4B", struct.pack("> 8) iv = os.urandom(16) message += list(struct.unpack("16B", iv)) payloadtocrypt = _aespad(payload) crypter = AES.new(self.aeskey, AES.MODE_CBC, iv) crypted = crypter.encrypt(struct.pack("%dB" % len(payloadtocrypt), *payloadtocrypt)) crypted = list(struct.unpack("%dB" % len(crypted), crypted)) message += crypted else: # no confidetiality algorithm message.append(psize & 0xff) message.append(psize >> 8) message += list(payload) if self.integrityalgo: # see table 13-8, # RMCP+ packet format # TODO(jbjohnso): SHA256 which is now # allowed neededpad = (len(message) - 2) % 4 if neededpad: neededpad = 4 - neededpad message += [0xff] * 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), SHA).digest()[:12] # SHA1-96 # per RFC2404 truncates to 96 bits message += struct.unpack("12B", authcode) self.netpacket = struct.pack("!%dB" % len(message), *message) #advance idle timer since we don't need keepalive while sending packets #out naturally if self in Session.keepalive_sessions: Session.keepalive_sessions[self]['timeout'] = _monotonic_time() + \ 25 + (random.random() * 4.9) self._xmit_packet(retry, delay_xmit=delay_xmit) def _ipmi15authcode(self, payload, checkremotecode=False): #checkremotecode is used to verify remote code, #otherwise this function is used to general authcode for local if self.authtype == 0: # Only for things before auth in ipmi 1.5, not # like 2.0 cipher suite 0 return () password = self.password padneeded = 16 - len(password) 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(" 16: raise exc.IpmiException( "Username too long for IPMI, must not exceed 16") padneeded = 16 - len(self.userid) userid = self.userid + ('\x00' * padneeded) reqdata += struct.unpack("!16B", userid) self.ipmicallback = self._got_session_challenge self._send_ipmi_net_payload(netfn=0x6, command=0x39, data=reqdata) def _open_rmcpplus_request(self): self.authtype = 6 self.localsid += 1 # have unique local session ids to ignore aborted # login attempts from the past self.rmcptag += 1 data = [ self.rmcptag, 0, # request as much privilege as the channel will give us 0, 0, # reserved ] data += list(struct.unpack("4B", struct.pack(" 0: while _poller((cls.socket,)): # if the somewhat lengthy # queue # processing takes long enough for packets to # come in, be eager pktqueue = collections.deque([]) while _poller((cls.socket,)): # looks rendundant, but # want # to queue and process packets to keep # things off RCVBUF rdata = cls.socket.recvfrom(3000) pktqueue.append(rdata) while len(pktqueue): (data, sockaddr) = pktqueue.popleft() cls._route_ipmiresponse(sockaddr, data) while _poller((cls.socket,)): # seems ridiculous, #but between every callback, check for packets again rdata = cls.socket.recvfrom(3000) pktqueue.append(rdata) for handlepair in _poller(cls.readersockets): if isinstance(handlepair, int): myhandle = handlepair else: myhandle = handlepair.fileno() if myhandle != cls.socket.fileno(): myfile = cls._external_handlers[myhandle][1] cls._external_handlers[myhandle][0](myfile) sessionstodel = [] for session, parms in cls.keepalive_sessions.iteritems(): if parms['timeout'] < curtime: cls.keepalive_sessions[session]['timeout'] = 25 + \ (random.random() * 4.9) session._keepalive() for session, parms in cls.waiting_sessions.iteritems(): if parms['timeout'] < curtime: # timeout has expired, time to # give up on it and trigger timeout # response in the respective # session sessionstodel.append( session) # defer deletion until after loop # to avoid confusing the for loop for session in sessionstodel: cls.pending -= 1 cls.waiting_sessions.pop(session, None) session._timedout() return len(cls.waiting_sessions) def _keepalive(self): """Performs a keepalive to avoid idle disconnect """ self.raw_command(netfn=6, command=1) @classmethod def register_handle_callback(cls, handle, callback): """Add a handle to be watched by Session's event loop In the event that an application would like IPMI Session event loop to drive things while adding their own filehandle to watch for events, this class method will register that. :param handle: filehandle too watch for input :param callback: function to call when input detected on the handle. will receive the handle as an argument """ if isinstance(handle, int): cls._external_handlers[handle] = (callback, handle) else: cls._external_handlers[handle.fileno()] = (callback, handle) cls.readersockets += [handle] @classmethod def _route_ipmiresponse(cls, sockaddr, data): if not (data[0] == '\x06' and data[2:4] == '\xff\x07'): # not ipmi return try: cls.bmc_handlers[sockaddr]._handle_ipmi_packet(data, sockaddr=sockaddr) cls.pending -= 1 except KeyError: pass def _handle_ipmi_packet(self, data, sockaddr=None): if self.sockaddr is None and sockaddr is not None: self.sockaddr = sockaddr elif (self.sockaddr is not None and sockaddr is not None and self.sockaddr != sockaddr): return # here, we might have sent an ipv4 and ipv6 packet to kick # things off ignore the second reply since we have one # satisfactory answer if data[4] in ('\x00', '\x02'): # This is an ipmi 1.5 paylod remsequencenumber = struct.unpack('> 2 != self.expectednetfn or payload[5] != self.expectedcmd): return -1 # payload is not a match for our last packet if hasattr(self, 'hasretried') and self.hasretried: self.hasretried = 0 self.tabooseq[ (self.expectednetfn, self.expectedcmd, self.seqlun)] = 16 # try to skip it for at most 16 cycles of overflow # We want to now remember that we do not have an expected packet self.expectednetfn = 0x1ff # bigger than one byte means it can never # match the one byte value by mistake self.expectedcmd = 0x1ff self.seqlun += 4 # prepare seqlun for next transmit self.seqlun &= 0xff # when overflowing, wrap around Session.waiting_sessions.pop(self, None) self.lastpayload = None # render retry mechanism utterly incapable of # doing anything, though it shouldn't matter self.last_payload_type = None response = {} response['netfn'] = payload[1] >> 2 del payload[0:5] # ^^ remove header of rsaddr/netfn/lun/checksum/rq/seq/lun del payload[-1] # remove the trailing checksum response['command'] = payload[0] response['code'] = payload[1] del payload[0:2] response['data'] = payload self.timeout = initialtimeout + (0.5 * random.random()) if len(self.pendingpayloads) > 0: (nextpayload, nextpayloadtype, retry) = \ self.pendingpayloads.popleft() self.send_payload(payload=nextpayload, payload_type=nextpayloadtype, retry=retry) call_with_optional_args(self.ipmicallback, response, self.ipmicallbackargs) def _timedout(self): if not self.lastpayload: return self.nowait = True self.timeout += 1 if self.timeout > 5: response = {'error': 'timeout'} call_with_optional_args(self.ipmicallback, response, self.ipmicallbackargs) self.nowait = False return elif self.sessioncontext == 'FAILED': self.nowait = False return if self.sessioncontext == 'OPENSESSION': # In this case, we want to craft a new session request to have # unambiguous session id regardless of how packet was dropped or # delayed in this case, it's safe to just redo the request self._open_rmcpplus_request() elif (self.sessioncontext == 'EXPECTINGRAKP2' or self.sessioncontext == 'EXPECTINGRAKP4'): # If we can't be sure which RAKP was dropped or if RAKP3/4 was just # delayed, the most reliable thing to do is rewind and start over # bmcs do not take kindly to receiving RAKP1 or RAKP3 twice self._relog() else: # in IPMI case, the only recourse is to act as if the packet is # idempotent. SOL has more sophisticated retry handling # the biggest risks are reset sp which is often fruitless to retry # and chassis reset, which sometimes will shoot itself # systematically in the head in a shared port case making replies # impossible self.hasretried = 1 # remember so that we can track taboo # combinations # of sequence number, netfn, and lun due to # ambiguity on the wire self.send_payload() self.nowait = False def _xmit_packet(self, retry=True, delay_xmit=None): if not self.nowait: # if we are retrying, we really need to get the # packet out and get our timeout updated Session.wait_for_rsp(timeout=0) # take a convenient opportunity # to drain the socket queue if # applicable while Session.pending > Session.maxpending: Session.wait_for_rsp() if self.sequencenumber: # seq number of zero will be left alone, it is # special, otherwise increment self.sequencenumber += 1 if retry: Session.waiting_sessions[self] = {} Session.waiting_sessions[self]['ipmisession'] = self Session.waiting_sessions[self]['timeout'] = self.timeout + \ _monotonic_time() Session.pending += 1 if delay_xmit is not None: Session.waiting_sessions[self]['timeout'] = delay_xmit + \ _monotonic_time() return # skip transmit, let retry timer do it's thing if self.sockaddr: Session.socket.sendto(self.netpacket, self.sockaddr) else: # he have not yet picked a working sockaddr for this connection, # try all the candidates that getaddrinfo provides try: for res in socket.getaddrinfo(self.bmc, self.port, 0, socket.SOCK_DGRAM): sockaddr = res[4] if (res[0] == socket.AF_INET): # convert the sockaddr # to AF_INET6 newhost = '::ffff:' + sockaddr[0] sockaddr = (newhost, sockaddr[1], 0, 0) Session.bmc_handlers[sockaddr] = self Session.socket.sendto(self.netpacket, sockaddr) except socket.gaierror: raise exc.IpmiException( "Unable to transmit to specified address") def logout(self, callback=None, callback_args=None): if not self.logged: if callback is None: return {'success': True} callback({'success': True}) return self.raw_command(command=0x3c, netfn=6, data=struct.unpack("4B", struct.pack("I", self.sessionid)), retry=False, callback=callback, callback_args=callback_args) self.logged = 0 if callback is None: return {'success': True} callback({'success': True}) if __name__ == "__main__": import sys ipmis = Session(bmc=sys.argv[1], userid=sys.argv[2], password=os.environ['IPMIPASS']) print ipmis.raw_command(command=2, data=[1], netfn=0) print get_ipmi_error({'command': 8, 'code': 128, 'netfn': 1}) python-pyghmi-0.5.8/pyghmi/ipmi/private/__init__.py0000664000175000017500000000000012205461765021332 0ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/ipmi/__init__.py0000664000175000017500000000000012205461765017660 0ustar chuckchuckpython-pyghmi-0.5.8/pyghmi/ipmi/command.py0000664000175000017500000002754212220616540017551 0ustar chuckchuck# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # This represents the low layer message framing portion of IPMI import pyghmi.exceptions as exc from pyghmi.ipmi.private import session boot_devices = { 'net': 4, 'network': 4, 'pxe': 4, 'hd': 8, 'cd': 0x14, 'cdrom': 0x14, 'dvd': 0x14, 'floppy': 0x3c, 'default': 0x0, 'setup': 0x18, 'f1': 0x18, 1: 'network', 2: 'hd', 5: 'optical', 6: 'setup', 0: 'default' } power_states = { "off": 0, "on": 1, "reset": 3, "softoff": 5, "shutdown": 5, # NOTE(jbjohnso): -1 is not a valid direct boot state, # but here for convenience of 'in' statement "boot": -1, } class Command(object): """Send IPMI commands to BMCs. This object represents a persistent session to an IPMI device (bmc) and allows the caller to reuse a single session to issue multiple commands. This class can be used in a synchronous (wait for answer and return) or asynchronous fashion (return immediately and provide responses by callbacks). Synchronous mode is the default behavior. For asynchronous mode, simply pass in a callback function. It is recommended to pass in an instance method to callback and ignore the callback_args parameter. However, callback_args can optionally be populated if desired. :param bmc: hostname or ip address of the BMC :param userid: username to use to connect :param password: password to connect to the BMC :param onlogon: function to run when logon completes in an asynchronous fashion. This will result in a greenthread behavior. :param kg: Optional parameter to use if BMC has a particular Kg configured """ def __init__(self, bmc, userid, password, port=623, onlogon=None, kg=None): # TODO(jbjohnso): accept tuples and lists of each parameter for mass # operations without pushing the async complexities up the stack self.onlogon = onlogon self.bmc = bmc if onlogon is not None: self.ipmi_session = session.Session(bmc=bmc, userid=userid, password=password, onlogon=self.logged, port=port, kg=kg) else: self.ipmi_session = session.Session(bmc=bmc, userid=userid, password=password, port=port, kg=kg) def logged(self, response): self.onlogon(response, self) @classmethod def eventloop(cls): while (session.Session.wait_for_rsp()): pass @classmethod def wait_for_rsp(cls, timeout): """Delay for no longer than timeout for next response. This acts like a sleep that exits on activity. :param timeout: Maximum number of seconds before returning """ return session.Session.wait_for_rsp(timeout=timeout) def get_bootdev(self): """Get current boot device override information. Provides the current requested boot device. Be aware that not all IPMI devices support this. Even in BMCs that claim to, occasionally the BIOS or UEFI fail to honor it. This is usually only applicable to the next reboot. :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)) # interpret response per 'get system boot options' if 'error' in response: return response # this should only be invoked for get system boot option complying to # ipmi spec and targeting the 'boot flags' parameter assert (response['command'] == 9 and response['netfn'] == 1 and response['data'][0] == 1 and (response['data'][1] & 0b1111111) == 5) if (response['data'][1] & 0b10000000 or not response['data'][2] & 0b10000000): return {'bootdev': 'default'} else: # will consult data2 of the boot flags parameter for the data bootnum = (response['data'][3] & 0b111100) >> 2 bootdev = boot_devices[bootnum] if (bootdev): return {'bootdev': bootdev} else: return {'bootdev': bootnum} def set_power(self, powerstate, wait=False): """Request power state change :param powerstate: * on -- Request system turn on * off -- Request system turn off without waiting for OS to shutdown * shutdown -- Have system request OS proper shutdown * reset -- Request system reset without waiting for OS * boot -- If system is off, then 'on', else 'reset' :param wait: If True, do not return until system actually completes requested state change for 300 seconds. If a non-zero number, adjust the wait time to the requested number of seconds :returns: dict -- A dict describing the response retrieved """ if powerstate not in power_states: raise exc.InvalidParameterValue( "Unknown power state %s requested" % powerstate) self.newpowerstate = powerstate response = self.ipmi_session.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' if self.powerstate == self.newpowerstate: return {'powerstate': self.powerstate} if self.newpowerstate == 'boot': self.newpowerstate = 'on' if self.powerstate == 'off' else 'reset' response = self.ipmi_session.raw_command( netfn=0, command=2, data=[power_states[self.newpowerstate]]) if 'error' in response: raise exc.IpmiException(response['error']) self.lastresponse = {'pendingpowerstate': self.newpowerstate} waitattempts = 300 if not isinstance(wait, bool): waitattempts = wait if (wait and self.newpowerstate in ('on', 'off', 'shutdown', 'softoff')): if self.newpowerstate in ('softoff', 'shutdown'): self.waitpowerstate = 'off' else: 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) if 'error' in response: return response currpowerstate = 'on' if (response['data'][0] & 1) else 'off' waitattempts -= 1 if currpowerstate != self.waitpowerstate: raise exc.IpmiException( "System did not accomplish power state change") return {'powerstate': currpowerstate} else: return self.lastresponse def set_bootdev(self, bootdev, persist=False, uefiboot=False, callback=None, callback_args=None): """Set boot device to use on next reboot :param bootdev: *net -- Request network boot *hd -- Boot from hard drive *optical -- boot from CD or DVD drive *setup -- Boot into setup utility *default -- remove any IPMI directed boot device request :param persist: If true, ask that system firmware use this device beyond next boot. Be aware many systems do not honor this :param uefiboot: If true, request UEFI boot explicitly. Strictly speaking, the spec sugests that if not set, the system should BIOS boot and offers no "don't care" option. In practice, this flag not being set does not preclude UEFI boot on any system I've encountered. :param callback: optional callback :param callback_args: optional arguments to callback :returns: dict or True -- If callback is not provided, the response """ self.commandcallback = callback self.commandcallbackargs = callback_args if bootdev not in boot_devices: return {'error': "Unknown bootdevice %s requested" % bootdev} self.bootdev = boot_devices[bootdev] self.persistboot = persist self.uefiboot = uefiboot # first, we disable timer by way of set system boot options, # 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)) self.lastresponse = response if 'error' in response: return response bootflags = 0x80 if self.uefiboot: bootflags = bootflags | 1 << 5 if self.persistboot: bootflags = bootflags | 1 << 6 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) if 'error' in response: return response return {'bootdev': bootdev} def raw_command(self, netfn, command, data=()): """Send raw ipmi command to BMC This allows arbitrary IPMI bytes to be issued. This is commonly used for certain vendor specific commands. Example: ipmicmd.raw_command(netfn=0,command=4,data=(5)) :param netfn: Net function number :param command: Command value :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, data=data) def get_power(self): """Get current power state of the managed system The response, if successful, should contain 'powerstate' key and either 'on' or 'off' to indicate current state. :returns: dict -- {'powerstate': value} """ response = self.ipmi_session.raw_command(netfn=0, command=1) if 'error' in response: raise exc.IpmiException(response['error']) assert(response['command'] == 1 and response['netfn'] == 1) self.powerstate = 'on' if (response['data'][0] & 1) else 'off' return {'powerstate': self.powerstate} python-pyghmi-0.5.8/README0000664000175000017500000000023512205461765014206 0ustar chuckchuckThis is a pure python implementation of IPMI protocol. ipmictl.py is a sample application to roughly show the most simple approach to invoking the library. python-pyghmi-0.5.8/LICENSE0000664000175000017500000002613612205461765014343 0ustar chuckchuck Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. python-pyghmi-0.5.8/requirements.txt0000664000175000017500000000001612214647132016601 0ustar chuckchuckpycrypto>=2.6 python-pyghmi-0.5.8/solconnect.py0000664000175000017500000000250212215066255016042 0ustar chuckchuck#!/usr/bin/env python # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ @author: Jarrod Johnson """ """A simple little script to exemplify/test ipmi.console module """ import os import sys import termios import tty from pyghmi.ipmi import console tcattr = termios.tcgetattr(sys.stdin) newtcattr = tcattr #TODO(jbjohnso): add our exit handler newtcattr[-1][termios.VINTR] = 0 newtcattr[-1][termios.VSUSP] = 0 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, newtcattr) tty.setcbreak(sys.stdin.fileno()) passwd = os.environ['IPMIPASSWORD'] try: sol = console.Console(bmc=sys.argv[1], userid=sys.argv[2], password=passwd, iohandler=(sys.stdin, sys.stdout), force=True) sol.main_loop() finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, tcattr) python-pyghmi-0.5.8/.testr.conf0000664000175000017500000000050012205461765015407 0ustar chuckchuck[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list python-pyghmi-0.5.8/doc/0000775000175000017500000000000012220616611014060 5ustar chuckchuckpython-pyghmi-0.5.8/doc/source/0000775000175000017500000000000012220616611015360 5ustar chuckchuckpython-pyghmi-0.5.8/doc/source/conf.py0000664000175000017500000001621312205467263016674 0ustar chuckchuck# -*- coding: utf-8 -*- # # pyghmi documentation build configuration file, created by # sphinx-quickstart on Tue Jun 18 09:15:24 2013. # # This file is execfile()d with the current directory set to # its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../../')) sys.path.insert(0, os.path.abspath('../')) sys.path.insert(0, os.path.abspath('.')) # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.pngmath'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pyghmi' copyright = u'2013, Jarrod Johnson ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # from ipmi.version import version_info # The full version, including alpha/beta/rc tags. release = version_info.release_string() # The short X.Y version. version = version_info.version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pyghmidoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) latex_documents = [ ('index', 'pyghmi.tex', u'pyghmi Documentation', u'Jarrod Johnson \\textless{}jbjohnso@us.ibm.com\\textgreater{}', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pyghmi', u'pyghmi Documentation', [u'Jarrod Johnson '], 1) ] python-pyghmi-0.5.8/doc/source/index.rst0000664000175000017500000000076312205467263017241 0ustar chuckchuck.. pyghmi documentation master file, created by sphinx-quickstart on Tue Jun 18 09:15:24 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyghmi's documentation! ======================================= Contents: .. toctree:: :maxdepth: 2 .. automodule:: ipmi.command .. autoclass:: ipmi.command :members: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-pyghmi-0.5.8/setup.py0000775000175000017500000000141512214647132015036 0ustar chuckchuck#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools setuptools.setup( setup_requires=['pbr'], pbr=True) python-pyghmi-0.5.8/ipmictl.py0000775000175000017500000000426712220372705015345 0ustar chuckchuck#!/usr/bin/env python # Copyright 2013 IBM Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ @author: Jarrod Johnson """ """This is an example of using the library in a synchronous fashion. For now, it isn't conceived as a general utility to actually use, just help developers understand how the ipmi_command class workes. """ import os import string import sys from pyghmi.ipmi import command password = os.environ['IPMIPASSWORD'] os.environ['IPMIPASSWORD'] = "" if (len(sys.argv) < 3): print "Usage:" print " IPMIPASSWORD=password %s bmc username " % sys.argv[0] sys.exit(1) bmc = sys.argv[1] userid = sys.argv[2] args = None if len(sys.argv) >= 5: args = sys.argv[4:] ipmicmd = None def docommand(result, ipmisession): cmmand = sys.argv[3] print "Logged into %s" % ipmisession.bmc if 'error' in result: print result['error'] return if cmmand == 'power': if args: print ipmisession.set_power(args[0], wait=True) else: value = ipmisession.get_power() print "%s: %s" % (ipmisession.bmc, value['powerstate']) elif cmmand == 'bootdev': if args: print ipmisession.set_bootdev(args[0]) else: print ipmisession.get_bootdev() elif cmmand == 'raw': print ipmisession.raw_command(netfn=int(args[0]), command=int(args[1]), data=map(lambda x: int(x, 16), args[2:])) bmcs = string.split(bmc, ",") for bmc in bmcs: ipmicmd = command.Command(bmc=bmc, userid=userid, password=password, onlogon=docommand) ipmicmd.eventloop() python-pyghmi-0.5.8/test-requirements.txt0000664000175000017500000000022612214647132017561 0ustar chuckchuckhacking>=0.5.6,<0.8 coverage>=3.6 discover fixtures>=0.3.14 python-subunit sphinx>=1.1.2 testrepository>=0.0.17 testscenarios>=0.4 testtools>=0.9.32 python-pyghmi-0.5.8/PKG-INFO0000664000175000017500000000154612220616611014416 0ustar chuckchuckMetadata-Version: 1.0 Name: pyghmi Version: 0.5.8 Summary: Python General Hardware Management Initiative (IPMI and others) Home-page: http://xcat.sf.net/ Author: Jarrod Johnson Author-email: jbjohnso@us.ibm.com License: UNKNOWN Description: This is a pure python implementation of IPMI protocol. ipmictl.py is a sample application to roughly show the most simple approach to invoking the library. Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 python-pyghmi-0.5.8/tox.ini0000664000175000017500000000112512205461765014640 0ustar chuckchuck[tox] envlist = py26,py27,py33,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' [tox:jenkins] sitepackages = True downloadcache = ~/cache/pip [testenv:pep8] commands = flake8 [testenv:cover] setenv = VIRTUAL_ENV={envdir} commands = python setup.py testr --coverage [testenv:venv] commands = {posargs} [flake8] exclude = .venv,.tox,dist,doc,*.egg,build show-source = true python-pyghmi-0.5.8/setup.cfg0000664000175000017500000000126412220616611015137 0ustar chuckchuck[files] packages = pyghmi [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [global] setup-hooks = pbr.hooks.setup_hook [metadata] description-file = README name = pyghmi author = Jarrod Johnson home-page = http://xcat.sf.net/ summary = Python General Hardware Management Initiative (IPMI and others) classifier = Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 author-email = jbjohnso@us.ibm.com python-pyghmi-0.5.8/MANIFEST.in0000664000175000017500000000013612205461765015064 0ustar chuckchuckinclude AUTHORS include ChangeLog exclude .gitignore exclude .gitreview global-exclude *.pyc python-pyghmi-0.5.8/AUTHORS0000664000175000017500000000017212220616611014363 0ustar chuckchuckDevananda van der Veen Jarrod Johnson Monty Taylor python-pyghmi-0.5.8/ChangeLog0000664000175000017500000005702412220616611015075 0ustar chuckchuckcommit ffe493df0f13f45963268d68b349c9dbf809a39a Author: Jarrod Johnson Date: Tue Sep 24 09:49:53 2013 -0400 Use distinct exceptions for many cases Caller is likely going to want to catch certain conditions. For those, use more specific exception classes. Exceptions indicating either TODO or usage errors are being left as 'Exception' for the time being. Change-Id: I4d68a2dbc394b534d54586b9f770160c1409f720 commit ccac817f2beaabf252e762238eb86f9b3e034df5 Author: Jarrod Johnson Date: Sat Sep 14 21:36:44 2013 -0400 Enhance filehandle registration Previously, pyghmi only could have file objects registered. Accept and adjust as appropriate to accomodate plain int filedescriptors. This allows, for example, os.pipe() construct to be hooked. Change-Id: Ie5e81a2276c68e9235d373d4a662f7b1aef153f5 commit 35b50fb705f11eadf71b35c5065c220fdbf83e8b Author: Jarrod Johnson Date: Sat Sep 14 10:03:56 2013 -0400 Fix pyghmi behavior on BMC NACK When BMC sent NACK, pyghmi incorrectly attempted to construct new payload by concatenating a tuple and str. Fix by converting the tuple to str prior to sending. Change-Id: Ia07384651870bf01e7dc24757e1e00018ab84556 commit 781a7829be26bee0a9a78ca6ff49e0436be5a3f7 Author: Monty Taylor Date: Fri Sep 13 11:21:12 2013 -0500 Update from requirements Trying to remove usage of d2to1 - it can cause breakages. Also, had to fix hacking 102. Turns out it expects copyright headers in comments, not docstrings. Change-Id: I3494349ffe2d4cba9c8bcb73408d60bbc12eff5e commit 317417987faeb62018150b7c8fa99e0373c205e0 Author: Jarrod Johnson Date: Thu Sep 12 10:08:14 2013 -0400 Correct concatenation of string and int object Straightforward change, a mistake was made with a string and an int was concatenated. Change-Id: I96de0f32b387e524c90d42acb5dcf6c808448b14 commit bdbb6aa018784c398a9cd4d6c2662b9c9824941b Author: Jarrod Johnson Date: Wed Sep 11 16:32:27 2013 -0400 Expose wait_for_rsp class method via public classes. In complicated uses of pyghmi, it becomes useful to call into the otherwise private event loop iteration. This exposes the wait_for_rsp method so that it may be consumed without violating the expectations of private. Change-Id: Iee64615f0577b00895f8c3db25676c877107b0be commit f24395bbee4dbb8269c6817febf7be489e37d838 Author: Jarrod Johnson Date: Wed Sep 11 15:21:36 2013 -0400 Fix mistake with external filehandle and solconnect In changing to select, the nature of the return value failed. When using poll(), we got file descriptor number. However select provides the object and so we have to consult the fileno() method. Change-Id: Ic525b315024b164eed22238ad1e4a89eed0fcf0c commit 5832db6a303da1c4a5acdeb49da24b335624c555 Author: Jarrod Johnson Date: Mon Sep 9 15:02:25 2013 -0400 Change from poll() to select() poll is not available in all platforms. Additionally, eventlet does not handle poll in a monkey patching scenario. Use select() as the greatest common factor. Change-Id: I170d6efb1636249df4b7ed42eac6378ae317cb35 commit 5c5c6770ea3cb704297602a3c0aaf0887a8f6917 Author: Jarrod Johnson Date: Mon Sep 9 16:49:16 2013 -0400 Have callback recognize any callable Previously, callback had to be a function. This misses a lot of things, notably instance methods. Change to recognize any callable. Change-Id: I9d2a00fe53f7e8039f0badeb4af0fdd3c6339b89 commit ef22636766ea43837e6e7fc760f76ae1f7977b1d Merge: c13a5a3 c2ee99e Author: Jenkins Date: Thu Aug 22 21:20:30 2013 +0000 Merge "Add function to send sol data" commit c2ee99eb0d3c0869dfe5cfc8535b1f25e6781655 Author: Jarrod Johnson Date: Thu Aug 22 16:50:07 2013 -0400 Add function to send sol data In the case of using function callin/callout, the console module was missing a callin. Correct that omission. Change-Id: I233b43eadf115ad617958148878b96f38bff1971 commit e6d0e2ae0dbd8ded9c843312a40b2f41d62f6875 Merge: 3ed2ef8 cfe88ab Author: Jenkins Date: Thu Aug 22 20:08:25 2013 +0000 Merge "Finalize rename to pyghmi" commit c13a5a375da845af9684a4997dd19d15932efb67 Author: Jarrod Johnson Date: Thu Aug 22 15:57:03 2013 -0400 Add support for non standard ports. It is possible and in some cases required for an implementation to deviate from the IPMI standard port of 623. This enables the library to actually support that case. Change-Id: I62e322410924153cebde9827d7ba0d0583aa1d83 commit cfe88ab8756d14e4d53583b47ba5ff4a445a1d8c Author: Monty Taylor Date: Sat Aug 17 22:29:14 2013 -0400 Finalize rename to pyghmi Change-Id: I11999d644c6f583a458eaa504b46f1b0eba24b21 commit 3ed2ef8494cc77ab3fd5f2ffb847224d0f817040 Author: Jarrod Johnson Date: Wed Aug 14 14:48:19 2013 -0400 Fix raw handling by ipmictl.py The test harness was incorrectly throwing string values at the library. Correct that by explicitly converting from hex string to int. Additionally, command.py was failing to pass data through. Change-Id: Iceda4c3b4a382992d445f90dfbc19f6c75b528df commit 04a9b89d15e6e3305a5d1ddc167e8c77aefb6356 Author: Jarrod Johnson Date: Mon Aug 12 16:10:00 2013 -0400 Rework set_power handling of errors and noops set_power failed to raise when error encountered. The behavior is amended to be consistent with other functions. set_power requested on/off state even if system already was in requested state. Change to simply report success in that scenario. Change-Id: I6feb8f4384705136a5ab1fae0899ea27b2d3511c commit 7e4f5a381fd44931061db9301bd0d626f1eb6ad0 Merge: 5ac560d ebbf8c9 Author: Jenkins Date: Thu Aug 8 20:34:50 2013 +0000 Merge "Rename package from python-ipmi to pyghmi" commit 5ac560d7209b74462c590efbbf4005c91f834947 Merge: 939ebea c0f4959 Author: Jenkins Date: Thu Aug 8 20:34:38 2013 +0000 Merge "Change long timeout to select() from poll()" commit ebbf8c94e4e65550ea2b04e1ffadd8a1a4164b83 Author: Jarrod Johnson Date: Thu Aug 8 14:28:40 2013 -0400 Rename package from python-ipmi to pyghmi Some have expressed a concern that ipmi is both too generic a term and too limiting in scope. Change python-ipmi to pyghmi to more easily allow the project to be specifically be referenced and denote the likelihood of non-ipmi protocols being supported Change-Id: I9c96f2154050a2970fc217e0eeae733605a38bde commit c0f4959ed766b6b6341f315d3c3f33848468b16a Author: Jarrod Johnson Date: Thu Aug 8 14:19:09 2013 -0400 Change long timeout to select() from poll() Change from poll() to select() for the potentially long wait in ipmi event loop. This is to facilitate interop with being monkey patched with eventlet, as eventlet does not currently monkey patch poll(), but does monkey patch select() Change-Id: I8d6f42193ee82e2c5de7ad112232c2c77ef78825 commit 939ebea0f38231f4180ebd59f210117961717a59 Author: Jarrod Johnson Date: Thu Aug 8 13:31:36 2013 -0400 Enhance wait behavior of set_power Previously, wait would always be indefinite and act in many ways like a busy wait. The check for completion now takes one second between every check. The default behavior now goes for 300 seconds, but wait can now take an int to indicate a different preference by the caller. 300 seconds may seem like a lot, but the 'softoff' request in particular suggests waiting for OS shutdown process to complete. Additionally, some bugs in the retry logic were noted in the process of having the retry timer drive the delay and are corrected. Change-Id: Ibe34e87c2a58f13981d60e5f80b4b636e67ac3f9 commit 7371a58aba5f9833bbf6778a4508f2ad25d71c88 Author: Jarrod Johnson Date: Wed Aug 7 10:01:16 2013 -0400 Rename to pyghmi It has been expressed as a concern that 'ipmi' is too generic a name. Additionally, it is also the case that non-ipmi capability is likely to be incorporated as it goes along (e.g. Enclosure management and virtual media are frequently not IPMI based). Move existing content under the 'pyghmi' namespace. pyghmi stands for 'python general hardware management infrastructure' and is pronounced 'pygmy' Change-Id: Ib549a9f5b7dd549c7dc5ddbab251a2e06c572e41 commit b985624a4bc1de4e59a3b23fc42ea7e534a0d1dc Author: Jarrod Johnson Date: Thu Jul 25 14:19:09 2013 -0400 Remove callback arguments from most functions. After some contemplation, I realized that given the underlying behavior of 'wait_for_rsp', that synchronous and asynchronous calls play well with each other. The pattern of considering an 'onlogon' function as running in a sort of greenthread works well and leads to much more comprehensible code for the average person. While removing those arguments, the module contents were reorganized to serve as an example of how more straightforward code written in this fashion may be. Change-Id: I9c83660e4d3c68ad8ae0c016a3377b2a0e7ee6ec commit 86a2d39db7e93fbb6211e89276039135ec7c20a5 Author: Jarrod Johnson Date: Thu Jul 18 16:20:44 2013 -0400 Change session timer to monotonic when possible Previously the ipmi session was using time.time(). This means that retries and keepalives could be thrown off by things like ntp or manual time corrections. Ideally, we'd use the baked in time.monotonic(), but that doesn't exist aside from python 3.3 Change-Id: Ia00026cef6df214f9463909309de44767c3752b5 commit 0e8ca338423d805330bf924d5e11de57740a327c Author: Jarrod Johnson Date: Tue Jul 16 13:37:09 2013 -0400 PEP8 compliance Change instances in code where flake8 complained Change-Id: I0b125fcff39024f9d4bf3c4c26a863efd0fa5cba commit 51cf01f6c9ffba003f4bbbab2a9b9eb89530a1eb Merge: 9dd2c95 35fb5ce Author: Jenkins Date: Tue Jul 16 17:46:10 2013 +0000 Merge "Add raw command to ipmictl" commit 35fb5ce4b0062b5d92cf2cff7ab29f7bc2423aeb Author: Jarrod Johnson Date: Tue Jul 16 10:59:45 2013 -0400 Add raw command to ipmictl Enable raw command for example utility Change-Id: I44d71b5c4cab57e9465fabb2482a78baa505895d commit 9dd2c950a7b07ad4998c10abd8b53b5d7e4899f5 Author: Jarrod Johnson Date: Fri Jul 12 13:58:07 2013 -0400 Add session keepalive Assure that a live session shows activity at least once every 25-29.9 seconds. The interval is randomized to mitigate risk of some synchronized activity bringing on a wave of activity all at once. The spec indicated timeout is 'about' 60 seconds by default, but an implementation could lower it to 30 seconds within reason within the constraints of the spec. Change-Id: I74dc78757b11571c23cb309c99eee667787ffc79 commit 02e353f2fb699316c35dfb9e7002c82f6e474e45 Author: Jarrod Johnson Date: Tue Jul 2 17:03:24 2013 -0400 Add SOL support SOL support is added in a manner that is actually functional Change-Id: I3f83e06b27a0d44038ac6e6afcd4f8af1c534946 commit a7b4b4a71d80a85faebe8c726c3fc5a90bd9962b Author: Jarrod Johnson Date: Tue Jul 2 13:35:27 2013 -0400 Add more constants to ipmi constant list Allow use of the more helpful names for net function codes in ipmi specification Change-Id: I3c7bc71e5df1936fbeac6fcc65e144e1b52ea68a commit e9ef542b65a42c911909c50f7a3a0c4070ab9b9b Merge: e0e39bd 6360f2c Author: Jenkins Date: Thu Jul 4 16:56:11 2013 +0000 Merge "Correct spelling mistakes" commit e0e39bda9a4cc9a9b1973dd9546b1d11997348ff Author: Jarrod Johnson Date: Tue Jul 2 13:29:26 2013 -0400 Fix errors after git repo transition. I realized that I had never pushed __init__.py for private. While doing tests locally, I noticed that the pep changes broke at least one place in code, correct that. Change-Id: Ia31cb8a75a7109a6d002524e08ef335e1c5018d4 commit 6360f2ca9e9d3ac8b961bc3bf818ea7aee18fcc5 Author: Jarrod Johnson Date: Tue Jul 2 13:16:16 2013 -0400 Correct spelling mistakes Change-Id: I752dc17e8ae3e354253614bdbd5277ca1fd67469 commit 016326795bf19e34c73f296102fe14dec6fba865 Author: Jarrod Johnson Date: Tue Jul 2 11:16:57 2013 -0400 Rename _pack_payload to send_payload Rename '_pack_payload' in private module to 'send_payload' as it is more descriptive of what is happening and will be called by console.py Change-Id: Ifa70d269c8a1e37cc5b6466845931f22cd97d6c3 commit ca22a48de5a62a254f534f1081e03dda04918349 Author: Devananda van der Veen Date: Tue Jul 2 06:09:21 2013 -0700 Fix all pep8 errors except E128 and E501 Ran autopep8 on the four .py files in this module. Manully fixed many additional pep8 errors as well, Turned some long-line-comments into proper NOTE lines, but there are more to do... Change-Id: I657ba037863860ec3956150931c2c0e41085bd63 commit 08d2e54d6871fcb4ae588e9a562cca0b9a311cba Author: Monty Taylor Date: Sun Jun 30 14:06:53 2013 -0400 Align to OpenStack Build Standards. Rearranged Sphinx. Put project under PBR control. Added tox file. Added .testr.conf file in anticipation of testr testing. Change-Id: I55180a6a3d224816c82999bf66156c7874786b17 commit fdca1fc1999a07b468ff9a1546bb599bc92e4a57 Author: Monty Taylor Date: Sun Jun 30 13:58:19 2013 -0400 Added gitreview file. Change-Id: If8789bb129a45ad747e596a479a6a4466a5bafe0 commit 7eec73261d0e61214d6437afb32010592ee060bc Author: Jarrod Johnson Date: Fri Jun 28 10:35:41 2013 -0400 fix setup to include the private parts of the project commit 0a01fe446d57671293b35baa19e75622af021356 Author: Jarrod Johnson Date: Fri Jun 28 10:31:43 2013 -0400 Reorganize pieces into 'private' to suggest to consumers the bits tehy need not think too hard about commit 284621feaf87505d01688331d91576b99f1bf5ab Author: Jarrod Johnson Date: Fri Jun 28 10:12:57 2013 -0400 Change to setuptools commit f0e36d8027bd96002b14be3f210c62a2d9193e6f Author: Jarrod Johnson Date: Fri Jun 28 10:10:28 2013 -0400 Change requires to install_requires commit f333dbff8b8dc649c5aad9cd941eedd33ca63d54 Author: Jarrod Johnson Date: Fri Jun 28 10:01:40 2013 -0400 Attempt to indicate pycrypto dependency commit bc198382682ec2312a431bad7bf36f06fe5daaa3 Author: Jarrod Johnson Date: Thu Jun 27 15:56:19 2013 -0400 Reorganie things and add a setup.py for fun commit 05d3d05695fe5fa6f273ddab481f7bef885a28fa Author: Jarrod Johnson Date: Tue Jun 25 13:38:41 2013 -0400 More style changes commit 147381a7e37c5f389713680f3657065cf1fd2d22 Author: Jarrod Johnson Date: Tue Jun 25 13:11:14 2013 -0400 More cleanup, correct some syntax errors encountered trying to meet line length commit 1dde7b8a7050d104650862391b1851ec03304335 Author: Jarrod Johnson Date: Tue Jun 25 11:44:57 2013 -0400 More style reworking commit ab1b9815478582fc04b3f5024bee7bfd837b09f3 Author: Jarrod Johnson Date: Tue Jun 25 11:23:24 2013 -0400 Stylistic reworks commit d8c7f9d9037664d7b14da92449eaa6acae715110 Author: Jarrod Johnson Date: Tue Jun 25 11:16:07 2013 -0400 More stylistic changes to ipmi_command.py commit 4973d577e340aca75e50086e383fb495340240a0 Author: Jarrod Johnson Date: Tue Jun 25 10:43:02 2013 -0400 Rename ipmi_syncexample to something less tounge twisting commit eeb6cb255d4b1105bb23ab120d73613f695f9e4d Author: Jarrod Johnson Date: Tue Jun 25 10:39:58 2013 -0400 Rework docstrings commit 41141ea467a3ba51cec53e75dc1d0a880d388c7b Author: Jarrod Johnson Date: Tue Jun 25 08:25:06 2013 -0400 Reorder imports commit 552ae49c58d81338c4861272c2cf110505de911a Author: Jarrod Johnson Date: Tue Jun 25 08:20:16 2013 -0400 Change one conditional to perhaps be a bit more readable commit d660073f77dc07544ffb7baffe8b2bbb152bfce6 Author: Jarrod Johnson Date: Tue Jun 25 08:17:50 2013 -0400 Add name to TODOs that I put down commit 7517bce5f6ed862278fc109894d31ad8dc80b92b Author: Jarrod Johnson Date: Mon Jun 24 14:37:57 2013 -0400 Add LICENSE excerpt to each file commit 3afd2e17d3057b8206ad74a30f8afea6d01862f7 Author: Jarrod Johnson Date: Mon Jun 24 14:15:39 2013 -0400 Place project under Apache license 2.0 commit a38894664cc5513f961c7998d8a0b9416d426b70 Author: Jarrod Johnson Date: Mon Jun 24 13:43:15 2013 -0400 Get acceptable copyright message in place commit bef7a29e126f4469c734d125c26fbbd13454bd13 Author: Jarrod Johnson Date: Tue Jun 18 10:00:21 2013 -0400 Flesh out documentation commit 5fb6a95a9daed4411fe3488e32085c551cdc527d Author: Jarrod Johnson Date: Tue Jun 18 09:15:50 2013 -0400 Add sphinx 1.0 structure instead of older version commit 2212e11d344098c4a32b29f72d0a790adbb9a2aa Author: Jarrod Johnson Date: Tue Jun 18 09:13:28 2013 -0400 Revert "Add sphinx doc generator bits" This reverts commit 5c04ffb265cd99b66534f2a2668f5ff31448c26b. commit 5c04ffb265cd99b66534f2a2668f5ff31448c26b Author: Jarrod Johnson Date: Tue Jun 18 09:10:04 2013 -0400 Add sphinx doc generator bits commit fff7b3bd57fe94d7ac450da89d9a241ddc5c2f06 Author: Jarrod Johnson Date: Mon Jun 17 16:55:44 2013 -0400 Add an example chunk of code showing how to code in a synchronous way to the python library commit cfc6d6ec156c61789fb2d2ba2055c607f4d61957 Author: Jarrod Johnson Date: Mon Jun 17 16:26:57 2013 -0400 Add set_power, with wait argument to not return until new state confirmed commit 5c1713d792d4a4b84639d87ad2f8545b0a835993 Author: Jarrod Johnson Date: Mon Jun 17 15:02:11 2013 -0400 Add retry/timeout behavior to library commit 1ecd7c2146786fed3ff74f3069a6e40746b05f2a Author: Jarrod Johnson Date: Mon Jun 10 15:49:24 2013 -0400 Have get_power coded more similarly to get_bootdev commit b7e27470ccfdab50d1613efd55821d3e6abeee37 Author: Jarrod Johnson Date: Thu Jun 6 17:04:49 2013 -0400 Add boot device control functions commit 8c68b1e3ef95f0e10a2260722857277d2dc1b15e Author: Jarrod Johnson Date: Tue Jun 4 13:16:05 2013 -0400 Put IBM copyright into the files commit 2f4a85711893c2ba27b592b14fcf223810efcf2f Author: Jarrod Johnson Date: Mon May 20 11:17:10 2013 -0400 Add a convenience wrapper to do the higher layer simple commands commit 785e712763e9d994f39cef43195ba931ff51f124 Author: Jarrod Johnson Date: Mon May 20 10:49:59 2013 -0400 Fix up error status situation commit da9f801c1f834453be86a928b4beaa3bfada4459 Author: Jarrod Johnson Date: Mon May 20 10:44:12 2013 -0400 Clean up ipmi_session a little bit commit 74f3bf592f93a5ed07bace9fac7e5a01d8e73aa5 Author: Jarrod Johnson Date: Mon May 20 10:29:01 2013 -0400 Do some rewording commit c8ac83ccccf8d069a0f60117f1d2b5a7c0879a60 Author: Jarrod Johnson Date: Sun May 19 18:51:42 2013 -0400 Fix the integrity pad calculation (shame python doesn't have a 'use strict') commit f44ad79391b7ddaf4ee62d310137d46853de43e8 Author: Jarrod Johnson Date: Sun May 19 18:26:42 2013 -0400 Remove developer prints commit 7c44577b6c725f6bfa50925e3d6f115fda8d3cf8 Author: Jarrod Johnson Date: Sun May 19 18:20:27 2013 -0400 Fix decryption... mystery remains as to why logout and only logout seems to fail if aes is not enabled in ipmi 2.0 land.. commit d6db94795e0ad6d4b7616542f010556fa39b15ae Author: Jarrod Johnson Date: Sun May 19 18:02:57 2013 -0400 Have IPMI2 work for unencrypted, still sorting out decryption... encryption seems to work, but decrypting the response... commit 40509f59cf4fb102e51bac2446a8d1bb8a6dea94 Author: Jarrod Johnson Date: Sun May 19 17:23:36 2013 -0400 Fix problem where the data from bmc was not being integrity checked in 1.5 commit 86ebe9790d42d9dfdd23d1d6599f138a532e707c Author: Jarrod Johnson Date: Sun May 19 17:14:22 2013 -0400 Fix problems with k1 and k2 key generation and HMAC on outgoing commit 4a1a43bbe973622b1fd277e5cd131484987e7345 Author: Jarrod Johnson Date: Sun May 19 16:28:46 2013 -0400 Almost working IPMI 2.0 support..... disable encryption to facilitate/narrow debug... currently integrity algorithm is apparantly failing to pass on *outgoing* packets, though RAKP4 incoming did pass commit 2b90f8184f94d295063e20deaeff490dbcb6d7ee Author: Jarrod Johnson Date: Sun May 19 10:06:01 2013 -0400 More forcefully note the TODOs so people know when they hit them commit 5248d821b5f01e5a3f83f640d47365a05271cf29 Author: Jarrod Johnson Date: Sun May 19 10:02:20 2013 -0400 Python ipmi can now actually do commands using 1.5 commit df43c5336be90365ac6707873d86497b15b8adf2 Author: Jarrod Johnson Date: Sat May 18 14:29:43 2013 -0400 More work to make it get further on session establishment commit 9269810e7cb7dbe0ed614bc7b6f14d382e7bea06 Author: Jarrod Johnson Date: Sat May 18 13:37:01 2013 -0400 Milestone: can now receive and correctly parse get channel auth capabilities result commit 6b575b84d2db1c58da47ec5ceee50e9c1b6db88c Author: Jarrod Johnson Date: Fri May 17 23:20:24 2013 -0400 Milestone: now able to craft valid Get channel authentication capabilities request and trigger response commit b1fd359d4008d3ad864ede30f2442dfa83078753 Author: Jarrod Johnson Date: Fri May 17 22:39:00 2013 -0400 Get a lot closer to a working ipmi python implementation commit dbe689882ad80e68750812950082d741e097b499 Author: Jarrod Johnson Date: Fri May 17 17:28:58 2013 -0400 Break out constantns to a diff file commit 507ee81db43e68ef5d93fd61509749e15e71cbe3 Author: Jarrod Johnson Date: Fri May 17 17:28:31 2013 -0400 More progress on perl to python port of ipmi commit 9552eb8e0f9d042119f4ff7d3656043f3f6f0eb1 Author: Jarrod Johnson Date: Thu May 16 15:43:17 2013 -0400 Kick things off with a little bit of header datapython-pyghmi-0.5.8/pyghmi.egg-info/0000775000175000017500000000000012220616611016302 5ustar chuckchuckpython-pyghmi-0.5.8/pyghmi.egg-info/top_level.txt0000664000175000017500000000000712220616611021031 0ustar chuckchuckpyghmi python-pyghmi-0.5.8/pyghmi.egg-info/not-zip-safe0000664000175000017500000000000112214647237020543 0ustar chuckchuck python-pyghmi-0.5.8/pyghmi.egg-info/requires.txt0000664000175000017500000000001512220616611020676 0ustar chuckchuckpycrypto>=2.6python-pyghmi-0.5.8/pyghmi.egg-info/SOURCES.txt0000664000175000017500000000107312220616611020167 0ustar chuckchuck.testr.conf AUTHORS ChangeLog LICENSE MANIFEST.in README ipmictl.py requirements.txt setup.cfg setup.py solconnect.py test-requirements.txt tox.ini doc/source/conf.py doc/source/index.rst pyghmi/__init__.py pyghmi/exceptions.py pyghmi.egg-info/PKG-INFO pyghmi.egg-info/SOURCES.txt pyghmi.egg-info/dependency_links.txt pyghmi.egg-info/not-zip-safe pyghmi.egg-info/requires.txt pyghmi.egg-info/top_level.txt pyghmi/ipmi/__init__.py pyghmi/ipmi/command.py pyghmi/ipmi/console.py pyghmi/ipmi/private/__init__.py pyghmi/ipmi/private/constants.py pyghmi/ipmi/private/session.pypython-pyghmi-0.5.8/pyghmi.egg-info/PKG-INFO0000664000175000017500000000154612220616611017405 0ustar chuckchuckMetadata-Version: 1.0 Name: pyghmi Version: 0.5.8 Summary: Python General Hardware Management Initiative (IPMI and others) Home-page: http://xcat.sf.net/ Author: Jarrod Johnson Author-email: jbjohnso@us.ibm.com License: UNKNOWN Description: This is a pure python implementation of IPMI protocol. ipmictl.py is a sample application to roughly show the most simple approach to invoking the library. Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 python-pyghmi-0.5.8/pyghmi.egg-info/dependency_links.txt0000664000175000017500000000000112220616611022350 0ustar chuckchuck