3 # Reboot specified nodes
9 import errno, time, traceback
14 from subprocess import PIPE, Popen
15 import pcucontrol.transports.ssh.pxssh as pxssh
16 import pcucontrol.transports.ssh.pexpect as pexpect
21 # Use our versions of telnetlib and pyssh
22 sys.path.insert(0, os.path.dirname(sys.argv[0]))
23 import pcucontrol.transports.telnetlib as telnetlib
24 sys.path.insert(0, os.path.dirname(sys.argv[0]) + "/pyssh")
25 import pcucontrol.transports.pyssh as pyssh
27 # Event class ID from pcu events
28 #NODE_POWER_CONTROL = 3
31 #MONITOR_USER_ID = 11142
37 class ExceptionNoTransport(Exception): pass
38 class ExceptionNotFound(Exception): pass
39 class ExceptionPassword(Exception): pass
40 class ExceptionTimeout(Exception): pass
41 class ExceptionPrompt(Exception): pass
42 class ExceptionSequence(Exception): pass
43 class ExceptionReset(Exception): pass
44 class ExceptionPort(Exception): pass
45 class ExceptionUsername(Exception): pass
49 # PCU has model, host, preferred-port, user, passwd,
51 # This is an object derived directly form the PLCAPI DB fields
53 def __init__(self, plc_pcu_dict):
54 for field in ['username', 'password', 'site_id',
57 'node_ids', 'ports', ]:
58 if field in plc_pcu_dict:
59 if type(u"") == type(plc_pcu_dict[field]):
60 # NOTE: if is a unicode string, convert it.
61 self.__setattr__(field, str(plc_pcu_dict[field]))
63 self.__setattr__(field, plc_pcu_dict[field])
65 raise Exception("No such field %s in PCU object" % field)
67 # These are the convenience functions build around the PCU object.
69 def __init__(self, plc_pcu_dict):
70 PCU.__init__(self, plc_pcu_dict)
71 self.host = self.pcu_name()
74 if self.hostname is not None and self.hostname is not "":
76 elif self.ip is not None and self.ip is not "":
81 def nodeidToPort(self, node_id):
82 if node_id in self.node_ids:
83 for i in range(0, len(self.node_ids)):
84 if node_id == self.node_ids[i]:
87 raise Exception("No such Node ID: %d" % node_id)
89 # This class captures the observed pcu records from FindBadPCUs.py
91 def __init__(self, pcu_record_dict):
92 for field in ['port_status',
95 if field in pcu_record_dict:
97 self.__setattr__("reboot_str", pcu_record_dict[field])
99 self.__setattr__(field, pcu_record_dict[field])
101 # raise Exception("No such field %s in pcu record dict" % field)
124 def __init__(self, type, verbose):
126 self.verbose = verbose
127 self.transport = None
129 def open(self, host, username=None, password=None, prompt="User Name"):
132 if self.type == self.TELNET:
133 transport = telnetlib.Telnet(host, timeout=self.TELNET_TIMEOUT)
134 transport.set_debuglevel(self.verbose)
135 if username is not None:
136 self.transport = transport
137 self.ifThenSend(prompt, username, ExceptionUsername)
139 elif self.type == self.SSH:
140 if username is not None:
141 transport = pyssh.Ssh(username, host)
142 transport.set_debuglevel(self.verbose)
144 # TODO: have an ssh set_debuglevel() also...
146 raise Exception("Username cannot be None for ssh transport.")
147 elif self.type == self.HTTP:
148 # NOTE: this does not work for all web-based services...
149 self.url = "http://%s:%d/" % (host,80)
150 uri = "%s:%d" % (host,80)
153 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm()
154 authinfo.add_password (None, uri, username, password)
155 authhandler = urllib2.HTTPBasicAuthHandler( authinfo )
157 transport = urllib2.build_opener(authhandler)
159 raise Exception("Unknown transport type: %s" % self.type)
161 self.transport = transport
165 if self.type == self.TELNET:
166 self.transport.close()
167 elif self.type == self.SSH:
168 self.transport.close()
169 elif self.type == self.HTTP:
172 raise Exception("Unknown transport type %s" % self.type)
173 self.transport = None
175 def write(self, msg):
176 return self.send(msg)
179 if self.transport == None:
180 raise ExceptionNoTransport("transport object is type None")
182 return self.transport.write(msg)
184 def sendPassword(self, password, prompt=None):
185 if self.type == self.TELNET:
187 self.ifThenSend("Password", password, ExceptionPassword)
189 self.ifThenSend(prompt, password, ExceptionPassword)
190 elif self.type == self.SSH:
191 self.ifThenSend("password:", password, ExceptionPassword)
192 elif self.type == self.HTTP:
195 raise Exception("Unknown transport type: %s" % self.type)
197 def sendHTTP(self, resource, data):
199 print "POSTing '%s' to %s" % (data,self.url + resource)
202 f = self.transport.open(self.url + resource ,data)
207 except urllib2.URLError,err:
208 print 'Could not open http connection', err
209 return "http transport error"
213 def ifThenSend(self, expected, buffer, ErrorClass=ExceptionPrompt):
215 if self.transport != None:
216 output = self.transport.read_until(expected, self.TELNET_TIMEOUT)
217 if output.find(expected) == -1:
218 print "OUTPUT: --%s--" % output
219 raise ErrorClass, "'%s' not found" % expected
221 self.transport.write(buffer + "\r\n")
223 raise ExceptionNoTransport("transport object is type None")
225 def ifElse(self, expected, ErrorClass):
227 self.transport.read_until(expected, self.TELNET_TIMEOUT)
229 raise ErrorClass("Could not find '%s' within timeout" % expected)
231 class PCUControl(PCUModel,PCURecord):
234 There are three cases:
235 1) the pcu_record passed below includes port_status from an
237 2) the external probe failed, and the values are empty
238 3) this call is made independent of port_status.
240 In the first case, the first open port is used.
241 In the third case, the ports are tried in sequence.
243 In this way, the port_status value serves only as an optimization,
244 because closed ports are avoided. The supported_ports value should
245 order ports by their preferred usage.
250 def __init__(self, plc_pcu_record, verbose, ignored=None):
251 PCUModel.__init__(self, plc_pcu_record)
252 PCURecord.__init__(self, plc_pcu_record)
254 def reboot(self, node_port, dryrun):
257 # There are two sources of potential ports. Those that are open and
258 # those that are part of the PCU's supported_ports.
259 # I think we should start with supported_ports and then filter that
262 port_list = self.supported_ports
264 if hasattr(self, 'port_status') and self.port_status:
265 # get out the open ports
266 port_list = filter(lambda x: self.port_status[x] == "open" , self.port_status.keys())
267 port_list = [ int(x) for x in port_list ]
268 # take only the open ports that are supported_ports
269 port_list = filter(lambda x: x in self.supported_ports, port_list)
271 raise ExceptionPort("No Open Port: No transport from open ports")
275 ret = "No implementation for open ports on selected PCU model"
276 for port in port_list:
277 if port not in Transport.porttypemap:
280 type = Transport.porttypemap[port]
281 self.transport = Transport(type, verbose)
283 print "checking for run_%s" % type
284 if hasattr(self, "run_%s" % type):
285 print "found run_%s" % type
286 fxn = getattr(self, "run_%s" % type)
287 ret = self.catcherror(fxn, node_port, dryrun)
288 if ret == 0: # NOTE: success!, so stop
295 def run(self, node_port, dryrun):
296 """ This function is to be defined by the specific PCU instance. """
297 raise Exception("This function is not implemented")
300 #def reboot(self, node_port, dryrun):
302 def catcherror(self, function, node_port, dryrun):
304 return function(node_port, dryrun)
305 except ExceptionNotFound, err:
306 return "error: " + str(err)
307 except ExceptionPassword, err:
308 return "Password exception: " + str(err)
309 except ExceptionTimeout, err:
310 return "Timeout exception: " + str(err)
311 except ExceptionUsername, err:
312 return "No username prompt: " + str(err)
313 except ExceptionSequence, err:
314 return "Sequence error: " + str(err)
315 except ExceptionPrompt, err:
316 return "Prompt exception: " + str(err)
317 except ExceptionNoTransport, err:
318 return "No Transport: " + str(err)
319 except ExceptionPort, err:
320 return "No ports exception: " + str(err)
321 except socket.error, err:
322 return "socket error: timeout: " + str(err)
323 except urllib2.HTTPError, err:
324 return "HTTPError: " + str(err)
325 except urllib2.URLError, err:
326 return "URLError: " + str(err)
327 except EOFError, err:
328 self.transport.close()
330 traceback.print_exc()
331 return "EOF connection reset" + str(err)
332 except Exception, err:
333 #from monitor.common import email_exception
334 #email_exception(self.host)
337 from pcucontrol.util import command
338 from pcucontrol.models import *
341 if pcu['hostname'] is not None and pcu['hostname'] is not "":
342 return pcu['hostname']
343 elif pcu['ip'] is not None and pcu['ip'] is not "":
348 class Unknown(PCUControl):
349 supported_ports = [22,23,80,443,5869,9100,16992]
351 def model_to_object(modelname):
352 if modelname is None:
354 if "AMT" in modelname:
356 elif "BayTech" in modelname:
358 elif "HPiLO" in modelname:
360 elif "IPAL" in modelname:
362 elif "APC" in modelname:
364 elif "DRAC" in modelname:
366 elif "WTI" in modelname:
368 elif "ePowerSwitch" in modelname:
369 return ePowerSwitchNew
370 elif "IPMI" in modelname:
372 elif "BlackBoxPSMaverick" in modelname:
373 return BlackBoxPSMaverick
374 elif "PM211MIP" in modelname:
376 elif "ManualPCU" in modelname:
379 print "UNKNOWN model %s"%modelname
382 def reboot_api(node, pcu):
386 modelname = pcu['model']
388 # get object instance
389 instance = eval('%s(pcu, verbose)' % modelname)
391 i = pcu['node_ids'].index(node['node_id'])
394 rb_ret = instance.reboot(p, False)
396 rb_ret = "No modelname in PCU record."
397 # TODO: how to handle the weird, georgetown pcus, the drac faults, and ilo faults
398 except Exception, err:
399 rb_ret = "Exception Model(%s): " % modelname
404 def convert_oldmodelname_to_newmodelname(oldmodelname, pcu_id):
406 update = { 'AP79xx' : 'APCControl13p13',
407 'Masterswitch' : 'APCControl13p13',
408 'DS4-RPC' : 'BayTech',
409 'IP-41x_IP-81x' : 'IPAL',
412 'ePowerSwitch' : 'ePowerSwitchOld',
415 'PM211-MIP' : 'PM211MIP',
416 'AMT2.5' : 'IntelAMT',
417 'AMT3.0' : 'IntelAMT',
418 'WTI_IPS-4' : 'WTIIPS4',
419 'unknown' : 'ManualPCU',
422 'bbsemaverick' : 'BlackBoxPSMaverick',
423 'manualadmin' : 'ManualPCU',
426 if oldmodelname in update:
427 newmodelname = update[oldmodelname]
429 newmodelname = oldmodelname
431 if pcu_id in [1102,1163,1055,1111,1231,1113,1127,1128,1148]:
432 newmodelname = 'APCControl12p3'
433 elif pcu_id in [1110,86]:
434 newmodelname = 'APCControl1p4'
435 elif pcu_id in [1221,1225,1220,1192]:
436 newmodelname = 'APCControl121p3'
437 elif pcu_id in [1173,1240,47,1363,1405,1401,1372,1371]:
438 newmodelname = 'APCControl121p1'
439 elif pcu_id in [1056,1237,1052,1209,1002,1008,1013,1022]:
440 newmodelname = 'BayTechCtrlC'
442 newmodelname = 'BayTechRPC3NC'
443 elif pcu_id in [1057]:
444 newmodelname = 'BayTechCtrlCUnibe'
445 elif pcu_id in [1012]:
446 newmodelname = 'BayTechRPC16'
447 elif pcu_id in [1089, 1071, 1046, 1035, 1118]:
448 newmodelname = 'ePowerSwitchNew'
452 def reboot_test_new(nodename, values, verbose, dryrun):
454 if 'plc_pcu_stats' in values:
455 values.update(values['plc_pcu_stats'])
458 #modelname = convert_oldmodelname_to_newmodelname(values['model'], values['pcu_id'])
459 modelname = values['model']
461 object = eval('%s(values, verbose)' % modelname)
462 rb_ret = object.reboot(values[nodename], dryrun)
465 # TODO: how to handle the weird, georgetown pcus, the drac faults, and ilo faults
466 except ExceptionPort, err:
468 except NameError, err:
474 print "this does not work."
476 if __name__ == '__main__':