+ else:
+ raise Exception("Unknown transport type: %s" % self.type)
+
+ self.transport = transport
+ return True
+
+ def close(self):
+ if self.type == self.TELNET:
+ self.transport.close()
+ elif self.type == self.SSH:
+ self.transport.close()
+ elif self.type == self.HTTP:
+ pass
+ else:
+ raise Exception("Unknown transport type %s" % self.type)
+ self.transport = None
+
+ def sendHTTP(self, resource, data):
+ if self.verbose:
+ print "POSTing '%s' to %s" % (data,self.url + resource)
+
+ try:
+ f = self.transport.open(self.url + resource ,data)
+ r = f.read()
+ if self.verbose:
+ print r
+
+ except urllib2.URLError,err:
+ logger.info('Could not open http connection', err)
+ return "http transport error"
+
+ return 0
+
+ def sendPassword(self, password, prompt=None):
+ if self.type == self.TELNET:
+ if prompt == None:
+ self.ifThenSend("Password", password, ExceptionPassword)
+ else:
+ self.ifThenSend(prompt, password, ExceptionPassword)
+ elif self.type == self.SSH:
+ self.ifThenSend("password:", password, ExceptionPassword)
+ elif self.type == self.HTTP:
+ pass
+ else:
+ raise Exception("Unknown transport type: %s" % self.type)
+
+ def ifThenSend(self, expected, buffer, ErrorClass=ExceptionPrompt):
+
+ if self.transport != None:
+ output = self.transport.read_until(expected, self.TELNET_TIMEOUT)
+ if output.find(expected) == -1:
+ raise ErrorClass, "'%s' not found" % expected
+ else:
+ self.transport.write(buffer + "\r\n")
+ else:
+ raise ExceptionNoTransport("transport object is type None")
+
+ def ifElse(self, expected, ErrorClass):
+ try:
+ self.transport.read_until(expected, self.TELNET_TIMEOUT)
+ except:
+ raise ErrorClass("Could not find '%s' within timeout" % expected)
+
+
+class PCUControl(Transport,PCUModel,PCURecord):
+ def __init__(self, plc_pcu_record, verbose, supported_ports=[]):
+ PCUModel.__init__(self, plc_pcu_record)
+ PCURecord.__init__(self, plc_pcu_record)
+ type = None
+ if self.portstatus:
+ if '22' in supported_ports and self.portstatus['22'] == "open":
+ type = Transport.SSH
+ elif '23' in supported_ports and self.portstatus['23'] == "open":
+ type = Transport.TELNET
+ elif '80' in supported_ports and self.portstatus['80'] == "open":
+ type = Transport.HTTP
+ elif '443' in supported_ports and self.portstatus['443'] == "open":
+ type = Transport.HTTP
+ elif '5869' in supported_ports and self.portstatus['5869'] == "open":
+ # For DRAC cards. Racadm opens this port.
+ type = Transport.HTTP
+ elif '9100' in supported_ports and self.portstatus['9100'] == "open":
+ type = Transport.IPAL
+ elif '16992' in supported_ports and self.portstatus['16992'] == "open":
+ type = Transport.HTTP
+ else:
+ raise ExceptionPort("Unsupported Port: No transport from open ports")
+ else:
+ raise Exception("No Portstatus: No transport because no open ports")
+ Transport.__init__(self, type, verbose)
+
+ def run(self, node_port, dryrun):
+ """ This function is to be defined by the specific PCU instance. """
+ pass
+
+ def reboot(self, node_port, dryrun):
+ try:
+ return self.run(node_port, dryrun)
+ except ExceptionNotFound, err:
+ return "error: " + str(err)
+ except ExceptionPassword, err:
+ return "password exception: " + str(err)
+ except ExceptionTimeout, err:
+ return "timeout exception: " + str(err)
+ except ExceptionUsername, err:
+ return "exception: no username prompt: " + str(err)
+ except ExceptionSequence, err:
+ return "sequence error: " + str(err)
+ except ExceptionPrompt, err:
+ return "prompt exception: " + str(err)
+ except ExceptionPort, err:
+ return "no ports exception: " + str(err)
+ except socket.error, err:
+ return "socket error: timeout: " + str(err)
+ except EOFError, err:
+ if self.verbose:
+ logger.debug("reboot: EOF")
+ logger.debug(err)
+ self.transport.close()
+ import traceback
+ traceback.print_exc()
+ return "EOF connection reset" + str(err)
+
+class IPAL(PCUControl):
+ """
+ This now uses a proprietary format for communicating with the PCU. I
+ prefer it to Telnet, and Web access, since it's much lighter weight
+ and, more importantly, IT WORKS!! HHAHHHAHAHAHAHAHA!
+ """
+
+ def format_msg(self, data, cmd):
+ esc = chr(int('1b',16))
+ return "%c%s%c%s%c" % (esc, self.password, esc, data, cmd) # esc, 'q', chr(4))
+
+ def recv_noblock(self, s, count):
+ import errno
+
+ try:
+ # TODO: make sleep backoff, before stopping.
+ time.sleep(4)
+ ret = s.recv(count, socket.MSG_DONTWAIT)
+ except socket.error, e:
+ if e[0] == errno.EAGAIN:
+ raise Exception(e[1])
+ else:
+ # TODO: not other exceptions.
+ raise Exception(e)
+ return ret
+
+ def run(self, node_port, dryrun):
+ import errno
+
+ power_on = False
+
+ print "open socket"
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ print "connect"
+ s.connect((self.host, 9100))
+ except socket.error, e:
+ s.close()
+ if e[0] == errno.ECONNREFUSED:
+ # cannot connect to remote host
+ raise Exception(e[1])
+ else:
+ # TODO: what other conditions are there?
+ raise Exception(e)
+
+ # get current status
+ print "Checking status"
+ s.send(self.format_msg("", 'O'))
+ ret = self.recv_noblock(s, 8)
+ print "Current status is '%s'" % ret
+
+ if ret == '':
+ raise Exception("Status returned 'another session already open' %s : %s" % (node_port, ret))
+
+
+ if node_port < len(ret):
+ status = ret[node_port]
+ if status == '1':
+ # up
+ power_on = True
+ elif status == '0':
+ # down
+ power_on = False
+ else:
+ raise Exception("Unknown status for PCU socket %s : %s" % (node_port, ret))
+ else:
+ raise Exception("Mismatch between configured port and PCU status: %s %s" % (node_port, ret))
+