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