ugg. output hangs on large file contents. truncate data. need a better
[monitor.git] / monitor / wrapper / mailer.py
1 #!/usr/bin/python2
2 #
3 # Copyright (c) 2004  The Trustees of Princeton University (Trustees).
4 #
5 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
6 # Stephen Soltesz <soltesz@cs.princeton.edu>
7 #
8 # $Id: mailer.py,v 1.10 2007/08/08 13:28:06 soltesz Exp $
9 from emailTxt import *
10 import smtplib
11 from monitor import config
12 import calendar
13 import logging
14 import os
15 import time
16
17 logger = logging.getLogger("monitor")
18
19 MTA="localhost"
20 FROM=config.email
21
22 def reformat_for_rt(text):
23         lines = text.split("\n")
24         spaced_text = ""
25         for line in lines:
26                 spaced_text += " %s\n" %line
27         return spaced_text
28                 
29
30 def _setupRTenvironment():
31         os.environ['PATH'] = config.RT_WEB_TOOLS_PATH + ":" + os.environ['PATH']
32         os.environ['RTSERVER'] = config.RT_WEB_SERVER
33         os.environ['RTUSER']   = config.RT_WEB_USER
34         os.environ['RTPASSWD'] = config.RT_WEB_PASSWORD
35         os.environ['RTDEBUG'] = config.RT_WEB_DEBUG
36         return
37
38 def setTicketStatus(ticket_id, status):
39         _setupRTenvironment()
40         if ticket_id == None or ticket_id == "":
41                 return {}
42
43         cmd = "rt edit ticket/%s set status=%s" % (ticket_id, status)
44         print cmd
45         (f_in, f_out, f_err) = os.popen3(cmd)
46         value = f_out.read()
47         l_values = value.split('\n')
48         return "".join(l_values).strip()
49
50 def getTicketStatus(ticket_id):
51         _setupRTenvironment()
52         if ticket_id == None or ticket_id == "":
53                 return {}
54
55         cmd = "rt show -t ticket -f id,subject,status,queue,created %s" % (ticket_id)
56         print cmd
57         (f_in, f_out, f_err) = os.popen3(cmd)
58         value = f_out.read()
59         l_values = value.split('\n')
60         r_values = {}
61         for line in l_values:
62                 if len(line) == 0: continue
63                 vals = line.split(':')
64                 key = vals[0]
65                 r_values[key] = ":".join(vals[1:])
66                 r_values[key] = r_values[key].strip()
67
68         r_values['Created'] = calendar.timegm(time.strptime(r_values['Created']))
69         return r_values
70
71 def setAdminCCViaRT(ticket_id, to):
72         # Set ENV Variables/PATH
73         _setupRTenvironment()
74         if ticket_id == None or ticket_id == "":
75                 raise Exception("ERROR: ticket_id must be set to some integer value")
76
77         # This will raise an exception if it is not a valid id.
78         i_ticket_id = int(ticket_id)
79
80         # create a comma-separated list
81         s_to = ",".join(to)
82         cmd = "rt edit ticket/%s set admincc='%s'" % (ticket_id, s_to)
83         print cmd
84         (f_in, f_out, f_err) = os.popen3(cmd)
85         value = f_out.read()
86         l_values = value.split()
87         f_in.close() ; f_out.close() ; f_err.close()
88         if len(l_values) > 3 and "updated" in l_values[3]:
89                 # Success
90                 pass
91         else:
92                 print "VALUE:", value
93                 print "ERROR: RT failed to update AdminCC for ticket %s" % ticket_id
94
95         return
96
97 def setSubjectViaRT(ticket_id, subject):
98         # Set ENV Variables/PATH
99         _setupRTenvironment()
100         if ticket_id == None or ticket_id == "":
101                 raise Exception("ERROR: ticket_id must be set to some integer value")
102
103         # This will raise an exception if it is not a valid id.
104         i_ticket_id = int(ticket_id)
105
106         cmd = "rt edit ticket/%s set subject='%s'" % (ticket_id, subject)
107         print cmd
108         (f_in, f_out, f_err) = os.popen3(cmd)
109         value = f_out.read()
110         l_values = value.split()
111         f_in.close() ; f_out.close() ; f_err.close()
112         if len(l_values) > 3 and "updated" in l_values[3]:
113                 # Success
114                 pass
115         else:
116                 print "VALUE:", value
117                 print "ERROR: RT failed to update subject for ticket %s" % ticket_id
118
119         return
120                 
121
122 def addCommentViaRT(ticket_id, comment):
123         # Set ENV Variables/PATH
124         _setupRTenvironment()
125         if ticket_id == None or ticket_id == "":
126                 raise Exception("ERROR: ticket_id must be set to some integer value")
127
128         # This will raise an exception if it is not a valid id.
129         i_ticket_id = int(ticket_id)
130
131         cmd = "rt comment -m '%s' ticket/%s" % (comment, i_ticket_id)
132         print cmd
133         (f_in, f_out, f_err) = os.popen3(cmd)
134         value = f_out.read()
135         l_values = value.split()
136         l_err = f_err.read()
137         f_in.close() ; f_out.close() ; f_err.close()
138         if len(l_values) > 1 and "recorded" in l_values[1]:
139                 # Success
140                 pass
141         else:
142                 # Error
143                 f_in.close() ; f_out.close() ; f_err.close()
144                 print "ERROR: RT failed to add comment to id %s" % ticket_id
145                 print "ERROR: %s" % l_err
146
147         return
148
149 def closeTicketViaRT(ticket_id, comment):
150         # Set ENV Variables/PATH
151         _setupRTenvironment()
152         if ticket_id == None or ticket_id == "":
153                 raise Exception("ERROR: ticket_id must be set to some integer value")
154
155         # This will raise an exception if it is not a valid id.
156         i_ticket_id = int(ticket_id)
157
158         # Append comment to RT ticket
159         addCommentViaRT(ticket_id, comment)
160
161         if not config.debug:
162                 cmd = "rt edit ticket/%s set status=resolved" % i_ticket_id
163                 print cmd
164                 (f_in, f_out, f_err) = os.popen3(cmd)
165                 f_in.close()
166                 value = f_out.read()
167                 f_out.close()
168                 f_err.close()
169                 l_values = value.split()
170                 if len(l_values) >= 4 and "updated" in l_values[3]:
171                         # Success!!
172                         pass
173                 else:
174                         print "VALUE: ", value
175                         # Failed!!
176                         print "FAILED to resolve Ticket %s" % ticket_id
177                         print "FAILED to resolve Ticket %s" % i_ticket_id
178
179         return
180
181 def emailViaRT(subject, text, to, ticket_id=None):
182         if ticket_id == None or ticket_id == "" or ticket_id == 0:
183                 print "No TICKET"
184                 return emailViaRT_NoTicket(subject, text, to)
185
186
187         # Set ENV Variables/PATH
188         _setupRTenvironment()
189
190         if config.mail and not config.debug:
191                 setSubjectViaRT(ticket_id, subject)
192                 setAdminCCViaRT(ticket_id, to)
193
194                 cmd = "rt correspond -m - %s" % ticket_id
195                 print cmd
196                 (f_in, f_out, f_err) = os.popen3(cmd)
197                 #f_in.write(input[:32000])
198                 f_in.write(text[:32000])
199                 f_in.flush()
200                 f_in.close()
201                 value = f_out.read()
202
203                 # TODO: rt doesn't write to stderr on error!!!
204                 if value == "":
205                         raise Exception, f_err.read()
206
207                 del f_in
208                 f_out.close(); del f_out
209                 f_err.close(); del f_err
210                 os.wait()
211
212         return ticket_id
213         
214
215 def emailViaRT_NoTicket(subject, text, to):
216         """Use RT command line tools to send email.
217                 return the generated RT ticket ID number.
218         """
219         i_ticket = 0
220
221         if config.mail and config.debug:
222                 to = [config.email]
223
224         # Set ENV Variables/PATH
225         _setupRTenvironment()
226
227         # NOTE: AdminCc: (in PLC's RT configuration) gets an email sent.
228         # This is not the case (surprisingly) for Cc:
229         input_text  = "Subject: %s\n"
230         input_text += "Requestor: %s\n"% FROM
231         input_text += "id: ticket/new\n"
232         input_text += "Queue: %s\n" % config.RT_QUEUE
233         for recipient in to:
234                 input_text += "AdminCc: %s\n" % recipient
235         #input_text += "AdminCc: %s\n" % config.cc_email
236         input_text += "Text: %s"
237
238         # Add a space for each new line to get RT to accept the file.
239         spaced_text = reformat_for_rt(text)
240
241         if config.mail and not config.debug:
242                 cmd = "rt create -i -t ticket"
243                 print cmd
244                 (f_in, f_out, f_err) = os.popen3(cmd)
245                 input = input_text % (subject, spaced_text)
246                 print "length: %s" % len(input)
247                 # NOTE: RT hangs with larger input, probably due to some internal
248                 # buffering.  So, chop off messages at 32000
249                 #f_in.write(input[:32000])
250                 f_in.write(input[:32000])
251                 f_in.flush()
252                 f_in.close()
253                 value = f_out.read()
254
255                 # TODO: rt doesn't write to stderr on error!!!
256                 if value == "":
257                         raise Exception, f_err.read()
258
259                 print "MAILER: ticket value == %s" % value.split()[2]
260                 i_ticket = int(value.split()[2])
261                 # clean up the child process.
262                 f_in.close();  del f_in
263                 f_out.close(); del f_out
264                 f_err.close(); del f_err
265                 os.wait()
266         elif config.mail and config.debug:
267                 email(subject, spaced_text, to)
268                 i_ticket = 0
269         else:
270                 i_ticket = 0
271
272         return i_ticket
273
274 def email(subject, text, to):
275         """Create a mime-message that will render HTML in popular
276         MUAs, text in better ones"""
277         import MimeWriter
278         import mimetools
279         import cStringIO
280
281         if (config.mail and config.debug) or (not config.mail and not config.debug and config.bcc):
282                 to = [config.email]
283
284         out = cStringIO.StringIO() # output buffer for our message 
285         txtin = cStringIO.StringIO(text)
286
287         writer = MimeWriter.MimeWriter(out)
288         #
289         # set up some basic headers... we put subject here
290         # because smtplib.sendmail expects it to be in the
291         # message body
292         #
293         writer.addheader("Subject", subject)
294         if to.__class__ == [].__class__ :       
295                 writer.addheader("To", to[0])
296                 cc = ""
297                 for dest in to[1:len(to)]:
298                         cc +="%s, " % dest
299                 cc = cc.rstrip(", ") 
300                 writer.addheader("Cc", cc)
301         else:
302                 writer.addheader("To", to)
303
304         if config.bcc and not config.debug:
305                 writer.addheader("Bcc", config.email)
306
307         writer.addheader("Reply-To", FROM)
308                 
309         writer.addheader("MIME-Version", "1.0")
310         #
311         # start the multipart section of the message
312         # multipart/alternative seems to work better
313         # on some MUAs than multipart/mixed
314         #
315         writer.startmultipartbody("alternative")
316         writer.flushheaders()
317         #
318         # the plain text section
319         #
320         subpart = writer.nextpart()
321         subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
322         pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
323         mimetools.encode(txtin, pout, 'quoted-printable')
324         txtin.close()
325         #
326         # Now that we're done, close our writer and
327         # return the message body
328         #
329         writer.lastpart()
330         msg = out.getvalue()
331         out.close()
332
333         # three cases:
334         #       mail but no-debug
335         #       mail and debug, 'to' changed at the beginning'
336         #   nomail, but report who I'd send to.
337         if config.mail:
338                 for mta in [MTA, 'golf.cs.princeton.edu']:
339                         try:
340                                 # This is normal operation
341                                 #print MTA
342                                 #print FROM
343                                 #print to
344                                 #print msg
345                                 server = smtplib.SMTP(mta)
346                                 #server = smtplib.SMTP('golf.cs.princeton.edu')
347                                 server.sendmail(FROM, to,  msg)
348                                 if config.bcc and not config.debug:
349                                         server.sendmail(FROM, config.email,  msg)
350                                 server.quit()
351                         except Exception, err:
352                                 print "Mailer error1: failed using MTA(%s) with: %s" % (mta, err)
353
354         elif not config.debug and not config.mail and config.bcc:
355                 for mta in [MTA, 'golf.cs.princeton.edu']:
356                         try:
357                                 server = smtplib.SMTP(mta)
358                                 server.sendmail(FROM, to,  msg)
359                                 server.quit()
360                         except Exception, err:
361                                 print "Mailer error2: failed using MTA(%s) with: %s" % (mta, err)
362         else:
363                 #print "Would mail %s" %to
364                 logger.debug("Would send mail to %s" % to)
365
366 if __name__=="__main__":
367         import smtplib
368         import emailTxt
369         import plc 
370         #email("[spam] bcc test from golf.cs.princeton.edu", 
371         #         "It gets to both recipients", 
372         #         "soltesz@cs.utk.edu")
373         emailViaRT("mail via RT", "Let's see if this succeeds...", [FROM])
374         #email("Re: [PL #21323] TEST 7", 
375         #                  mailtxt.newbootcd_one[1] % {'hostname_list':"hostname list..."},
376         #                  [FROM])