*** empty log message ***
[monitor.git] / rt.py
1 #!/usr/bin/python2
2
3 import os, sys, shutil
4 import MySQLdb
5 import string
6 import logging
7 import Queue
8 import time 
9 import comon
10 from threading import *
11 import config
12
13 # RT database access constants file
14 RT_DB_CONSTANTS_PATH='/etc/planetlab/rt_db'
15
16 #Logging
17 logger = logging.getLogger("monitor")
18
19 # seconds between ticket update
20 RTSLEEP = 7200
21
22 def stripQuotes( str ):
23         quotes= ["'",'"']
24         if str[0] in quotes:
25                 str= str[1:]
26         if str[len(str)-1] in quotes:
27                 str= str[:len(str)-1]
28         return str
29
30
31 def readConstantsFile( file_path ):
32         """
33         read a file consisting of lines of
34         NAME=VALUE
35         NAME='VALUE'
36         NAME="VALUE"
37         and return a dictionary of the values.
38
39         blank lines, and lines starting with # (comments) are skipped
40         """
41
42         contents= {}
43
44         try:
45                 input_file= file(file_path,"r")
46         except IOError, err:
47                 return None
48
49         for line in input_file:
50                 if line[0] == "#":
51                         continue
52                 line= string.strip(line)
53                 if len(line) == 0:
54                         continue
55
56                 parts= string.split(line,"=",)
57                 if len(parts) <> 2:
58                         continue
59
60                 contents[parts[0]]= stripQuotes(parts[1])
61
62         return contents
63
64
65
66 def open_rt_db():
67
68         # read plc database passwords and connect
69         rt_db_constants= readConstantsFile(RT_DB_CONSTANTS_PATH)
70         if rt_db_constants is None:
71                 print "Unable to read database access constants from %s" % \
72                           RT_DB_CONSTANTS_PATH
73                 return -1
74
75         try:
76                 rt_db = MySQLdb.connect(host=rt_db_constants['RT_DB_HOST'],
77                                 user=rt_db_constants['RT_DB_USER'],
78                                 passwd=rt_db_constants['RT_DB_PASSWORD'],
79                                 db=rt_db_constants['RT_DB_NAME'])
80         except Error:
81                 print "Failed to connect to RT database"
82                 return -1
83
84         return rt_db
85
86
87
88
89 def rt_tickets(hostname):
90         db = open_rt_db()
91 #       sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject
92 #                        FROM Tickets AS Tk
93 #                        JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
94 #                        JOIN Attachments AS At ON Tr.id=At.TransactionID
95 #                        WHERE (At.Content LIKE '%%%s%%' OR
96 #                               At.Subject LIKE '%%%s%%') AND
97 #                               (Tk.Status = 'new' OR Tk.Status = 'open') AND
98 #                               Tk.Queue = 3 OR Tk.Queue = 19 
99 #                        ORDER BY Tk.Status, Tk.LastUpdated DESC""" \
100 #                        % (hostname,hostname)
101         sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject
102                          FROM Tickets AS Tk
103                          JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
104                          JOIN Attachments AS At ON Tr.id=At.TransactionID
105                          WHERE (At.Content LIKE '%%%s%%' OR
106                                 At.Subject LIKE '%%%s%%') AND
107                                 (Tk.Status = 'new' OR Tk.Status = 'open')
108                          ORDER BY Tk.Status, Tk.LastUpdated DESC""" \
109                          % (hostname,hostname)
110
111         try:
112                 # create a 'cursor' (required by MySQLdb)
113                 c = db.cursor()
114                 c.execute(sql)
115         except Exception, err:
116                 print "Could not execute RT query %s" %err
117                 return -1
118
119         # fetch all rows (list of lists)
120         raw = c.fetchall()
121
122         # map list of lists (raw) to list of dicts (tickets) 
123         # when int gets pulls from SQL into python ints are converted to LONG to
124         # prevent overflow .. convert back
125         tickets = map(lambda x: {"ticket_id":int(x[0]),
126                                 "status":x[1],
127                                 "subj":x[2]},
128                                 raw)
129         db.close()
130
131         return tickets
132
133
134 '''
135 Finds tickets associated with hostnames.
136 The idea is if you give it an array of host names,
137 presumeably from comon's list of bad nodes, it starts
138 a few threads to query RT.  RT takes a while to return.
139
140 This is turning into a reinvention of DB design, which I dont believe in.
141 In an effort to keep things minimal, here's the basic algo:
142
143 Give list of hostnames to RT()
144 Finds tickets associate with new hostnames (not in dict(tickets)).
145 Remove nodes that have come backup. Don't care of ticket is closed after first query.
146 Another thread refresh tickets of nodes already in dict and remove nodes that have come up. 
147 '''
148 class RT(Thread):
149         def __init__(self, tickets, toCheck, sickNoTicket, target = None): 
150                 # Time of last update of ticket DB
151                 self.lastupdated = 0
152                 # Queue() is MP/MC self locking.
153                 # Check host in queue.  Queue populated from comon data of sick. 
154                 self.toCheck = toCheck
155                 # Result of rt db query.  Nodes without tickets that are sick.
156                 self.sickNoTicket = sickNoTicket 
157                 #DB of tickets.  Name -> ticket
158                 self.tickets = tickets
159                 Thread.__init__(self,target = self.getTickets)
160
161         # Takes node from toCheck, gets tickets.  
162         # Thread that actually gets the tickets.
163         def getTickets(self):
164                 while 1:
165                         host = self.toCheck.get(block = True)
166                         if host == "None": break
167                         #if self.tickets.has_key(host) == False:
168                         #logger.debug("Popping from q - %s" %host)
169                         tmp = rt_tickets(host)
170                         if tmp:
171                                 #logger.debug("RT: tickets for %s" %host)
172                                 self.tickets[host] = tmp
173                         else:
174                                 logger.debug("RT: no tix for %s" %host)
175                                 self.sickNoTicket.put(host) 
176
177         # Removes hosts that are no longer down.
178         def remTickets(self):
179                 logger.debug("Removing stale entries from DB.") 
180                 prevdown = self.tickets.keys()
181
182                 currdown = []
183                 #BEGIN HACK.  This should be outside of this file. passed to class.
184                 cmn = comon.Comon(None, None)
185                 cmn.updatebkts()
186                 for bucket in cmn.comonbkts.keys():
187                         for host in getattr(cmn,bucket):
188                                 if host not in currdown: currdown.append(host)
189                 #END HACK
190
191                 # Actually do the comparison
192                 for host in prevdown:
193                         if host not in currdown:
194                                 del self.tickets[host]
195                                 logger.info("RT: %s no longer down." % host)
196
197         # Update Tickets
198         def updateTickets(self):
199                 logger.info("Refreshing DB.")
200                 for host in self.tickets.keys():
201                         # Put back in Q to refresh
202                         self.toCheck.put(host)
203
204         def cleanTickets(self):
205                 while 1:
206                         self.remTickets()
207                         self.updateTickets()
208                         time.sleep(RTSLEEP)
209         
210 def main():
211         logger.setLevel(logging.DEBUG)
212         ch = logging.StreamHandler()
213         ch.setLevel(logging.DEBUG)
214         formatter = logging.Formatter('%(message)s')
215         ch.setFormatter(formatter)
216         logger.addHandler(ch)
217
218         bucket = Queue.Queue() 
219         tickets = {}
220         a = RT(tickets, bucket)
221         b = RT(tickets, bucket)
222         c = RT(tickets, bucket)
223         d = RT(tickets, bucket)
224         e = RT(tickets, bucket)
225         a.start()
226         b.start()
227         c.start()
228         d.start()
229         tmp = ('planetlab-1.cs.ucy.ac.cy','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') 
230         for host in tmp:
231                 bucket.put(host)
232         #et = Thread(target=e.pushHosts)        
233         #et.start()
234         time.sleep(15)
235         print tickets.keys()
236         time.sleep(15)
237         print tickets.keys()
238         time.sleep(15)
239         print tickets.keys()
240         #cmn = comon.Comon(cdb, bucket)
241         #cmn.updatebkts()
242         #for bucket in cmn.comonbkts.keys():
243 #               for host in getattr(cmn,bucket):
244 #                       alldown.put(host)
245 #
246         at = Thread(target=a.cleanTickets)
247         at.start()
248         time.sleep(15)
249         print tickets.keys()
250         os._exit(0)
251
252 if __name__ == '__main__':
253     main()