4a0d661917f63b5ebfe9a0ff23a04ff98afdab7f
[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(text)
198                 f_in.flush()
199                 f_in.close()
200                 value = f_out.read()
201
202                 # TODO: rt doesn't write to stderr on error!!!
203                 if value == "":
204                         raise Exception, f_err.read()
205
206                 del f_in
207                 f_out.close(); del f_out
208                 f_err.close(); del f_err
209                 os.wait()
210
211         return ticket_id
212         
213
214 def emailViaRT_NoTicket(subject, text, to):
215         """Use RT command line tools to send email.
216                 return the generated RT ticket ID number.
217         """
218         i_ticket = 0
219
220         if config.mail and config.debug:
221                 to = [config.email]
222
223         # Set ENV Variables/PATH
224         _setupRTenvironment()
225
226         # NOTE: AdminCc: (in PLC's RT configuration) gets an email sent.
227         # This is not the case (surprisingly) for Cc:
228         input_text  = "Subject: %s\n"
229         input_text += "Requestor: %s\n"% FROM
230         input_text += "id: ticket/new\n"
231         input_text += "Queue: %s\n" % config.RT_QUEUE
232         for recipient in to:
233                 input_text += "AdminCc: %s\n" % recipient
234         input_text += "Text: %s"
235
236         # Add a space for each new line to get RT to accept the file.
237         spaced_text = reformat_for_rt(text)
238
239         if config.mail and not config.debug:
240                 cmd = "rt create -i -t ticket"
241                 print cmd
242                 (f_in, f_out, f_err) = os.popen3(cmd)
243                 f_in.write(input_text % (subject, spaced_text))
244                 f_in.flush()
245                 f_in.close()
246                 value = f_out.read()
247
248                 # TODO: rt doesn't write to stderr on error!!!
249                 if value == "":
250                         raise Exception, f_err.read()
251
252                 print "MAILER: ticket value == %s" % value.split()[2]
253                 i_ticket = int(value.split()[2])
254                 # clean up the child process.
255                 f_in.close();  del f_in
256                 f_out.close(); del f_out
257                 f_err.close(); del f_err
258                 os.wait()
259         elif config.mail and config.debug:
260                 email(subject, spaced_text, to)
261                 i_ticket = 0
262         else:
263                 i_ticket = 0
264
265         return i_ticket
266
267 def email(subject, text, to):
268         """Create a mime-message that will render HTML in popular
269         MUAs, text in better ones"""
270         import MimeWriter
271         import mimetools
272         import cStringIO
273
274         if (config.mail and config.debug) or (not config.mail and not config.debug and config.bcc):
275                 to = [config.email]
276
277         out = cStringIO.StringIO() # output buffer for our message 
278         txtin = cStringIO.StringIO(text)
279
280         writer = MimeWriter.MimeWriter(out)
281         #
282         # set up some basic headers... we put subject here
283         # because smtplib.sendmail expects it to be in the
284         # message body
285         #
286         writer.addheader("Subject", subject)
287         if to.__class__ == [].__class__ :       
288                 writer.addheader("To", to[0])
289                 cc = ""
290                 for dest in to[1:len(to)]:
291                         cc +="%s, " % dest
292                 cc = cc.rstrip(", ") 
293                 writer.addheader("Cc", cc)
294         else:
295                 writer.addheader("To", to)
296
297         if config.bcc and not config.debug:
298                 writer.addheader("Bcc", config.email)
299
300         writer.addheader("Reply-To", FROM)
301                 
302         writer.addheader("MIME-Version", "1.0")
303         #
304         # start the multipart section of the message
305         # multipart/alternative seems to work better
306         # on some MUAs than multipart/mixed
307         #
308         writer.startmultipartbody("alternative")
309         writer.flushheaders()
310         #
311         # the plain text section
312         #
313         subpart = writer.nextpart()
314         subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
315         pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
316         mimetools.encode(txtin, pout, 'quoted-printable')
317         txtin.close()
318         #
319         # Now that we're done, close our writer and
320         # return the message body
321         #
322         writer.lastpart()
323         msg = out.getvalue()
324         out.close()
325
326         # three cases:
327         #       mail but no-debug
328         #       mail and debug, 'to' changed at the beginning'
329         #   nomail, but report who I'd send to.
330         if config.mail:
331                 for mta in [MTA, 'golf.cs.princeton.edu']:
332                         try:
333                                 # This is normal operation
334                                 #print MTA
335                                 #print FROM
336                                 #print to
337                                 #print msg
338                                 server = smtplib.SMTP(mta)
339                                 #server = smtplib.SMTP('golf.cs.princeton.edu')
340                                 server.sendmail(FROM, to,  msg)
341                                 if config.bcc and not config.debug:
342                                         server.sendmail(FROM, config.email,  msg)
343                                 server.quit()
344                         except Exception, err:
345                                 print "Mailer error1: failed using MTA(%s) with: %s" % (mta, err)
346
347         elif not config.debug and not config.mail and config.bcc:
348                 for mta in [MTA, 'golf.cs.princeton.edu']:
349                         try:
350                                 server = smtplib.SMTP(mta)
351                                 server.sendmail(FROM, to,  msg)
352                                 server.quit()
353                         except Exception, err:
354                                 print "Mailer error2: failed using MTA(%s) with: %s" % (mta, err)
355         else:
356                 #print "Would mail %s" %to
357                 logger.debug("Would send mail to %s" % to)
358
359 if __name__=="__main__":
360         import smtplib
361         import emailTxt
362         import plc 
363         #email("[spam] bcc test from golf.cs.princeton.edu", 
364         #         "It gets to both recipients", 
365         #         "soltesz@cs.utk.edu")
366         emailViaRT("mail via RT", "Let's see if this succeeds...", [FROM])
367         #email("Re: [PL #21323] TEST 7", 
368         #                  mailtxt.newbootcd_one[1] % {'hostname_list':"hostname list..."},
369         #                  [FROM])