12 from threading import *
14 # TODO: merge the RT mailer from mailer.py into this file.
16 # RT database access constants file
17 RT_DB_CONSTANTS_PATH='rt_db'
20 logger = logging.getLogger("monitor")
22 # seconds between ticket update
25 def stripQuotes( str ):
29 if str[len(str)-1] in quotes:
34 def readConstantsFile( file_path ):
36 read a file consisting of lines of
40 and return a dictionary of the values.
42 blank lines, and lines starting with # (comments) are skipped
48 input_file= file(file_path,"r")
52 for line in input_file:
55 line= string.strip(line)
59 parts= string.split(line,"=",)
63 contents[parts[0]]= stripQuotes(parts[1])
71 # read plc database passwords and connect
72 rt_db_constants= readConstantsFile(RT_DB_CONSTANTS_PATH)
73 if rt_db_constants is None:
74 print "Unable to read database access constants from %s" % \
79 rt_db = MySQLdb.connect(host=rt_db_constants['RT_DB_HOST'],
80 user=rt_db_constants['RT_DB_USER'],
81 passwd=rt_db_constants['RT_DB_PASSWORD'],
82 db=rt_db_constants['RT_DB_NAME'])
83 except Exception, err:
84 print "Failed to connect to RT database: %s" %err
91 def fetch_from_db(db, sql):
93 # create a 'cursor' (required by MySQLdb)
96 except Exception, err:
97 print "Could not execute RT query %s" %err
100 # fetch all rows (list of lists)
109 # sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject
111 # JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
112 # JOIN Attachments AS At ON Tr.id=At.TransactionID
113 # WHERE (At.Content LIKE '%%%s%%' OR
114 # At.Subject LIKE '%%%s%%') AND
115 # (Tk.Status = 'new' OR Tk.Status = 'open') AND
116 # Tk.Queue = 3 OR Tk.Queue = 19
117 # ORDER BY Tk.Status, Tk.LastUpdated DESC""" \
118 # % (hostname,hostname)
119 # sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject
121 # JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
122 # JOIN Attachments AS At ON Tr.id=At.TransactionID
123 # WHERE (At.Content LIKE '%%%s%%' OR
124 # At.Subject LIKE '%%%s%%') AND
125 # (Tk.Status = 'new' OR Tk.Status = 'open')
126 # ORDER BY Tk.Status, Tk.LastUpdated DESC""" \
127 # % (hostname,hostname)
129 # Queue == 10 is the spam Queue in RT.
130 sql = """SELECT distinct Tk.id, Tk.Status, Tk.Subject, At.Content
131 FROM Tickets AS Tk, Attachments AS At
132 JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
133 WHERE Tk.Queue != 10 AND Tk.id > 10000 AND
134 Tr.id=At.TransactionID AND Tk.Status = 'open'"""
135 #Tr.id=At.TransactionID AND (Tk.Status = 'new' OR Tk.Status = 'open')"""
136 #sqlall = """SELECT distinct Tk.id, Tk.Status, Tk.Subject, At.Content
137 #FROM Tickets AS Tk, Attachments AS At
138 #JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId
139 #WHERE Tk.Queue != 10 AND Tk.id > 10000 AND
140 #Tr.id=At.TransactionID AND ( Tk.Status = 'open' OR
141 #Tk.Status = 'new') """
142 sqlall = """SELECT distinct Tk.id, Tk.Status, Tk.Subject, At.Content, Us.EmailAddress FROM Tickets AS Tk, Attachments AS At, Users as Us JOIN Transactions AS Tr ON Tk.id=Tr.ObjectId WHERE (Tk.Queue=3 OR Tk.Queue=22) AND Tk.id > 10000 AND Tr.id=At.TransactionID AND ( Tk.Status = 'open' OR Tk.Status = 'new') AND Us.id=Tk.LastUpdatedBy """
145 raw = fetch_from_db(db, sql)
148 tickets = map(lambda x: {"ticket_id":str(x[0]),
151 "content":str(x[3])},
154 raw = fetch_from_db(db,sqlall)
157 tickets_all = map(lambda x: {"ticket_id":str(x[0]),
167 for t in tickets_all:
168 idTickets[t['ticket_id']] = t
169 soltesz.dbDump("idTickets", idTickets)
173 def is_host_in_rt_tickets(host, ticket_blacklist, ad_rt_tickets):
174 # ad_rt_tickets is an array of dicts, defined above.
175 if len(ad_rt_tickets) == 0:
178 d_ticket = ad_rt_tickets[0]
179 if not ('ticket_id' in d_ticket and 'status' in d_ticket and
180 'subj' in d_ticket and 'content' in d_ticket):
181 logger.debug("RT_tickets array has wrong fields!!!")
184 #logger.debug("Searching all tickets for %s" % host)
185 def search_tickets(host, ad_rt_tickets):
186 # compile once for more efficiency
187 re_host = re.compile(host)
188 for x in ad_rt_tickets:
189 if re_host.search(x['subj'], re.MULTILINE|re.IGNORECASE) or \
190 re_host.search(x['content'], re.MULTILINE|re.IGNORECASE):
191 logger.debug("\t ticket %s has %s" % (x['ticket_id'], host))
192 print "\t ticket %s has %s" % (x['ticket_id'], host)
193 if x['ticket_id'] in ticket_blacklist:
197 print "\t noticket -- has %s" % host
198 #logger.debug("\t noticket -- has %s" % host)
201 # This search, while O(tickets), takes less than a millisecond, 05-25-07
202 #t = soltesz.MyTimer()
203 ret = search_tickets(host, ad_rt_tickets)
210 Finds tickets associated with hostnames.
211 The idea is if you give it an array of host names,
212 presumeably from comon's list of bad nodes, it starts
213 a few threads to query RT. RT takes a while to return.
215 This is turning into a reinvention of DB design, which I dont believe in.
216 In an effort to keep things minimal, here's the basic algo:
218 Give list of hostnames to RT()
219 Finds tickets associate with new hostnames (not in dict(tickets)).
220 Remove nodes that have come backup. Don't care of ticket is closed after first query.
221 Another thread refresh tickets of nodes already in dict and remove nodes that have come up.
224 def __init__(self, dbTickets, q_toRT, q_fromRT, l_ticket_blacklist, target = None):
225 # Time of last update of ticket DB
226 self.dbTickets = dbTickets
228 self.l_ticket_blacklist = l_ticket_blacklist
230 self.q_fromRT = q_fromRT
232 Thread.__init__(self,target = self.getTickets)
234 # Takes node from q_toRT, gets tickets.
235 # Thread that actually gets the tickets.
236 def getTickets(self):
239 diag_node = self.q_toRT.get(block = True)
240 if diag_node != None:
241 host = diag_node['nodename']
242 (b_host_inticket, r_ticket) = is_host_in_rt_tickets(host, \
243 self.l_ticket_blacklist, \
245 diag_node['found_rt_ticket'] = None
247 logger.debug("RT: found tickets for %s" %host)
248 diag_node['found_rt_ticket'] = r_ticket['ticket_id']
251 if r_ticket is not None:
252 print "Ignoring ticket %s" % r_ticket['ticket_id']
253 # TODO: why do i return the ticket id for a
254 # blacklisted ticket id?
255 #diag_node['found_rt_ticket'] = r_ticket['ticket_id']
256 self.count = self.count + 1
258 self.q_fromRT.put(diag_node)
260 print "RT processed %d nodes with noticket" % self.count
261 logger.debug("RT filtered %d noticket nodes" % self.count)
262 self.q_fromRT.put(None)
266 # Removes hosts that are no longer down.
267 def remTickets(self):
268 logger.debug("Removing stale entries from DB.")
269 prevdown = self.tickets.keys()
272 ##BEGIN HACK. This should be outside of this file. passed to class.
273 #cmn = comon.Comon(None, None)
275 #for bucket in cmn.comonbkts.keys():
276 # for host in getattr(cmn,bucket):
277 # if host not in currdown: currdown.append(host)
280 # Actually do the comparison
281 #for host in prevdown:
282 # if host not in currdown:
283 # del self.tickets[host]
284 # logger.info("RT: %s no longer down." % host)
287 def updateTickets(self):
288 logger.info("Refreshing DB.")
289 for host in self.tickets.keys():
290 # Put back in Q to refresh
291 self.q_toRT.put(host)
293 def cleanTickets(self):
300 logger.setLevel(logging.DEBUG)
301 ch = logging.StreamHandler()
302 ch.setLevel(logging.DEBUG)
303 formatter = logging.Formatter('%(message)s')
304 ch.setFormatter(formatter)
305 logger.addHandler(ch)
307 tickets = rt_tickets()
308 soltesz.dbDump("ad_dbTickets", tickets)
311 if __name__ == '__main__':