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