*** empty log message ***
authorFaiyaz Ahmed <faiyaza@cs.princeton.edu>
Tue, 14 Nov 2006 19:27:09 +0000 (19:27 +0000)
committerFaiyaz Ahmed <faiyaza@cs.princeton.edu>
Tue, 14 Nov 2006 19:27:09 +0000 (19:27 +0000)
.mailer.py.swp [deleted file]
.monitor.py.swo [deleted file]
.monitor.py.swp [deleted file]
plc.py [new file with mode: 0644]
reboot.py [new file with mode: 0755]
telnetlib.py [new file with mode: 0644]

diff --git a/.mailer.py.swp b/.mailer.py.swp
deleted file mode 100644 (file)
index 287fd2a..0000000
Binary files a/.mailer.py.swp and /dev/null differ
diff --git a/.monitor.py.swo b/.monitor.py.swo
deleted file mode 100644 (file)
index dfe0598..0000000
Binary files a/.monitor.py.swo and /dev/null differ
diff --git a/.monitor.py.swp b/.monitor.py.swp
deleted file mode 100644 (file)
index f0aeff8..0000000
Binary files a/.monitor.py.swp and /dev/null differ
diff --git a/plc.py b/plc.py
new file mode 100644 (file)
index 0000000..42335aa
--- /dev/null
+++ b/plc.py
@@ -0,0 +1,225 @@
+#
+# plc.py
+#
+# Helper functions that minipulate the PLC api.
+# 
+# Faiyaz Ahmed <faiyaza@cs.princeton.edu
+#
+# $Id: $
+#
+
+from emailTxt import *
+import xml, xmlrpclib
+import logging
+import auth
+import time
+import config
+
+logger = logging.getLogger("monitor")
+
+XMLRPC_SERVER = 'https://www.planet-lab.org/PLCAPI/'
+
+'''
+Returns list of nodes in dbg as reported by PLC
+'''
+def nodesDbg():
+       dbgNodes = []
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       allnodes = api.AnonAdmGetNodes(anon, [], ['hostname','boot_state'])
+       for node in allnodes:
+               if node['boot_state'] == 'dbg': dbgNodes.append(node['hostname'])
+       logger.info("%s nodes in debug according to PLC." %len(dbgNodes))
+       return dbgNodes
+
+
+'''
+Returns loginbase for given nodename
+'''
+def siteId(nodename):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       site_id = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
+       if len(site_id) == 1:
+               loginbase = api.AnonAdmGetSites (anon, site_id, ["login_base"])
+               return loginbase[0]['login_base']
+
+'''
+Returns list of slices for a site.
+'''
+def slices(loginbase):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       return api.SliceListNames (auth.auth, loginbase)
+
+'''
+Returns dict of PCU info of a given node.
+'''
+def getpcu(nodename):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       nodes = []
+       site_id = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
+       if len(site_id) == 1:
+               # PCU uname, pw, etc
+               try:
+                       sitepcu = api.AdmGetSitePowerControlUnits(auth.auth, site_id[0])[0]
+                       # returns node_id and port
+                       sitepcuports = api.AdmGetPowerControlUnitNodes(auth.auth, sitepcu['pcu_id'])
+                       # Joining feilds
+                       for nodeidports in sitepcuports:
+                               nodeidports.update(api.AnonAdmGetNodes(anon, 
+                               [nodeidports['node_id']], ["node_id", "hostname"])[0])
+                               nodes.append(nodeidports)
+
+                       # WHY THE FUCK DOES EVERY XMl+RPC RETURN A FUCKING ARRAY?????
+                       # FURTHER, WHY THE FUCK WOULD YOU RETURN A NODE-ID WHEN SANITY WOULD SUGGEST
+                       # FQDN???? /RANT
+                       for node in nodes:
+                               sitepcu[node['hostname']] = node['port_number']
+
+                       # Sanity Check.  Make sure the node is in the return, if not, barf.
+                       if nodename in sitepcu.keys():
+                               return sitepcu
+                       else:
+                               raise Exception
+               except Exception, err:
+                       logger.debug("getpcu: %s" % err)
+                       return
+       else:
+               logger.info("Cant find site for %s" % nodename)
+
+
+'''
+Returns all site nodes for site id (loginbase).
+'''
+def getSiteNodes(loginbase):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       nodelist = []
+       anon = {'AuthMethod': "anonymous"}
+       try:
+               site_id = api.AnonAdmQuerySite(anon, {'site_loginbase': "%s" % loginbase})
+               node_ids = api.AnonAdmGetSiteNodes(anon, site_id)
+               for node in api.AnonAdmGetNodes(anon, node_ids["%s" % site_id[0]], ["hostname"]):
+                       nodelist.append(node['hostname'])
+       except Exception, exc:
+               logger.info("getSiteNodes:  %s" % exc)
+       return nodelist
+
+'''
+Sets boot state of a node.
+'''
+def nodeBootState(nodename, state):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       node_id = api.AnonAdmQueryNode(anon, {'node_hostname' : nodename})
+       if len(node_id) == 1:
+               logger.info("Setting node %s to %s" %(nodename, state))
+               try:
+                       if not config.debug:
+                               api.AdmUpdateNode(auth.auth, node_id[0], {'boot_state': state})
+               except Exception, exc:
+                       logger.info("nodeBootState:  %s" % exc)
+       else:
+               logger.info("Cant find node %s to toggle boot state" % nodename)
+
+'''
+Sends Ping Of Death to node.
+'''
+def nodePOD(nodename):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       node_id = api.AnonAdmQueryNode(anon, {'node_hostname' : nodename})
+       if len(node_id) == 1:
+               logger.info("Sending POD to %s" % nodename)
+               try:
+                       if not config.debug:
+                               api.AdmRebootNode(auth.auth, node_id[0])
+               except Exception, exc:
+                       logger.info("nodePOD:  %s" % exc)
+       else:
+               logger.info("Cant find node %s to send POD." % nodename)
+
+'''
+Freeze all site slices.
+'''
+def suspendSlices(nodename):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       for slice in slices(siteId(nodename)):
+               logger.info("Suspending slice %s" % slice)
+               try:
+                       if not config.debug:
+                               api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", 
+                               {"state" : "suspended"})
+               except Exception, exc:
+                       logger.info("suspendSlices:  %s" % exc)
+
+
+#I'm commenting this because this really should be a manual process.  
+#'''
+#Enable suspended site slices.
+#'''
+#def enableSlices(nodename, slicelist):
+#      api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+#      for slice in  slices(siteId(nodename)):
+#              logger.info("Suspending slice %s" % slice)
+#              api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
+#
+
+'''
+Removes ability to create slices. Returns previous max_slices
+'''
+def removeSliceCreation(nodename):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       siteid = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
+       numslices = api.AdmGetSites(auth.auth, siteid, ["max_slices"])[0]['max_slices']
+       if len(siteid) == 1:
+               logger.info("Removing slice creation for site %s" % siteId(nodename))
+               try:
+                       if not config.debug:
+                               api.AdmUpdateSite(auth.auth, siteid[0], {'max_slices': 0})
+                       return numslices
+               except Exception, exc:
+                       logger.info("removeSliceCreation:  %s" % exc)
+       else:
+               logger.debug("Cant find site for %s.  Cannot revoke creation." % nodename)
+
+'''
+QED
+'''
+def enableSliceCreation(nodename, maxslices):
+       api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
+       anon = {'AuthMethod': "anonymous"}
+       siteid = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
+       if len(siteid) == 1:
+               logger.info("Enabling slice creation for site %s" % siteId(nodename))
+               try:
+                       if not config.debug:
+                               api.AdmUpdateSite(auth.auth, siteid[0], {"max_slices" : maxslices})
+               except Exception, exc:
+                       logger.info("API:  %s" % exc)
+       else:
+               logger.debug("Cant find site for %s.  Cannot enable creation." % nodename)
+
+def main():
+       logger.setLevel(logging.DEBUG)
+       ch = logging.StreamHandler()
+       ch.setLevel(logging.DEBUG)
+       formatter = logging.Formatter('logger - %(message)s')
+       ch.setFormatter(formatter)
+       logger.addHandler(ch)
+       #print getpcu("kupl2.ittc.ku.edu")
+       #print getpcu("planetlab1.cse.msu.edu")
+       #print getpcu("alice.cs.princeton.edu")
+       #print nodesDbg()
+       #nodeBootState("alice.cs.princaeton.edu", "boot")
+       #freezeSite("alice.cs.princeton.edu")
+       #removeSliceCreation("alice.cs.princeton.edu")
+       #enableSliceCreation("alice.cs.princeton.edu", 1024)
+       print getSiteNodes("princeton")
+       #print siteId("alice.cs.princeton.edu")
+       #print nodePOD("planetlab5.warsaw.rd.tp.pl")
+
+if __name__=="__main__":
+       import reboot
+       main() 
diff --git a/reboot.py b/reboot.py
new file mode 100755 (executable)
index 0000000..297abe6
--- /dev/null
+++ b/reboot.py
@@ -0,0 +1,508 @@
+#!/usr/bin/python
+#
+# Reboot specified nodes
+#
+
+import getpass, getopt
+import os, sys
+import xml, xmlrpclib
+import errno, time, traceback
+import urllib2
+import threading, popen2
+import array, struct
+from socket import *
+import plc
+
+# Use our versions of telnetlib and pyssh
+sys.path.insert(0, os.path.dirname(sys.argv[0]))
+import telnetlib
+sys.path.insert(0, os.path.dirname(sys.argv[0]) + "/pyssh")    
+import pyssh
+
+# Timeouts in seconds
+TELNET_TIMEOUT = 20
+
+# Event class ID from pcu events
+#NODE_POWER_CONTROL = 3
+
+# Monitor user ID
+#MONITOR_USER_ID = 11142
+
+import logging
+logger = logging.getLogger("monitor")
+verbose = 1
+dryrun = 0;
+
+def telnet_answer(telnet, expected, buffer):
+       global verbose
+
+       output = telnet.read_until(expected, TELNET_TIMEOUT)
+       if verbose:
+               logger.debug(output)
+       if output.find(expected) == -1:
+               raise Exception, "'%s' not found" % expected
+       else:
+               telnet.write(buffer + "\r\n")
+
+
+def ipal_reboot(ip, password, port):
+       global dryrun, verbose
+
+       telnet = None
+
+       try:
+               telnet = telnetlib.Telnet(ip, timeout=TELNET_TIMEOUT)
+               telnet.set_debuglevel(verbose)
+
+               # XXX Some iPals require you to hit Enter a few times first
+               telnet_answer(telnet, "Password >", "\r\n\r\n")
+
+               # Login
+               telnet_answer(telnet, "Password >", password)
+
+               # P# - Pulse relay
+               if not dryrun:
+                       telnet_answer(telnet, "Enter >", "P%d" % port)
+
+               telnet.read_until("Enter >", TELNET_TIMEOUT)
+
+               # Close
+               telnet.close()
+               return 0
+
+       except EOFError, err:
+               if verbose:
+                       logger.debug(err)
+               telnet.close()
+               return errno.ECONNRESET
+       except Exception, err:
+               if verbose:
+                       logger.debug(err)
+               if telnet:
+                       telnet.close()
+               return errno.ETIMEDOUT
+
+
+def apc_reboot(ip, username, password, port):
+       global dryrun, verbose
+
+       telnet = None
+
+       try:
+               telnet = telnetlib.Telnet(ip, timeout=TELNET_TIMEOUT)
+               telnet.set_debuglevel(verbose)
+
+               # Login
+               telnet_answer(telnet, "User Name", username)
+               telnet_answer(telnet, "Password", password)
+
+               # 1- Device Manager
+               # 2- Network
+               # 3- System
+               # 4- Logout
+
+               # 1- Device Manager
+               telnet_answer(telnet, "\r\n> ", "1")
+
+               # 1- Phase Monitor/Configuration
+               # 2- Outlet Restriction Configuration
+               # 3- Outlet Control/Config
+               # 4- Power Supply Status
+
+               # 3- Outlet Control/Config
+               telnet_answer(telnet, "\r\n> ", "3")
+
+               # 1- Outlet 1
+               # 2- Outlet 2
+               # ...
+
+               # n- Outlet n
+               telnet_answer(telnet, "\r\n> ", str(port))
+               
+               # 1- Control Outlet
+               # 2- Configure Outlet
+
+               # 1- Control Outlet
+               telnet_answer(telnet, "\r\n> ", "1")
+
+               # 1- Immediate On                         
+               # 2- Immediate Off                       
+               # 3- Immediate Reboot             
+               # 4- Delayed On                         
+               # 5- Delayed Off                           
+               # 6- Delayed Reboot                     
+               # 7- Cancel                                     
+
+               # 3- Immediate Reboot             
+               telnet_answer(telnet, "\r\n> ", "3")
+
+               if not dryrun:
+                       telnet_answer(telnet, 
+                               "Enter 'YES' to continue or <ENTER> to cancel", "YES\r\n")
+                       telnet_answer(telnet, 
+                               "Press <ENTER> to continue...", "")
+
+               # Close
+               telnet.close()
+               return 0
+
+       except EOFError, err:
+               if verbose:
+                       logger.debug(err)
+               if telnet:
+                       telnet.close()
+               return errno.ECONNRESET
+       except Exception, err:
+               if verbose:
+                       logger.debug(err)
+               if telnet:
+                       telnet.close()
+               return errno.ETIMEDOUT
+
+
+def baytech_reboot(ip, username, password, port):
+       global dryrun, verbose
+
+       ssh = None
+
+       try:
+               ssh = pyssh.Ssh(username, ip)
+               ssh.open()
+
+               # Login
+               telnet_answer(ssh, "password:", password)
+
+               # PL1 comm output  (2 ,1).........1
+               # PL2 comm output  (2 ,2).........2
+               # PL3 comm output  (2 ,3).........3
+               # no machine       (2 ,4).........4
+               # Control Outlets  (5 ,1).........5
+               # Logout..........................T
+
+               # Control Outlets  (5 ,1).........5
+               telnet_answer(ssh, "Enter Request :", "5")
+
+               # Reboot N
+               telnet_answer(ssh, "DS-RPC>", "Reboot %d" % port)
+
+               # Reboot Outlet  N        (Y/N)?
+               if dryrun:
+                       telnet_answer(ssh, "(Y/N)?", "N")
+               else:
+                       telnet_answer(ssh, "(Y/N)?", "Y")
+               telnet_answer(ssh, "DS-RPC>", "")
+
+               # Close
+               output = ssh.close()
+               if verbose:
+                       logger.debug(err)
+               return 0
+
+       except Exception, err:
+               if verbose:
+                       logger.debug(err)
+               if ssh:
+                       output = ssh.close()
+                       if verbose:
+                               logger.debug(err)
+               return errno.ETIMEDOUT
+
+### rebooting european BlackBox PSE boxes
+# Thierry Parmentelat - May 11 2005
+# tested on 4-ports models known as PSE505-FR
+# uses http to POST a data 'P<port>=r'
+# relies on basic authentication within http1.0
+# first curl-based script was
+# curl --http1.0 --basic --user <username>:<password> --data P<port>=r \
+#      http://<hostname>:<http_port>/cmd.html && echo OK
+
+def bbpse_reboot (pcu_ip,username,password,port_in_pcu,http_port):
+
+       global dryrun, verbose
+
+       url = "http://%s:%d/cmd.html" % (pcu_ip,http_port)
+       data= "P%d=r" % port_in_pcu
+       if verbose:
+               logger.debug("POSTing '%s' on %s" % (data,url))
+
+       authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm()
+       uri = "%s:%d" % (pcu_ip,http_port)
+       authinfo.add_password (None, uri, username, password)
+       authhandler = urllib2.HTTPBasicAuthHandler( authinfo )
+
+       opener = urllib2.build_opener(authhandler)
+       urllib2.install_opener(opener)
+
+       if (dryrun):
+               return 0
+
+       try:
+               f = urllib2.urlopen(url,data)
+
+               r= f.read()
+               if verbose:
+                       logger.debug(r)
+               return 0
+
+       except urllib2.URLError,err:
+               logger.info('Could not open http connection', err)
+               return -1
+
+### rebooting x10toggle based systems addressed by port
+# Marc E. Fiuczynski - May 31 2005
+# tested on 4-ports models known as PSE505-FR
+# uses ssh and password to login to an account
+# that will cause the system to be powercycled.
+
+def x10toggle_reboot(ip, username, password, port):
+       global dryrun, verbose
+
+       ssh = None
+       try:
+               ssh = pyssh.Ssh(username, ip)
+               ssh.open()
+
+               # Login
+               telnet_answer(ssh, "password:", password)
+
+               if not dryrun:
+                       # Reboot
+                       telnet_answer(ssh, "x10toggle>", "A%d" % port)
+
+               # Close
+               output = ssh.close()
+               if verbose:
+                       logger.debug(output)
+               return 0
+
+       except Exception, err:
+               if verbose:
+                       logger.debug(err)
+               if ssh:
+                       output = ssh.close()
+                       if verbose:
+                               logger.debug(output)
+               return errno.ETIMEDOUT
+
+### rebooting Dell systems via RAC card
+# Marc E. Fiuczynski - June 01 2005
+# tested with David Lowenthal's itchy/scratchy nodes at UGA
+#
+
+def runcmd(command, args, username, password, timeout = None):
+
+       result = [None]
+       result_ready = threading.Condition()
+
+       def set_result(x):
+
+               result_ready.acquire()
+               try:
+                       result[0] = x
+               finally:
+                       result_ready.notify()
+                       result_ready.release()
+
+       def do_command(command, username, password):
+
+               try:
+                       # Popen4 is a popen-type class that combines stdout and stderr
+                       p = popen2.Popen4(command)
+
+                       # read all output data
+                       p.tochild.write("%s\n" % username)
+                       p.tochild.write("%s\n" % password)
+                       p.tochild.close()
+                       data = p.fromchild.read()
+
+                       while True:
+                               # might get interrupted by a signal in poll() or waitpid()
+                               try:
+                                       retval = p.wait()
+                                       set_result((retval, data))
+                                       break
+                               except OSError, ex:
+                                       if ex.errno == errno.EINTR:
+                                               continue
+                                       raise ex
+               except Exception, ex:
+                       set_result(ex)
+
+       if args:
+               command = " ".join([command] + args)
+
+       worker = threading.Thread(target = do_command, args = (command, username, password, ))
+       worker.setDaemon(True)
+       result_ready.acquire()
+       worker.start()
+       result_ready.wait(timeout)
+       try:
+               if result == [None]:
+                       raise Exception, "command timed-out: '%s'" % command
+       finally:
+               result_ready.release()
+       result = result[0]
+
+       if isinstance(result, Exception):
+               raise result
+       else:
+               (retval, data) = result
+               if os.WIFEXITED(retval) and os.WEXITSTATUS(retval) == 0:
+                       return data
+               else:
+                       out = "system command ('%s') " % command
+                       if os.WIFEXITED(retval):
+                               out += "failed, rc = %d" % os.WEXITSTATUS(retval)
+                       else:
+                               out += "killed by signal %d" % os.WTERMSIG(retval)
+                       if data:
+                               out += "; output follows:\n" + data
+                       raise Exception, out
+
+def racadm_reboot(ip, username, password, port):
+       global dryrun, verbose
+
+       try:
+               cmd = "/usr/sbin/racadm"
+               os.stat(cmd)
+               if not dryrun:
+                       output = runcmd(cmd, ["-r %s -i serveraction powercycle" % ip],
+                               username, password)
+               else:
+                       output = "dryrun of racadm command"
+
+               logger.debug("runcmd returned without output %s" % output)
+               if verbose:
+                       logger.debug(output)
+               return 0
+
+       except Exception, err:
+               logger.debug("runcmd raised exception %s" % err)
+               if verbose:
+                       logger.debug(err)
+               return errno.ETIMEDOUT
+
+# Returns true if rebooted via PCU
+def reboot(nodename):
+       pcu = plc.getpcu(nodename)
+       if not pcu:
+               plc.nodePOD(nodename)
+               return False
+       # Try the PCU first
+       logger.debug("Trying PCU %s %s" % (pcu['hostname'], pcu['model']))
+
+       # APC Masterswitch (Berkeley)
+       if pcu['protocol'] == "telnet" and pcu['model'] == "APC Masterswitch":
+               err = apc_reboot(pcu['ip'], pcu['username'],pcu['password'], pcu[nodename])
+
+       # DataProbe iPal (many sites)
+       elif pcu['protocol'] == "telnet" and pcu['model'].find("IP-4") >= 0:
+               err = ipal_reboot(pcu['ip'],pcu['password'], pcu[nodename])
+
+       # BayTech DS4-RPC
+       elif pcu['protocol'] == "ssh" and \
+       (pcu['model'].find("Baytech") >= 0 or pcu['model'].find("DS4") >= 0):
+               err = baytech_reboot(pcu['ip'], pcu['username'],pcu['password'], pcu[nodename])
+
+       # BlackBox PSExxx-xx (e.g. PSE505-FR)
+       elif pcu['protocol'] == "http" and (pcu['model'] == "bbpse"):
+               err = bbpse_reboot(pcu['ip'], pcu['username'], pcu['password'], pcu[nodename],80)
+
+       # x10toggle
+       elif pcu['protocol'] == "ssh" and (pcu['model'] == "x10toggle"):
+               err = x10toggle_reboot(pcu['ip'], pcu['username'],pcu['password'], pcu[nodename])
+
+       # x10toggle
+       elif pcu['protocol'] == "racadm" and (pcu['model'] == "RAC"):
+               err = racadm_reboot(pcu['ip'], pcu['username'],pcu['password'], pcu_[nodename])
+
+       # Unknown or unsupported
+       else:
+               err = errno.EPROTONOSUPPORT
+               return False
+       return True 
+
+#def get_suggested(suggestion_id,db):
+#
+#      sql= """select node_id,pcu_id from nodes where suggestion = %d """\
+#                      % (suggestion_id)
+#      try:
+#              nodes = db.query(sql).dictresult()
+#      except pg.ProgrammingError, err:
+#              print( "Database error for query: %s\n%s" % (sql,err) )
+#              sys.exit(1)
+#      return nodes
+
+#def get_pcu_info(node_id,pcu_id,db):
+#      sql= """select port_number from pcu_ports where node_id = %d and pcu_id = %d """\
+#                      % (node_id,pcu_id)
+#      try:
+#         port_number = db.query(sql).dictresult()
+#      except pg.ProgrammingError, err:
+#              print( "Database error for query: %s\n%s" % (sql,err) )
+#              sys.exit(1)
+#      
+#      sql= """select * from pcu where pcu_id = %d """\
+#                      % (pcu_id)
+#      try:
+#              pcu = db.query(sql).dictresult()
+#      except pg.ProgrammingError, err:
+#              print( "Database error for query: %s\n%s" % (sql,err) )
+#              sys.exit(1)
+#
+#      result = {'node_id':node_id,'pcu_id':pcu_id,'port_number':port_number[0]['port_number'], 
+#                        'ip':pcu[0]['ip'],'username':pcu[0]['username'],'password':pcu[0]['password'],\
+#                        'model':pcu[0]['model'],'protocol':pcu[0]['protocol'],'hostname':pcu[0]['hostname']}
+#
+#      return result
+
+#def add_plc_event(node_id,err,db):
+#      site_id = plc_db_utils.get_site_from_node_id(node_id,db)
+#      message = "PCU reboot by monitor-msgs@planet-lab.org: %s" % os.strerror(err)
+#
+#      sql = """insert into events (event_class_id,message,person_id,node_id,site_id) values """\
+#                """(%d,'%s',%d,%d,%d)""" % (NODE_POWER_CONTROL,message,MONITOR_USER_ID,node_id,site_id)
+#      print sql
+#
+#      try:
+#              db.query(sql)
+#      except pg.ProgrammingError, err:
+#              print( "Database error for: %s\n%s" % (sql,err) )
+#              sys.exit(1)
+
+
+def main():
+       logger.setLevel(logging.DEBUG)
+       ch = logging.StreamHandler()
+       ch.setLevel(logging.DEBUG)
+       formatter = logging.Formatter('LOGGER - %(message)s')
+       ch.setFormatter(formatter)
+       logger.addHandler(ch)
+
+
+       try:
+               reboot("planetlab2.cs.uchicago.edu")
+               reboot("alice.cs.princeton.edu")
+       except Exception, err:
+               print err
+       # used later for pretty printing
+#      pp = pprint.PrettyPrinter(indent=2)
+
+#      user = "Monitor"
+#      password = None
+
+#      plc_db = plc_dbs.open_plc_db_write()
+#      mon_db = plc_dbs.open_mon_db()
+
+       # 5 = needs script reboot - fix this later
+#      nodes = get_suggested(5,mon_db)
+
+#      for row in nodes:
+               
+#              pcu = get_pcu_info(row['node_id'],row['pcu_id'],plc_db)
+#              add_plc_event(row['node_id'],err,plc_db)
+
+if __name__ == '__main__':
+       import plc
+       logger = logging.getLogger("monitor")
+       main()
diff --git a/telnetlib.py b/telnetlib.py
new file mode 100644 (file)
index 0000000..cfa96c9
--- /dev/null
@@ -0,0 +1,659 @@
+"""TELNET client class.
+
+Based on RFC 854: TELNET Protocol Specification, by J. Postel and
+J. Reynolds
+
+Example:
+
+>>> from telnetlib import Telnet
+>>> tn = Telnet('www.python.org', 79)   # connect to finger port
+>>> tn.write('guido\r\n')
+>>> print tn.read_all()
+Login       Name               TTY         Idle    When    Where
+guido    Guido van Rossum      pts/2        <Dec  2 11:10> snag.cnri.reston..
+
+>>>
+
+Note that read_all() won't read until eof -- it just reads some data
+-- but it guarantees to read at least one byte unless EOF is hit.
+
+It is possible to pass a Telnet object to select.select() in order to
+wait until more data is available.  Note that in this case,
+read_eager() may return '' even if there was data on the socket,
+because the protocol negotiation may have eaten the data.  This is why
+EOFError is needed in some cases to distinguish between "no data" and
+"connection closed" (since the socket also appears ready for reading
+when it is closed).
+
+To do:
+- option negotiation
+- timeout should be intrinsic to the connection object instead of an
+  option on one of the read calls only
+
+"""
+
+
+# Imported modules
+import sys
+import socket
+import select
+
+__all__ = ["Telnet"]
+
+# Tunable parameters
+DEBUGLEVEL = 0
+
+# Telnet protocol defaults
+TELNET_PORT = 23
+
+# Telnet protocol characters (don't change)
+IAC  = chr(255) # "Interpret As Command"
+DONT = chr(254)
+DO   = chr(253)
+WONT = chr(252)
+WILL = chr(251)
+theNULL = chr(0)
+
+SE  = chr(240)  # Subnegotiation End
+NOP = chr(241)  # No Operation
+DM  = chr(242)  # Data Mark
+BRK = chr(243)  # Break
+IP  = chr(244)  # Interrupt process
+AO  = chr(245)  # Abort output
+AYT = chr(246)  # Are You There
+EC  = chr(247)  # Erase Character
+EL  = chr(248)  # Erase Line
+GA  = chr(249)  # Go Ahead
+SB =  chr(250)  # Subnegotiation Begin
+
+
+# Telnet protocol options code (don't change)
+# These ones all come from arpa/telnet.h
+BINARY = chr(0) # 8-bit data path
+ECHO = chr(1) # echo
+RCP = chr(2) # prepare to reconnect
+SGA = chr(3) # suppress go ahead
+NAMS = chr(4) # approximate message size
+STATUS = chr(5) # give status
+TM = chr(6) # timing mark
+RCTE = chr(7) # remote controlled transmission and echo
+NAOL = chr(8) # negotiate about output line width
+NAOP = chr(9) # negotiate about output page size
+NAOCRD = chr(10) # negotiate about CR disposition
+NAOHTS = chr(11) # negotiate about horizontal tabstops
+NAOHTD = chr(12) # negotiate about horizontal tab disposition
+NAOFFD = chr(13) # negotiate about formfeed disposition
+NAOVTS = chr(14) # negotiate about vertical tab stops
+NAOVTD = chr(15) # negotiate about vertical tab disposition
+NAOLFD = chr(16) # negotiate about output LF disposition
+XASCII = chr(17) # extended ascii character set
+LOGOUT = chr(18) # force logout
+BM = chr(19) # byte macro
+DET = chr(20) # data entry terminal
+SUPDUP = chr(21) # supdup protocol
+SUPDUPOUTPUT = chr(22) # supdup output
+SNDLOC = chr(23) # send location
+TTYPE = chr(24) # terminal type
+EOR = chr(25) # end or record
+TUID = chr(26) # TACACS user identification
+OUTMRK = chr(27) # output marking
+TTYLOC = chr(28) # terminal location number
+VT3270REGIME = chr(29) # 3270 regime
+X3PAD = chr(30) # X.3 PAD
+NAWS = chr(31) # window size
+TSPEED = chr(32) # terminal speed
+LFLOW = chr(33) # remote flow control
+LINEMODE = chr(34) # Linemode option
+XDISPLOC = chr(35) # X Display Location
+OLD_ENVIRON = chr(36) # Old - Environment variables
+AUTHENTICATION = chr(37) # Authenticate
+ENCRYPT = chr(38) # Encryption option
+NEW_ENVIRON = chr(39) # New - Environment variables
+# the following ones come from
+# http://www.iana.org/assignments/telnet-options
+# Unfortunately, that document does not assign identifiers
+# to all of them, so we are making them up
+TN3270E = chr(40) # TN3270E
+XAUTH = chr(41) # XAUTH
+CHARSET = chr(42) # CHARSET
+RSP = chr(43) # Telnet Remote Serial Port
+COM_PORT_OPTION = chr(44) # Com Port Control Option
+SUPPRESS_LOCAL_ECHO = chr(45) # Telnet Suppress Local Echo
+TLS = chr(46) # Telnet Start TLS
+KERMIT = chr(47) # KERMIT
+SEND_URL = chr(48) # SEND-URL
+FORWARD_X = chr(49) # FORWARD_X
+PRAGMA_LOGON = chr(138) # TELOPT PRAGMA LOGON
+SSPI_LOGON = chr(139) # TELOPT SSPI LOGON
+PRAGMA_HEARTBEAT = chr(140) # TELOPT PRAGMA HEARTBEAT
+EXOPL = chr(255) # Extended-Options-List
+NOOPT = chr(0)
+
+class Telnet:
+
+    """Telnet interface class.
+
+    An instance of this class represents a connection to a telnet
+    server.  The instance is initially not connected; the open()
+    method must be used to establish a connection.  Alternatively, the
+    host name and optional port number can be passed to the
+    constructor, too.
+
+    Don't try to reopen an already connected instance.
+
+    This class has many read_*() methods.  Note that some of them
+    raise EOFError when the end of the connection is read, because
+    they can return an empty string for other reasons.  See the
+    individual doc strings.
+
+    read_until(expected, [timeout])
+        Read until the expected string has been seen, or a timeout is
+        hit (default is no timeout); may block.
+
+    read_all()
+        Read all data until EOF; may block.
+
+    read_some()
+        Read at least one byte or EOF; may block.
+
+    read_very_eager()
+        Read all data available already queued or on the socket,
+        without blocking.
+
+    read_eager()
+        Read either data already queued or some data available on the
+        socket, without blocking.
+
+    read_lazy()
+        Read all data in the raw queue (processing it first), without
+        doing any socket I/O.
+
+    read_very_lazy()
+        Reads all data in the cooked queue, without doing any socket
+        I/O.
+
+    read_sb_data()
+        Reads available data between SB ... SE sequence. Don't block.
+
+    set_option_negotiation_callback(callback)
+        Each time a telnet option is read on the input flow, this callback
+        (if set) is called with the following parameters :
+        callback(telnet socket, command, option)
+            option will be chr(0) when there is no option.
+        No other action is done afterwards by telnetlib.
+
+    """
+
+    def __init__(self, host=None, port=0, timeout=None):
+        """Constructor.
+
+        When called without arguments, create an unconnected instance.
+        With a hostname argument, it connects the instance; a port
+        number and connect timeout is optional.
+
+        """
+        self.debuglevel = DEBUGLEVEL
+        self.host = host
+        self.port = port
+        self.sock = None
+        self.rawq = ''
+        self.irawq = 0
+        self.cookedq = ''
+        self.eof = 0
+        self.iacseq = '' # Buffer for IAC sequence.
+        self.sb = 0 # flag for SB and SE sequence.
+        self.sbdataq = ''
+        self.option_callback = None
+        if host is not None:
+            self.open(host, port, timeout)
+
+    def open(self, host, port=0, timeout=None):
+        """Connect to a host.
+
+        The optional second argument is the port number, which
+        defaults to the standard telnet port (23). The optional third
+        argument is a timeout in seconds; if None, the default timeout
+        of the underlying socket will be used.
+
+        Don't try to reopen an already connected instance.
+
+        """
+        self.eof = 0
+        if not port:
+            port = TELNET_PORT
+        self.host = host
+        self.port = port
+        msg = "getaddrinfo returns an empty list"
+        for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+            af, socktype, proto, canonname, sa = res
+            try:
+                self.sock = socket.socket(af, socktype, proto)
+                if timeout is not None:
+                    self.sock.settimeout(timeout)
+                self.sock.connect(sa)
+            except socket.error, msg:
+                if self.sock:
+                    self.sock.close()
+                self.sock = None
+                continue
+            break
+        if not self.sock:
+            raise socket.error, msg
+
+    def __del__(self):
+        """Destructor -- close the connection."""
+        self.close()
+
+    def msg(self, msg, *args):
+        """Print a debug message, when the debug level is > 0.
+
+        If extra arguments are present, they are substituted in the
+        message using the standard string formatting operator.
+
+        """
+        if self.debuglevel > 0:
+            print 'Telnet(%s,%d):' % (self.host, self.port),
+            if args:
+                print msg % args
+            else:
+                print msg
+
+    def set_debuglevel(self, debuglevel):
+        """Set the debug level.
+
+        The higher it is, the more debug output you get (on sys.stdout).
+
+        """
+        self.debuglevel = debuglevel
+
+    def close(self):
+        """Close the connection."""
+        if self.sock:
+            self.sock.close()
+        self.sock = 0
+        self.eof = 1
+        self.iacseq = ''
+        self.sb = 0
+
+    def get_socket(self):
+        """Return the socket object used internally."""
+        return self.sock
+
+    def fileno(self):
+        """Return the fileno() of the socket object used internally."""
+        return self.sock.fileno()
+
+    def write(self, buffer):
+        """Write a string to the socket, doubling any IAC characters.
+
+        Can block if the connection is blocked.  May raise
+        socket.error if the connection is closed.
+
+        """
+        if IAC in buffer:
+            buffer = buffer.replace(IAC, IAC+IAC)
+        self.msg("send %s", `buffer`)
+        self.sock.sendall(buffer)
+
+    def read_until(self, match, timeout=None):
+        """Read until a given string is encountered or until timeout.
+
+        When no match is found, return whatever is available instead,
+        possibly the empty string.  Raise EOFError if the connection
+        is closed and no cooked data is available.
+
+        """
+        n = len(match)
+        self.process_rawq()
+        i = self.cookedq.find(match)
+        if i >= 0:
+            i = i+n
+            buf = self.cookedq[:i]
+            self.cookedq = self.cookedq[i:]
+            return buf
+        s_reply = ([self], [], [])
+        s_args = s_reply
+        if timeout is not None:
+            s_args = s_args + (timeout,)
+        while not self.eof and select.select(*s_args) == s_reply:
+            i = max(0, len(self.cookedq)-n)
+            self.fill_rawq()
+            self.process_rawq()
+            i = self.cookedq.find(match, i)
+            if i >= 0:
+                i = i+n
+                buf = self.cookedq[:i]
+                self.cookedq = self.cookedq[i:]
+                return buf
+        return self.read_very_lazy()
+
+    def read_all(self):
+        """Read all data until EOF; block until connection closed."""
+        self.process_rawq()
+        while not self.eof:
+            self.fill_rawq()
+            self.process_rawq()
+        buf = self.cookedq
+        self.cookedq = ''
+        return buf
+
+    def read_some(self):
+        """Read at least one byte of cooked data unless EOF is hit.
+
+        Return '' if EOF is hit.  Block if no data is immediately
+        available.
+
+        """
+        self.process_rawq()
+        while not self.cookedq and not self.eof:
+            self.fill_rawq()
+            self.process_rawq()
+        buf = self.cookedq
+        self.cookedq = ''
+        return buf
+
+    def read_very_eager(self):
+        """Read everything that's possible without blocking in I/O (eager).
+
+        Raise EOFError if connection closed and no cooked data
+        available.  Return '' if no cooked data available otherwise.
+        Don't block unless in the midst of an IAC sequence.
+
+        """
+        self.process_rawq()
+        while not self.eof and self.sock_avail():
+            self.fill_rawq()
+            self.process_rawq()
+        return self.read_very_lazy()
+
+    def read_eager(self):
+        """Read readily available data.
+
+        Raise EOFError if connection closed and no cooked data
+        available.  Return '' if no cooked data available otherwise.
+        Don't block unless in the midst of an IAC sequence.
+
+        """
+        self.process_rawq()
+        while not self.cookedq and not self.eof and self.sock_avail():
+            self.fill_rawq()
+            self.process_rawq()
+        return self.read_very_lazy()
+
+    def read_lazy(self):
+        """Process and return data that's already in the queues (lazy).
+
+        Raise EOFError if connection closed and no data available.
+        Return '' if no cooked data available otherwise.  Don't block
+        unless in the midst of an IAC sequence.
+
+        """
+        self.process_rawq()
+        return self.read_very_lazy()
+
+    def read_very_lazy(self):
+        """Return any data available in the cooked queue (very lazy).
+
+        Raise EOFError if connection closed and no data available.
+        Return '' if no cooked data available otherwise.  Don't block.
+
+        """
+        buf = self.cookedq
+        self.cookedq = ''
+        if not buf and self.eof and not self.rawq:
+            raise EOFError, 'telnet connection closed'
+        return buf
+
+    def read_sb_data(self):
+        """Return any data available in the SB ... SE queue.
+
+        Return '' if no SB ... SE available. Should only be called
+        after seeing a SB or SE command. When a new SB command is
+        found, old unread SB data will be discarded. Don't block.
+
+        """
+        buf = self.sbdataq
+        self.sbdataq = ''
+        return buf
+
+    def set_option_negotiation_callback(self, callback):
+        """Provide a callback function called after each receipt of a telnet option."""
+        self.option_callback = callback
+
+    def process_rawq(self):
+        """Transfer from raw queue to cooked queue.
+
+        Set self.eof when connection is closed.  Don't block unless in
+        the midst of an IAC sequence.
+
+        """
+        buf = ['', '']
+        try:
+            while self.rawq:
+                c = self.rawq_getchar()
+                if not self.iacseq:
+                    if c == theNULL:
+                        continue
+                    if c == "\021":
+                        continue
+                    if c != IAC:
+                        buf[self.sb] = buf[self.sb] + c
+                        continue
+                    else:
+                        self.iacseq += c
+                elif len(self.iacseq) == 1:
+                    'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]'
+                    if c in (DO, DONT, WILL, WONT):
+                        self.iacseq += c
+                        continue
+
+                    self.iacseq = ''
+                    if c == IAC:
+                        buf[self.sb] = buf[self.sb] + c
+                    else:
+                        if c == SB: # SB ... SE start.
+                            self.sb = 1
+                            self.sbdataq = ''
+                        elif c == SE:
+                            self.sb = 0
+                            self.sbdataq = self.sbdataq + buf[1]
+                            buf[1] = ''
+                        if self.option_callback:
+                            # Callback is supposed to look into
+                            # the sbdataq
+                            self.option_callback(self.sock, c, NOOPT)
+                        else:
+                            # We can't offer automatic processing of
+                            # suboptions. Alas, we should not get any
+                            # unless we did a WILL/DO before.
+                            self.msg('IAC %d not recognized' % ord(c))
+                elif len(self.iacseq) == 2:
+                    cmd = self.iacseq[1]
+                    self.iacseq = ''
+                    opt = c
+                    if cmd in (DO, DONT):
+                        self.msg('IAC %s %d',
+                            cmd == DO and 'DO' or 'DONT', ord(opt))
+                        if self.option_callback:
+                            self.option_callback(self.sock, cmd, opt)
+                        else:
+                            self.sock.sendall(IAC + WONT + opt)
+                    elif cmd in (WILL, WONT):
+                        self.msg('IAC %s %d',
+                            cmd == WILL and 'WILL' or 'WONT', ord(opt))
+                        if self.option_callback:
+                            self.option_callback(self.sock, cmd, opt)
+                        else:
+                            self.sock.sendall(IAC + DONT + opt)
+        except EOFError: # raised by self.rawq_getchar()
+            self.iacseq = '' # Reset on EOF
+            self.sb = 0
+            pass
+        self.cookedq = self.cookedq + buf[0]
+        self.sbdataq = self.sbdataq + buf[1]
+
+    def rawq_getchar(self):
+        """Get next char from raw queue.
+
+        Block if no data is immediately available.  Raise EOFError
+        when connection is closed.
+
+        """
+        if not self.rawq:
+            self.fill_rawq()
+            if self.eof:
+                raise EOFError
+        c = self.rawq[self.irawq]
+        self.irawq = self.irawq + 1
+        if self.irawq >= len(self.rawq):
+            self.rawq = ''
+            self.irawq = 0
+        return c
+
+    def fill_rawq(self):
+        """Fill raw queue from exactly one recv() system call.
+
+        Block if no data is immediately available.  Set self.eof when
+        connection is closed.
+
+        """
+        if self.irawq >= len(self.rawq):
+            self.rawq = ''
+            self.irawq = 0
+        # The buffer size should be fairly small so as to avoid quadratic
+        # behavior in process_rawq() above
+        buf = self.sock.recv(50)
+        self.msg("recv %s", `buf`)
+        self.eof = (not buf)
+        self.rawq = self.rawq + buf
+
+    def sock_avail(self):
+        """Test whether data is available on the socket."""
+        return select.select([self], [], [], 0) == ([self], [], [])
+
+    def interact(self):
+        """Interaction function, emulates a very dumb telnet client."""
+        if sys.platform == "win32":
+            self.mt_interact()
+            return
+        while 1:
+            rfd, wfd, xfd = select.select([self, sys.stdin], [], [])
+            if self in rfd:
+                try:
+                    text = self.read_eager()
+                except EOFError:
+                    print '*** Connection closed by remote host ***'
+                    break
+                if text:
+                    sys.stdout.write(text)
+                    sys.stdout.flush()
+            if sys.stdin in rfd:
+                line = sys.stdin.readline()
+                if not line:
+                    break
+                self.write(line)
+
+    def mt_interact(self):
+        """Multithreaded version of interact()."""
+        import thread
+        thread.start_new_thread(self.listener, ())
+        while 1:
+            line = sys.stdin.readline()
+            if not line:
+                break
+            self.write(line)
+
+    def listener(self):
+        """Helper for mt_interact() -- this executes in the other thread."""
+        while 1:
+            try:
+                data = self.read_eager()
+            except EOFError:
+                print '*** Connection closed by remote host ***'
+                return
+            if data:
+                sys.stdout.write(data)
+            else:
+                sys.stdout.flush()
+
+    def expect(self, list, timeout=None):
+        """Read until one from a list of a regular expressions matches.
+
+        The first argument is a list of regular expressions, either
+        compiled (re.RegexObject instances) or uncompiled (strings).
+        The optional second argument is a timeout, in seconds; default
+        is no timeout.
+
+        Return a tuple of three items: the index in the list of the
+        first regular expression that matches; the match object
+        returned; and the text read up till and including the match.
+
+        If EOF is read and no text was read, raise EOFError.
+        Otherwise, when nothing matches, return (-1, None, text) where
+        text is the text received so far (may be the empty string if a
+        timeout happened).
+
+        If a regular expression ends with a greedy match (e.g. '.*')
+        or if more than one expression can match the same input, the
+        results are undeterministic, and may depend on the I/O timing.
+
+        """
+        re = None
+        list = list[:]
+        indices = range(len(list))
+        for i in indices:
+            if not hasattr(list[i], "search"):
+                if not re: import re
+                list[i] = re.compile(list[i])
+        while 1:
+            self.process_rawq()
+            for i in indices:
+                m = list[i].search(self.cookedq)
+                if m:
+                    e = m.end()
+                    text = self.cookedq[:e]
+                    self.cookedq = self.cookedq[e:]
+                    return (i, m, text)
+            if self.eof:
+                break
+            if timeout is not None:
+                r, w, x = select.select([self.fileno()], [], [], timeout)
+                if not r:
+                    break
+            self.fill_rawq()
+        text = self.read_very_lazy()
+        if not text and self.eof:
+            raise EOFError
+        return (-1, None, text)
+
+
+def test():
+    """Test program for telnetlib.
+
+    Usage: python telnetlib.py [-d] ... [host [port]]
+
+    Default host is localhost; default port is 23.
+
+    """
+    debuglevel = 0
+    while sys.argv[1:] and sys.argv[1] == '-d':
+        debuglevel = debuglevel+1
+        del sys.argv[1]
+    host = 'localhost'
+    if sys.argv[1:]:
+        host = sys.argv[1]
+    port = 0
+    if sys.argv[2:]:
+        portstr = sys.argv[2]
+        try:
+            port = int(portstr)
+        except ValueError:
+            port = socket.getservbyname(portstr, 'tcp')
+    tn = Telnet()
+    tn.set_debuglevel(debuglevel)
+    tn.open(host, port)
+    tn.interact()
+    tn.close()
+
+if __name__ == '__main__':
+    test()