superguard 0.0.3__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- superguard/__init__.py +1 -0
- superguard/compat.py +26 -0
- superguard/crashmail.py +193 -0
- superguard/crashmailbatch.py +60 -0
- superguard/crashsms.py +65 -0
- superguard/fatalmailbatch.py +57 -0
- superguard/httpok.py +452 -0
- superguard/memmon.py +515 -0
- superguard/process_state_email_monitor.py +200 -0
- superguard/process_state_monitor.py +104 -0
- superguard/tests/__init__.py +0 -0
- superguard/tests/dummy.py +96 -0
- superguard/tests/test_crashmail.py +79 -0
- superguard/tests/test_crashmailbatch.py +76 -0
- superguard/tests/test_crashsms.py +17 -0
- superguard/tests/test_fatalmailbatch.py +54 -0
- superguard/tests/test_httpok.py +470 -0
- superguard/tests/test_memmon.py +407 -0
- superguard/tests/test_process_state_email_monitor.py +171 -0
- superguard/tests/test_process_state_monitor.py +113 -0
- superguard/timeoutconn.py +44 -0
- superguard-0.0.3.dist-info/METADATA +68 -0
- superguard-0.0.3.dist-info/RECORD +27 -0
- superguard-0.0.3.dist-info/WHEEL +6 -0
- superguard-0.0.3.dist-info/entry_points.txt +7 -0
- superguard-0.0.3.dist-info/licenses/LICENSE.txt +53 -0
- superguard-0.0.3.dist-info/top_level.txt +1 -0
superguard/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# superguard package
|
superguard/compat.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
try:
|
|
2
|
+
import http.client as httplib
|
|
3
|
+
except ImportError:
|
|
4
|
+
import httplib
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from StringIO import StringIO
|
|
8
|
+
except ImportError:
|
|
9
|
+
from io import StringIO
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from sys import maxsize as maxint
|
|
13
|
+
except ImportError:
|
|
14
|
+
from sys import maxint
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import urllib.parse as urlparse
|
|
18
|
+
import urllib.parse as urllib
|
|
19
|
+
except ImportError:
|
|
20
|
+
import urlparse
|
|
21
|
+
import urllib
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import xmlrpc.client as xmlrpclib
|
|
25
|
+
except ImportError:
|
|
26
|
+
import xmlrpclib
|
superguard/crashmail.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env python -u
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2007 Agendaless Consulting and Contributors.
|
|
5
|
+
# All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This software is subject to the provisions of the BSD-like license at
|
|
8
|
+
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
|
|
9
|
+
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
|
|
10
|
+
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
|
|
11
|
+
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
# A event listener meant to be subscribed to PROCESS_STATE_CHANGE
|
|
17
|
+
# events. It will send mail when processes that are children of
|
|
18
|
+
# supervisord transition unexpectedly to the EXITED state.
|
|
19
|
+
|
|
20
|
+
# A supervisor config snippet that tells supervisor to use this script
|
|
21
|
+
# as a listener is below.
|
|
22
|
+
#
|
|
23
|
+
# [eventlistener:crashmail]
|
|
24
|
+
# command =
|
|
25
|
+
# /usr/bin/crashmail
|
|
26
|
+
# -o hostname -a -m notify-on-crash@domain.com
|
|
27
|
+
# -s '/usr/sbin/sendmail -t -i -f crash-notifier@domain.com'
|
|
28
|
+
# events=PROCESS_STATE
|
|
29
|
+
#
|
|
30
|
+
# Sendmail is used explicitly here so that we can specify the 'from' address.
|
|
31
|
+
|
|
32
|
+
from __future__ import print_function
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
import sys
|
|
36
|
+
from argparse import ArgumentParser
|
|
37
|
+
|
|
38
|
+
from supervisor import childutils
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CrashMail:
|
|
42
|
+
|
|
43
|
+
def __init__(self, programs, any, email, sendmail, optionalheader):
|
|
44
|
+
|
|
45
|
+
self.programs = programs
|
|
46
|
+
self.any = any
|
|
47
|
+
self.email = email
|
|
48
|
+
self.sendmail = sendmail
|
|
49
|
+
self.optionalheader = optionalheader
|
|
50
|
+
self.stdin = sys.stdin
|
|
51
|
+
self.stdout = sys.stdout
|
|
52
|
+
self.stderr = sys.stderr
|
|
53
|
+
|
|
54
|
+
def runforever(self, test=False):
|
|
55
|
+
while 1:
|
|
56
|
+
# we explicitly use self.stdin, self.stdout, and self.stderr
|
|
57
|
+
# instead of sys.* so we can unit test this code
|
|
58
|
+
headers, payload = childutils.listener.wait(self.stdin, self.stdout)
|
|
59
|
+
|
|
60
|
+
if not headers["eventname"] == "PROCESS_STATE_EXITED":
|
|
61
|
+
# do nothing with non-TICK events
|
|
62
|
+
childutils.listener.ok(self.stdout)
|
|
63
|
+
if test:
|
|
64
|
+
self.stderr.write("non-exited event\n")
|
|
65
|
+
self.stderr.flush()
|
|
66
|
+
break
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
pheaders, pdata = childutils.eventdata(payload + "\n")
|
|
70
|
+
|
|
71
|
+
if int(pheaders["expected"]):
|
|
72
|
+
childutils.listener.ok(self.stdout)
|
|
73
|
+
if test:
|
|
74
|
+
self.stderr.write("expected exit\n")
|
|
75
|
+
self.stderr.flush()
|
|
76
|
+
break
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
msg = (
|
|
80
|
+
"Process %(processname)s in group %(groupname)s exited "
|
|
81
|
+
"unexpectedly (pid %(pid)s) from state %(from_state)s" % pheaders
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
subject = " %s crashed at %s" % (
|
|
85
|
+
pheaders["processname"],
|
|
86
|
+
childutils.get_asctime(),
|
|
87
|
+
)
|
|
88
|
+
if self.optionalheader:
|
|
89
|
+
subject = self.optionalheader + ":" + subject
|
|
90
|
+
|
|
91
|
+
self.stderr.write("unexpected exit, mailing\n")
|
|
92
|
+
self.stderr.flush()
|
|
93
|
+
|
|
94
|
+
self.mail(self.email, subject, msg)
|
|
95
|
+
|
|
96
|
+
childutils.listener.ok(self.stdout)
|
|
97
|
+
if test:
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
def mail(self, email, subject, msg):
|
|
101
|
+
body = "To: %s\n" % self.email
|
|
102
|
+
body += "Subject: %s\n" % subject
|
|
103
|
+
body += "\n"
|
|
104
|
+
body += msg
|
|
105
|
+
with os.popen(self.sendmail, "w") as m:
|
|
106
|
+
m.write(body)
|
|
107
|
+
self.stderr.write("Mailed:\n\n%s" % body)
|
|
108
|
+
self.mailed = body
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main(argv=sys.argv):
|
|
112
|
+
parser = ArgumentParser(
|
|
113
|
+
description=(
|
|
114
|
+
"Send email when processes that are children of supervisord "
|
|
115
|
+
"transition unexpectedly to the EXITED state."
|
|
116
|
+
),
|
|
117
|
+
epilog=(
|
|
118
|
+
"The -p option may be specified more than once, allowing for "
|
|
119
|
+
"specification of multiple processes. Specifying -a overrides "
|
|
120
|
+
"any selection of -p.\n\n"
|
|
121
|
+
"A sample invocation:\n\n"
|
|
122
|
+
" crashmail -p program1 -p group1:program2 -m dev@example.com"
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
parser.add_argument(
|
|
126
|
+
"-p",
|
|
127
|
+
"--program",
|
|
128
|
+
action="append",
|
|
129
|
+
default=[],
|
|
130
|
+
dest="programs",
|
|
131
|
+
help=(
|
|
132
|
+
"specify a supervisor process_name. Send mail when this process "
|
|
133
|
+
"transitions to the EXITED state unexpectedly. If this process is "
|
|
134
|
+
"part of a group, it can be specified using the "
|
|
135
|
+
"'group_name:process_name' syntax."
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
"-a",
|
|
140
|
+
"--any",
|
|
141
|
+
action="store_true",
|
|
142
|
+
default=False,
|
|
143
|
+
dest="any",
|
|
144
|
+
help=(
|
|
145
|
+
"Send mail when any child of the supervisord transitions "
|
|
146
|
+
"unexpectedly to the EXITED state. Overrides any -p parameters "
|
|
147
|
+
"passed in the same crashmail process invocation."
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
parser.add_argument(
|
|
151
|
+
"-o",
|
|
152
|
+
"--optionalheader",
|
|
153
|
+
default=None,
|
|
154
|
+
help="Specify a parameter used as a prefix in the mail subject header.",
|
|
155
|
+
)
|
|
156
|
+
parser.add_argument(
|
|
157
|
+
"-s",
|
|
158
|
+
"--sendmail_program",
|
|
159
|
+
default="/usr/sbin/sendmail -t -i",
|
|
160
|
+
dest="sendmail",
|
|
161
|
+
help=(
|
|
162
|
+
"the sendmail command to use to send email "
|
|
163
|
+
'(e.g. "/usr/sbin/sendmail -t -i"). Must be a command which '
|
|
164
|
+
"accepts header and message data on stdin and sends mail. "
|
|
165
|
+
'Default is "/usr/sbin/sendmail -t -i".'
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
parser.add_argument(
|
|
169
|
+
"-m",
|
|
170
|
+
"--email",
|
|
171
|
+
default=None,
|
|
172
|
+
help=(
|
|
173
|
+
"specify an email address. The script will send mail to this "
|
|
174
|
+
"address when crashmail detects a process crash. If no email "
|
|
175
|
+
"address is specified, email will not be sent."
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
args = parser.parse_args(argv[1:])
|
|
180
|
+
|
|
181
|
+
if "SUPERVISOR_SERVER_URL" not in os.environ:
|
|
182
|
+
sys.stderr.write("crashmail must be run as a supervisor event " "listener\n")
|
|
183
|
+
sys.stderr.flush()
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
prog = CrashMail(
|
|
187
|
+
args.programs, args.any, args.email, args.sendmail, args.optionalheader
|
|
188
|
+
)
|
|
189
|
+
prog.runforever()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
main()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env python -u
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2007 Agendaless Consulting and Contributors.
|
|
5
|
+
# All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This software is subject to the provisions of the BSD-like license at
|
|
8
|
+
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
|
|
9
|
+
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
|
|
10
|
+
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
|
|
11
|
+
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
# A event listener meant to be subscribed to PROCESS_STATE_CHANGE
|
|
17
|
+
# events. It will send mail when processes that are children of
|
|
18
|
+
# supervisord transition unexpectedly to the EXITED state.
|
|
19
|
+
|
|
20
|
+
# A supervisor config snippet that tells supervisor to use this script
|
|
21
|
+
# as a listener is below.
|
|
22
|
+
#
|
|
23
|
+
# [eventlistener:crashmailbatch]
|
|
24
|
+
# command=python crashmailbatch --toEmail=you@bar.com --fromEmail=me@bar.com
|
|
25
|
+
# events=PROCESS_STATE,TICK_60
|
|
26
|
+
|
|
27
|
+
from __future__ import print_function
|
|
28
|
+
|
|
29
|
+
from supervisor import childutils
|
|
30
|
+
from superguard.process_state_email_monitor import ProcessStateEmailMonitor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CrashMailBatch(ProcessStateEmailMonitor):
|
|
34
|
+
|
|
35
|
+
process_state_events = ["PROCESS_STATE_EXITED"]
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kwargs):
|
|
38
|
+
if kwargs.get("subject") is None:
|
|
39
|
+
kwargs["subject"] = "Crash alert from supervisord"
|
|
40
|
+
ProcessStateEmailMonitor.__init__(self, **kwargs)
|
|
41
|
+
self.now = kwargs.get("now", None)
|
|
42
|
+
|
|
43
|
+
def get_process_state_change_msg(self, headers, payload):
|
|
44
|
+
pheaders, pdata = childutils.eventdata(payload + "\n")
|
|
45
|
+
|
|
46
|
+
if int(pheaders["expected"]):
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
txt = "Process %(groupname)s:%(processname)s (pid %(pid)s) died \
|
|
50
|
+
unexpectedly" % pheaders
|
|
51
|
+
return "%s -- %s" % (childutils.get_asctime(self.now), txt)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def main():
|
|
55
|
+
crash = CrashMailBatch.create_from_cmd_line()
|
|
56
|
+
crash.run()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
main()
|
superguard/crashsms.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python -u
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2007 Agendaless Consulting and Contributors.
|
|
5
|
+
# All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This software is subject to the provisions of the BSD-like license at
|
|
8
|
+
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
|
|
9
|
+
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
|
|
10
|
+
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
|
|
11
|
+
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
##############################################################################
|
|
17
|
+
# crashsms
|
|
18
|
+
# author: Juan Batiz-Benet (http://github.com/jbenet)
|
|
19
|
+
# based on crashmailbatch.py
|
|
20
|
+
##############################################################################
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# A event listener meant to be subscribed to PROCESS_STATE_CHANGE
|
|
24
|
+
# events. It will send mail when processes that are children of
|
|
25
|
+
# supervisord transition unexpectedly to the EXITED state.
|
|
26
|
+
|
|
27
|
+
# A supervisor config snippet that tells supervisor to use this script
|
|
28
|
+
# as a listener is below.
|
|
29
|
+
#
|
|
30
|
+
# [eventlistener:crashsms]
|
|
31
|
+
# command =
|
|
32
|
+
# python crashsms
|
|
33
|
+
# -t <mobile phone>@<mobile provider> -f me@bar.com -e TICK_5
|
|
34
|
+
# events=PROCESS_STATE,TICK_5
|
|
35
|
+
|
|
36
|
+
from __future__ import print_function
|
|
37
|
+
|
|
38
|
+
from supervisor import childutils
|
|
39
|
+
from superguard.process_state_email_monitor import ProcessStateEmailMonitor
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class CrashSMS(ProcessStateEmailMonitor):
|
|
43
|
+
process_state_events = ["PROCESS_STATE_EXITED"]
|
|
44
|
+
|
|
45
|
+
def __init__(self, **kwargs):
|
|
46
|
+
ProcessStateEmailMonitor.__init__(self, **kwargs)
|
|
47
|
+
self.now = kwargs.get("now", None)
|
|
48
|
+
|
|
49
|
+
def get_process_state_change_msg(self, headers, payload):
|
|
50
|
+
pheaders, pdata = childutils.eventdata(payload + "\n")
|
|
51
|
+
|
|
52
|
+
if int(pheaders["expected"]):
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
txt = "[%(groupname)s:%(processname)s](%(pid)s) exited unexpectedly" % pheaders
|
|
56
|
+
return "%s %s" % (txt, childutils.get_asctime(self.now))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
crash = CrashSMS.create_from_cmd_line()
|
|
61
|
+
crash.run()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python -u
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2007 Agendaless Consulting and Contributors.
|
|
5
|
+
# All Rights Reserved.
|
|
6
|
+
#
|
|
7
|
+
# This software is subject to the provisions of the BSD-like license at
|
|
8
|
+
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
|
|
9
|
+
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
|
|
10
|
+
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
|
|
11
|
+
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
# A event listener meant to be subscribed to PROCESS_STATE_CHANGE
|
|
17
|
+
# events. It will send mail when processes that are children of
|
|
18
|
+
# supervisord transition unexpectedly to the EXITED state.
|
|
19
|
+
|
|
20
|
+
# A supervisor config snippet that tells supervisor to use this script
|
|
21
|
+
# as a listener is below.
|
|
22
|
+
#
|
|
23
|
+
# [eventlistener:fatalmailbatch]
|
|
24
|
+
# command=python fatalmailbatch
|
|
25
|
+
# events=PROCESS_STATE,TICK_60
|
|
26
|
+
|
|
27
|
+
from __future__ import print_function
|
|
28
|
+
|
|
29
|
+
from supervisor import childutils
|
|
30
|
+
from superguard.process_state_email_monitor import ProcessStateEmailMonitor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FatalMailBatch(ProcessStateEmailMonitor):
|
|
34
|
+
|
|
35
|
+
process_state_events = ["PROCESS_STATE_FATAL"]
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kwargs):
|
|
38
|
+
if kwargs.get("subject") is None:
|
|
39
|
+
kwargs["subject"] = "Fatal start alert from supervisord"
|
|
40
|
+
ProcessStateEmailMonitor.__init__(self, **kwargs)
|
|
41
|
+
self.now = kwargs.get("now", None)
|
|
42
|
+
|
|
43
|
+
def get_process_state_change_msg(self, headers, payload):
|
|
44
|
+
pheaders, pdata = childutils.eventdata(payload + "\n")
|
|
45
|
+
|
|
46
|
+
txt = "Process %(groupname)s:%(processname)s failed to start too many \
|
|
47
|
+
times" % pheaders
|
|
48
|
+
return "%s -- %s" % (childutils.get_asctime(self.now), txt)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
fatal = FatalMailBatch.create_from_cmd_line()
|
|
53
|
+
fatal.run()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|