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