diff --git a/ipmi/command.py b/ipmi/command.py index de91796d..1fa289e4 100644 --- a/ipmi/command.py +++ b/ipmi/command.py @@ -81,17 +81,17 @@ class Command(object): # TODO(jbjohnso): accept tuples and lists of each parameter for mass # operations without pushing the async complexities up the stack self.ipmi_session = session.Session(bmc=bmc, - userid=userid, - password=password, - kg=kg) + userid=userid, + password=password, + kg=kg) def get_bootdev(self, callback=None, callback_args=None): """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. + 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. :param callback: optional callback :param callback_args: optional arguments to callback @@ -114,13 +114,16 @@ class Command(object): return self.lastresponse return True - def set_power(self, powerstate, wait=False, callback=None, callback_args=None): + def set_power(self, powerstate, wait=False, callback=None, + callback_args=None): """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 + * 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' @@ -141,11 +144,11 @@ class Command(object): self.wait_for_power = wait self.ipmi_session.raw_command(netfn=0, command=1, - callback=self._set_power_with_chassis_info + callback=self._set_power_with_chassisinfo ) return self._waitifsync() - def _set_power_with_chassis_info(self, response): + def _set_power_with_chassisinfo(self, response): if 'error' in response: _raiseorcall( self.commandcallback, response, self.commandcallbackargs) @@ -177,8 +180,8 @@ class Command(object): self.requestpending = False if self.commandcallback: session.call_with_optional_args(self.commandcallback, - self.lastresponse, - self.commandcallbackargs) + self.lastresponse, + self.commandcallbackargs) def _power_wait(self, response): if 'error' in response: @@ -191,8 +194,8 @@ class Command(object): self.lastresponse = {'powerstate': self.powerstate} if self.commandcallback: session.call_with_optional_args(self.commandcallback, - self.lastresponse, - self.commandcallbackargs) + self.lastresponse, + self.commandcallbackargs) return self.ipmi_session.raw_command(netfn=0, command=1, @@ -211,9 +214,11 @@ class Command(object): *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 + *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. @@ -321,8 +326,8 @@ class Command(object): self.lastresponse = {'bootdev': bootnum} if self.commandcallback: session.call_with_optional_args(self.commandcallback, - self.lastresponse, - self.commandcallbackargs) + self.lastresponse, + self.commandcallbackargs) def get_power(self, callback=None, callback_args=None): """Get current power state of the managed system @@ -352,5 +357,5 @@ class Command(object): self.lastresponse = {'powerstate': self.powerstate} if self.commandcallback: session.call_with_optional_args(self.commandcallback, - self.lastresponse, - self.commandcallbackargs) + self.lastresponse, + self.commandcallbackargs) diff --git a/ipmi/console.py b/ipmi/console.py index 99e5ba1f..e0073eb2 100644 --- a/ipmi/console.py +++ b/ipmi/console.py @@ -19,9 +19,11 @@ import fcntl import os import struct +import types -from ipmi.private import session from ipmi.private import constants +from ipmi.private import session + class Console(object): """IPMI SOL class. @@ -42,13 +44,13 @@ class Console(object): def __init__(self, bmc, userid, password, iohandler=None, force=False, kg=None): - if type(iohandler) == tuple: #two file handles + 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 + elif type(iohandler) == file: # one full duplex file handle self.console_out = iohandler self.console_in = iohandler - elif type(iohander) == types.FunctionType: + elif isinstance(iohandler, types.FunctionType): self.console_out = None self.console_in = None self.out_handler = iohandler @@ -63,15 +65,14 @@ class Console(object): self.ackedcount = 0 self.ackedseq = 0 self.retriedpayload = 0 - self.pendingoutput="" - self.awaitingack=False + self.pendingoutput = "" + self.awaitingack = False self.force_session = force self.ipmi_session = session.Session(bmc=bmc, - userid=userid, - password=password, - kg=kg, - onlogon=self._got_session - ) + userid=userid, + password=password, + kg=kg, + onlogon=self._got_session) def _got_session(self, response): """Private function to navigate SOL payload activation @@ -104,15 +105,15 @@ class Console(object): 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']]) + constants.ipmi_completion_codes[response['code']]) return elif response['code'] == 0x80: if self.force_session and not self.retriedpayload: - self.retriedpayload=1 + self.retriedpayload = 1 self.ipmi_session.raw_command(netfn=0x6, command=0x49, data=(1, 1, 0, 0, 0, 0), callback=self._got_session) @@ -125,8 +126,8 @@ class Console(object): return else: self._print_data( - 'SOL encountered Unrecognized error code %d\n' % - response['code']) + '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'] @@ -138,12 +139,12 @@ class Console(object): raise Exception("TODO(jbjohnso): support atypical 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 + 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): + def _got_cons_input(self, handle): """Callback for handle events detected by ipmi session """ self.pendingoutput += handle.read() @@ -156,19 +157,19 @@ class Console(object): if self.myseq == 0: self.myseq = 1 payload = struct.pack("BBBB", - self.myseq, - self.ackedseq, - self.ackedseq, - self.sendbreak) + 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.lastpayload = payload self.ipmi_session.send_payload(payload, payload_type=1) - def _print_data(self,data): + 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 @@ -178,7 +179,7 @@ class Console(object): if self.console_out is not None: self.console_out.write(data) self.console_out.flush() - elif self.out_handler: #callback style.. + elif self.out_handler: # callback style.. self.out_handler(data) def _got_sol_payload(self, payload): @@ -197,41 +198,41 @@ class Console(object): #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 newseq != 0: # this packet at least has some data to send to us.. if len(payload) > 4: - remdatalen = len(payload[4:]) #store remote data len before dupe + 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 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... + else: # no new data... remdata = "" - else: #TODO(jbjohnso) what if remote sequence number is wrong?? + else: # TODO(jbjohnso) what if remote sequence number is wrong?? self.remseq = newseq - self.lastsize=remdatalen + 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 our last xmit - self.awaitingack=False - if nacked > 0: #the BMC was in some way unhappy + 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 + 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:] self.pendingoutput = newtext + self.pendingoutput self._sendpendingoutput() - elif self.awaitingack: #session has marked us as happy, but we are not + 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, @@ -247,7 +248,7 @@ class Console(object): """ #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 from our own + #TODO(jbjohnso): wait_for_rsp is not returning a true value for our own #session while (1): session.Session.wait_for_rsp(timeout=600) diff --git a/ipmi/private/session.py b/ipmi/private/session.py index 09830adc..de9d6cd8 100644 --- a/ipmi/private/session.py +++ b/ipmi/private/session.py @@ -305,16 +305,16 @@ class Session: else: self.ipmicallback = callback self._send_ipmi_net_payload(netfn, command, data, retry=retry) - if retry: #in retry case, let the retry timers auto-indicate wait time - timeout=None - else: #if not retry, give it a second before surrending - timeout=1 + 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 python-ipmi 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 manner governed by + #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: @@ -332,10 +332,10 @@ class Session: #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 a more complicated + # and we also avoid having to do more complicated # retry mechanism where each payload is # retried separately - self.pendingpayloads.append((payload,payload_type,retry)) + self.pendingpayloads.append((payload, payload_type, retry)) return if payload_type is None: payload_type = self.last_payload_type @@ -389,8 +389,8 @@ class Session: payloadtocrypt = _aespad(payload) crypter = AES.new(self.aeskey, AES.MODE_CBC, iv) crypted = crypter.encrypt(struct.pack("%dB" % - len(payloadtocrypt), - *payloadtocrypt)) + len(payloadtocrypt), + *payloadtocrypt)) crypted = list(struct.unpack("%dB" % len(crypted), crypted)) message += crypted else: # no confidetiality algorithm @@ -411,21 +411,21 @@ class Session: integdata = message[4:] authcode = HMAC.new(self.k1, struct.pack("%dB" % len(integdata), - *integdata), + *integdata), SHA).digest()[:12] # SHA1-96 - # per RFC2404 truncates to 96 bits + # 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 slinging packets + #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'] = time.time() + 25 + \ - (random.random() * 4.9) + (random.random() * 4.9) self._xmit_packet(retry) def _ipmi15authcode(self, payload, checkremotecode=False): - if self.authtype == 0: # Only for things prior to auth in ipmi 1.5, not - # like 2.0 cipher suite 0 + 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) @@ -435,14 +435,14 @@ class Session: passdata = struct.unpack("16B", password) if checkremotecode: seqbytes = struct.unpack("!4B", - struct.pack("> 2 != self.expectednetfn or - payload[5] != self.expectedcmd): - return -1 # this payload is not a match for our outstanding packet + payload[1] >> 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.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) + Session.waiting_sessions.pop(self, None) self.lastpayload = None # render retry mechanism utterly incapable of - #doing anything, though it shouldn't matter + # 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 + # ^^ 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] @@ -1003,7 +1010,7 @@ class Session: self.pendingpayloads.popleft() self.send_payload(payload=nextpayload, payload_type=nextpayloadtype, - retry=nextretry) + retry=retry) call_with_optional_args(self.ipmicallback, response, self.ipmicallbackargs) @@ -1036,7 +1043,7 @@ class Session: 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 + # 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 @@ -1059,7 +1066,7 @@ class Session: Session.waiting_sessions[self] = {} Session.waiting_sessions[self]['ipmisession'] = self Session.waiting_sessions[self]['timeout'] = self.timeout + \ - time.time() + time.time() Session.pending += 1 if self.sockaddr: Session.socket.sendto(self.netpacket, self.sockaddr) @@ -1070,7 +1077,7 @@ class Session: 0, socket.SOCK_DGRAM): sockaddr = res[4] - if (res[0] == socket.AF_INET): #convert the sockaddr AF_INET6 + if (res[0] == socket.AF_INET): # convert the sockaddr AF_INET6 newhost = '::ffff:' + sockaddr[0] sockaddr = (newhost, sockaddr[1], 0, 0) Session.bmc_handlers[sockaddr] = self @@ -1088,7 +1095,7 @@ class Session: self.raw_command(command=0x3c, netfn=6, data=struct.unpack("4B", - struct.pack("I", self.sessionid)), + struct.pack("I", self.sessionid)), retry=False, callback=callback, callback_args=callback_args) diff --git a/ipmictl.py b/ipmictl.py index ffa33d08..e02ab7b3 100755 --- a/ipmictl.py +++ b/ipmictl.py @@ -38,12 +38,12 @@ if len(sys.argv) >= 5: args = sys.argv[4:] ipmicmd = Command(bmc=bmc, userid=userid, password=password) if command == 'power': - if args[0]: + if args: print ipmicmd.set_power(args[0], wait=True) else: print ipmicmd.get_power() elif command == 'bootdev': - if args[0]: + if args: print ipmicmd.set_bootdev(args[0]) else: print ipmicmd.get_bootdev() diff --git a/solconnect.py b/solconnect.py index c0baf725..71022351 100644 --- a/solconnect.py +++ b/solconnect.py @@ -20,16 +20,14 @@ limitations under the License. """ import os import sys -import fcntl - -import tty import termios +import tty from ipmi import console tcattr = termios.tcgetattr(sys.stdin) newtcattr = tcattr -#TODO: allow ctrl-c and crtl-z to go to remote console, add our own exit handler +#TODO(jbjohnso): add our exit handler newtcattr[-1][termios.VINTR] = 0 newtcattr[-1][termios.VSUSP] = 0 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, newtcattr)