1#!/usr/bin/env python2
2
3"""Receive messages from supervisor process over stdin, using supervisor utility functions.
4
5Send an email in response to status changes in other processes.
6"""
7
8import os
9import sys
10import socket
11import smtplib
12import argparse
13
14def log(message):
15 """Log `message` to the supervisord stderr file."""
16 sys.stderr.write('{message}\n'.format(message=message))
17 sys.stderr.flush()
18
19
20def listen(sendmail_program,
21 to_address,
22 from_address,
23 subject_prefix):
24 """Listen for messages and send emails."""
25 from supervisor import childutils # throws off lint/doctest tools because this is
26 # python2 code
27
28 log('Listening')
29
30 while True:
31 # we explicitly use self.stdin, self.stdout, and self.stderr
32 # instead of sys.* so we can unit test this code
33 headers, payload = childutils.listener.wait(sys.stdin, sys.stdout)
34 log('headers ' + str(headers))
35 log('payload ' + str(payload))
36
37 # if not headers['eventname'] == 'PROCESS_STATE_EXITED':
38 # do nothing with non-TICK events
39 # childutils.listener.ok(self.stdout)
40 # if test:
41 # self.stderr.write('non-exited event\n')
42 # self.stderr.flush()
43 # break
44 # continue
45
46 pheaders, pdata = childutils.eventdata(payload + '\n')
47
48 # if int(pheaders['expected']):
49 # childutils.listener.ok(self.stdout)
50 # if test:
51 # self.stderr.write('expected exit\n')
52 # self.stderr.flush()
53 # break
54 # continue
55
56 log('pheaders ' + str(pheaders))
57 log('pdata ' + str(pdata))
58
59 # headers['server'] gives a decent subj prefix
60
61 # headers {'ver': '3.0', 'poolserial': '10', 'len': '89', 'server': 'CHART_MIG',
62 # 'eventname': 'PROCESS_STATE_RUNNING', 'serial': '10', 'pool': 'event_notifications'}
63 # payload processname:event_notifications groupname:event_notifications
64 # from_state:STARTING pid:643
65 # pheaders {'from_state': 'STARTING', 'processname': 'event_notifications', 'pid': '643',
66 # 'groupname': 'event_notifications'}
67 # pdata
68
69 if from_address is None:
70 from_address = '{user} <{nonspaceuser}@{hostname}>'.format(
71 user=headers.get('server', '').replace('_', ' '),
72 nonspaceuser=headers.get('server', '').replace('_',''),
73 hostname=socket.gethostname())
74
75 if 'server' in headers and len(headers['server']) > 0:
76 subject_prefix = '{name}: '.format(name=headers['server'].replace('_', ' '))
77
78 else:
79 subject_prefix = ''
80
81 subject = '{prefix}{processname} entered {eventname}'.format(
82 prefix=subject_prefix,
83 processname=pheaders.get('processname', 'supervisord'),
84 eventname=headers['eventname'])
85
86 message = 'Process {processname} entered {eventname} from {oldstate}'.format(
87 processname=pheaders.get('processname', 'supervisord'),
88 eventname=headers['eventname'],
89 oldstate=pheaders.get('from_state'))
90
91 sendmail(sendmail_program,
92 to_address,
93 from_address,
94 subject,
95 message)
96 childutils.listener.ok(sys.stdout)
97
98
99def sendmail(sendmail_program,
100 to_address,
101 from_address,
102 subject,
103 message,
104 fake=False):
105 """Send an email."""
106 # m = os.popen(sendmail_program, 'w')
107 # m.write(body)
108 # m.close()
109 # sys.stderr.write('Mailed:\n\n{body}'.format(body=body))
110 body = """To: {to_address}
111From: {from_address}
112Subject: {subject}
113
114{message}""".format(to_address=to_address,
115 from_address=from_address,
116 subject=subject,
117 message=message)
118
119 if not fake:
120 s = smtplib.SMTP()
121 s.connect()
122 s.sendmail(from_address,
123 [to_address],
124 body)
125 s.close()
126
127 log(body)
128
129
130def main():
131 """Command line entry point."""
132 if 'SUPERVISOR_SERVER_URL' not in os.environ:
133 sys.stderr.write('This program must be run as a supervisord process (SUPERVISOR_SERVER_URL not set)\n')
134 sys.stderr.flush()
135 return
136
137 parser = argparse.ArgumentParser()
138 parser.add_argument('--to-address', '-m',
139 help=('Email address(s) to send alerts to. Use "name <address>" for nice '
140 'display'))
141 parser.add_argument('--from-address', '--from',
142 help=('Email address(s) to send alerts to. Use "name <address>" for nice '
143 'display'))
144 parser.add_argument('--sendmail', '-s',
145 help='Command to run sendmail program',
146 default='sendmail -t -i')
147 parser.add_argument('--subject-prefix', '-p',
148 help='Prefix subject field of emails')
149 parser.add_argument('--test',
150 action='store_true',
151 help='Send a test email')
152 parser.add_argument('--fake',
153 action='store_true',
154 help='Do not send emails')
155 args = parser.parse_args()
156
157 if not args.to_address:
158 parser.error('No recipient address specified')
159
160 if args.test:
161 sendmail(sendmail_program=args.sendmail,
162 to_address=args.to_address,
163 from_address='Chart',
164 subject='Test message',
165 message='This is a test message',
166 fake=args.fake)
167 parser.exit()
168
169 listen(sendmail_program=args.sendmail,
170 to_address=args.to_address,
171 from_address=args.from_address,
172 subject_prefix=args.subject_prefix)
173
174if __name__ == '__main__':
175 main()