This commit was generated by cvs2svn to compensate for changes in r2,
[monitor.git] / rt.py
diff --git a/rt.py b/rt.py
new file mode 100644 (file)
index 0000000..4e56869
--- /dev/null
+++ b/rt.py
@@ -0,0 +1,237 @@
+#!/usr/bin/python2
+
+import os, sys, shutil
+import MySQLdb
+import string
+import logging
+import Queue
+import time 
+import comon
+from threading import *
+
+# RT database access constants file
+RT_DB_CONSTANTS_PATH='/etc/planetlab/rt_db'
+
+#Logging
+logger = logging.getLogger("monitor")
+
+# seconds between ticket update
+RTSLEEP = 7200
+
+def stripQuotes( str ):
+       quotes= ["'",'"']
+       if str[0] in quotes:
+               str= str[1:]
+       if str[len(str)-1] in quotes:
+               str= str[:len(str)-1]
+       return str
+
+
+def readConstantsFile( file_path ):
+       """
+       read a file consisting of lines of
+       NAME=VALUE
+       NAME='VALUE'
+       NAME="VALUE"
+       and return a dictionary of the values.
+
+       blank lines, and lines starting with # (comments) are skipped
+       """
+
+       contents= {}
+
+       try:
+               input_file= file(file_path,"r")
+       except IOError, err:
+               return None
+
+       for line in input_file:
+               if line[0] == "#":
+                       continue
+               line= string.strip(line)
+               if len(line) == 0:
+                       continue
+
+               parts= string.split(line,"=",)
+               if len(parts) <> 2:
+                       continue
+
+               contents[parts[0]]= stripQuotes(parts[1])
+
+       return contents
+
+
+
+def open_rt_db():
+
+       # read plc database passwords and connect
+       rt_db_constants= readConstantsFile(RT_DB_CONSTANTS_PATH)
+       if rt_db_constants is None:
+               print "Unable to read database access constants from %s" % \
+                         RT_DB_CONSTANTS_PATH
+               return -1
+
+       try:
+               rt_db = MySQLdb.connect(host=rt_db_constants['RT_DB_HOST'],
+                               user=rt_db_constants['RT_DB_USER'],
+                               passwd=rt_db_constants['RT_DB_PASSWORD'],
+                               db=rt_db_constants['RT_DB_NAME'])
+       except Error:
+               print "Failed to connect to RT database"
+               return -1
+
+       return rt_db
+
+
+
+
+def rt_tickets(hostname):
+       db = open_rt_db()
+       sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject
+                        FROM Tickets AS Tk
+                        JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
+                        JOIN Attachments AS At ON Tr.id=At.TransactionID
+                        WHERE (At.Content LIKE '%%%s%%' OR
+                               At.Subject LIKE '%%%s%%') AND
+                               (Tk.Status = 'new' OR Tk.Status = 'open') AND
+                               Tk.Queue = 3
+                        ORDER BY Tk.Status, Tk.LastUpdated DESC""" \
+                        % (hostname,hostname)
+
+       try:
+               # create a 'cursor' (required by MySQLdb)
+               c = db.cursor()
+               c.execute(sql)
+       except Exception, err:
+               print "Could not execute RT query %s" %err
+               return -1
+
+       # fetch all rows (list of lists)
+       raw = c.fetchall()
+
+       # map list of lists (raw) to list of dicts (tickets) 
+       # when int gets pulls from SQL into python ints are converted to LONG to
+       # prevent overflow .. convert back
+       tickets = map(lambda x: {"ticket_id":int(x[0]),
+                               "status":x[1],
+                               "subj":x[2]},
+                               raw)
+       db.close()
+
+       return tickets
+
+
+'''
+Finds tickets associated with hostnames.
+The idea is if you give it an array of host names,
+presumeably from comon's list of bad nodes, it starts
+a few threads to query RT.  RT takes a while to return.
+
+This is turning into a reinvention of DB design, which I dont believe in.
+In an effort to keep things minimal, here's the basic algo:
+
+Give list of hostnames to RT()
+Finds tickets associate with new hostnames (not in dict(tickets)).
+Remove nodes that have come backup. Don't care of ticket is closed after first query.
+Another thread refresh tickets of nodes already in dict and remove nodes that have come up. 
+'''
+class RT(Thread):
+       def __init__(self, tickets, bucket, target = None): 
+               # Time of last update of ticket DB
+               self.lastupdated = 0
+               # Queue() is MP/MC self locking 
+               self.bucket = bucket 
+               #DB of tickets.  Name -> ticket
+               self.tickets = tickets
+               Thread.__init__(self,target = self.getTickets)
+
+       # Takes node from alldownq, gets tickets.  
+       # Thread that actually gets the tickets.
+       def getTickets(self):
+               while 1:
+                       host = self.bucket.get(block = True)
+                       if host == "None": break
+                       #if self.tickets.has_key(host) == False:
+                       logger.debug("Popping from q - %s" %host)
+                       tmp = rt_tickets(host)
+                       if tmp:
+                               logger.debug("Found tickets for %s" %host)
+                               self.tickets[host] = tmp 
+
+       # Removes hosts that are no longer down.
+       def remTickets(self):
+               logger.debug("Removing stale entries from DB.") 
+               prevdown = self.tickets.keys()
+
+               currdown = []
+               #BEGIN HACK.  This should be outside of this file. passed to class.
+               cmn = comon.Comon(None, None)
+               cmn.updatebkts()
+               for bucket in cmn.comonbkts.keys():
+                       for host in getattr(cmn,bucket):
+                               if host not in currdown: currdown.append(host)
+               #END HACK
+
+               # Actually do the comparison
+               for host in prevdown:
+                       if host not in currdown:
+                               del self.tickets[host]
+                               logger.info("%s no longer down" % host)
+
+       # Update Tickets
+       def updateTickets(self):
+               logger.info("Refreshing DB.")
+               for host in self.tickets.keys():
+                       # Put back in Q to refresh
+                       self.bucket.put(host)
+
+       def cleanTickets(self):
+               while 1:
+                       self.remTickets()
+                       self.updateTickets()
+                       time.sleep(RTSLEEP)
+       
+def main():
+       logger.setLevel(logging.DEBUG)
+       ch = logging.StreamHandler()
+       ch.setLevel(logging.DEBUG)
+       formatter = logging.Formatter('%(message)s')
+       ch.setFormatter(formatter)
+       logger.addHandler(ch)
+
+       bucket = Queue.Queue() 
+       tickets = {}
+       a = RT(tickets, bucket)
+       b = RT(tickets, bucket)
+       c = RT(tickets, bucket)
+       d = RT(tickets, bucket)
+       e = RT(tickets, bucket)
+       a.start()
+       b.start()
+       c.start()
+       d.start()
+       tmp = ('planetlab-2.vuse.vanderbilt.edu', 'planetlab-11.cs.princeton.edu', 'planet03.csc.ncsu.edu', 'planetlab1.pop-rj.rnp.br', 'planet1.halifax.canet4.nodes.planet-lab.org', 'planet1.cavite.nodes.planet-lab.org', 'ds-pl3.technion.ac.il', 'planetlab2.cs.purdue.edu', 'planetlab3.millennium.berkeley.edu', 'planetlab1.unl.edu', 'planetlab1.cs.colorado.edu', 'planetlab02.cs.washington.edu', 'orbpl2.rutgers.edu', 'planetlab2.informatik.uni-erlangen.de', 'pl2.ernet.in', 'neu2.6planetlab.edu.cn', 'planetlab-2.cs.uni-paderborn.de', 'planetlab1.elet.polimi.it', 'planetlab2.iiitb.ac.in', 'server1.planetlab.iit-tech.net', 'planetlab2.iitb.ac.in', 'planetlab1.ece.ucdavis.edu', 'planetlab02.dis.unina.it', 'planetlab-1.dis.uniroma1.it', 'planetlab1.iitb.ac.in', 'pku1.6planetlab.edu.cn', 'planetlab1.warsaw.rd.tp.pl', 'planetlab2.cs.unc.edu', 'csu2.6planetlab.edu.cn', 'pl1.ernet.in', 'planetlab2.georgetown.edu', 'planetlab1.cs.uchicago.edu') 
+       for host in tmp:
+               bucket.put(host)
+       #et = Thread(target=e.pushHosts)        
+       #et.start()
+       time.sleep(15)
+       print tickets.keys()
+       time.sleep(15)
+       print tickets.keys()
+       time.sleep(15)
+       print tickets.keys()
+       #cmn = comon.Comon(cdb, bucket)
+       #cmn.updatebkts()
+       #for bucket in cmn.comonbkts.keys():
+#              for host in getattr(cmn,bucket):
+#                      alldown.put(host)
+#
+       at = Thread(target=a.cleanTickets)
+       at.start()
+       time.sleep(15)
+       print tickets.keys()
+       os._exit(0)
+
+if __name__ == '__main__':
+    main()