Browse files

Merge pull request #11 from frisi/master

memmon -n option for adding a project name to the email's subject
  • Loading branch information...
2 parents ebde674 + bec8c27 commit 7f11b06817992aee896d68375ebe30e8e12a055c @mnaberez mnaberez committed Mar 27, 2013
Showing with 76 additions and 26 deletions.
  1. +9 −0 CHANGES.txt
  2. +12 −1 docs/memmon.rst
  3. +27 −16 superlance/memmon.py
  4. +28 −9 superlance/tests/memmon_test.py
View
9 CHANGES.txt
@@ -3,6 +3,15 @@ Next Release
- Superlance will now refuse to install on an unsupported version of Python.
+Next Release
+------------
+
+- Added ``-n`` option to memmon that adds this name to the email
+ subject to identify which memmon process restarted a process.
+ Useful in case you run multiple supervisors that control
+ different processes with the same name.
+ [fRiSi]
+
0.7 (2012-08-22)
----------------
View
13 docs/memmon.rst
@@ -29,7 +29,7 @@ Command-Line Syntax
.. code-block:: sh
$ memmon [-p processname=byte_size] [-g groupname=byte_size] \
- [-a byte_size] [-s sendmail] [-m email_address]
+ [-a byte_size] [-s sendmail] [-m email_address] [-n memmon_name]
.. program:: memmon
@@ -84,6 +84,17 @@ Command-Line Syntax
By default, memmon will not send any mail unless an email address is
specified.
+.. cmdoption:: -n <memmon name>, --name=<memmon name>
+
+ An optional name that identifies this memmon process. If given, the
+ email subject will start with ``memmon [<memmon name>]:`` instead
+ of ``memmon:``
+ In case you run multiple supervisors on a single host that control
+ different processes with the same name (eg `zopeinstance1`) you can
+ use this option to indicate which project the restarted instance
+ belongs to.
+
+
Configuring :command:`memmon` Into the Supervisor Config
--------------------------------------------------------
View
43 superlance/memmon.py
@@ -27,19 +27,20 @@
# events=TICK_60
doc = """\
-memmon.py [-p processname=byte_size] [-g groupname=byte_size]
+memmon.py [-p processname=byte_size] [-g groupname=byte_size]
[-a byte_size] [-s sendmail] [-m email_address]
+ [-n memmon_name]
Options:
-p -- specify a process_name=byte_size pair. Restart the supervisor
process named 'process_name' when it uses more than byte_size
RSS. If this process is in a group, it can be specified using
the 'process_name:group_name' syntax.
-
+
-g -- specify a group_name=byte_size pair. Restart any process in this group
when it uses more than byte_size RSS.
-
+
-a -- specify a global byte_size. Restart any child of the supervisord
under which this runs if it uses more than byte_size RSS.
@@ -52,6 +53,10 @@
address when any process is restarted. If no email address is
specified, email will not be sent.
+-n -- optionally specify the name of the memmon process. This name will
+ be used in the email subject to identify which memmon process
+ restarted the process.
+
The -p and -g options may be specified more than once, allowing for
specification of multiple groups and processes.
@@ -61,7 +66,7 @@
A sample invocation:
-memmon.py -p program1=200MB -p theprog:thegroup=100MB -g thegroup=100MB -a 1GB -s "/usr/sbin/sendmail -t -i" -m chrism@plope.com
+memmon.py -p program1=200MB -p theprog:thegroup=100MB -g thegroup=100MB -a 1GB -s "/usr/sbin/sendmail -t -i" -m chrism@plope.com -n "Project 1"
"""
import os
@@ -80,12 +85,13 @@ def shell(cmd):
return os.popen(cmd).read()
class Memmon:
- def __init__(self, programs, groups, any, sendmail, email, rpc):
+ def __init__(self, programs, groups, any, sendmail, email, name, rpc):
self.programs = programs
self.groups = groups
self.any = any
self.sendmail = sendmail
self.email = email
+ self.memmonName = name
self.rpc = rpc
self.stdin = sys.stdin
self.stdout = sys.stdout
@@ -174,15 +180,15 @@ def runforever(self, test=False):
def restart(self, name, rss):
self.stderr.write('Restarting %s\n' % name)
-
+ memmonId = self.memmonName and " [%s]" % self.memmonName or ""
try:
self.rpc.supervisor.stopProcess(name)
except xmlrpclib.Fault, what:
msg = ('Failed to stop process %s (RSS %s), exiting: %s' %
(name, rss, what))
self.stderr.write(str(msg))
if self.email:
- subject = 'memmon: failed to stop process %s, exiting' % name
+ subject = 'memmon%s: failed to stop process %s, exiting' % (memmonId, name)
self.mail(self.email, subject, msg)
raise
@@ -193,7 +199,7 @@ def restart(self, name, rss):
'exiting: %s' % (name, what))
self.stderr.write(str(msg))
if self.email:
- subject = 'memmon: failed to start process %s, exiting' % name
+ subject = 'memmon%s: failed to start process %s, exiting' % (memmonId, name)
self.mail(self.email, subject, msg)
raise
@@ -204,7 +210,7 @@ def restart(self, name, rss):
'it was consuming too much memory (%s bytes RSS)' % (
name, now, rss)
)
- subject = 'memmon: process %s restarted' % name
+ subject = 'memmon%s: process %s restarted' % (memmonId, name)
self.mail(self.email, subject, msg)
def mail(self, email, subject, msg):
@@ -216,7 +222,7 @@ def mail(self, email, subject, msg):
m.write(body)
m.close()
self.mailed = body
-
+
def parse_namesize(option, value):
try:
name, size = value.split('=')
@@ -232,19 +238,20 @@ def parse_size(option, value):
except:
print 'Unparseable byte_size in %r for %r' % (value, option)
usage()
-
+
return size
def main():
import getopt
- short_args="hp:g:a:s:m:"
+ short_args="hp:g:a:s:m:n:"
long_args=[
"help",
"program=",
"group=",
"any=",
"sendmail_program=",
"email=",
+ "name=",
]
arguments = sys.argv[1:]
if not arguments:
@@ -260,6 +267,7 @@ def main():
any = None
sendmail = '/usr/sbin/sendmail -t -i'
email = None
+ name = None
for option, value in opts:
@@ -284,12 +292,15 @@ def main():
if option in ('-m', '--email'):
email = value
+ if option in ('-n', '--name'):
+ name = value
+
rpc = childutils.getRPCInterface(os.environ)
- memmon = Memmon(programs, groups, any, sendmail, email, rpc)
+ memmon = Memmon(programs, groups, any, sendmail, email, name, rpc)
memmon.runforever()
if __name__ == '__main__':
main()
-
-
-
+
+
+
View
37 superlance/tests/memmon_test.py
@@ -7,21 +7,22 @@ class MemmonTests(unittest.TestCase):
def _getTargetClass(self):
from superlance.memmon import Memmon
return Memmon
-
+
def _makeOne(self, *opts):
return self._getTargetClass()(*opts)
def _makeOnePopulated(self, programs, groups, any):
rpc = DummyRPCServer()
sendmail = 'cat - > /dev/null'
email = 'chrism@plope.com'
- memmon = self._makeOne(programs, groups, any, sendmail, email, rpc)
+ name = 'test'
+ memmon = self._makeOne(programs, groups, any, sendmail, email, name, rpc)
memmon.stdin = StringIO()
memmon.stdout = StringIO()
memmon.stderr = StringIO()
memmon.pscommand = 'echo 22%s'
return memmon
-
+
def test_runforever_notatick(self):
programs = {'foo':0, 'bar':0, 'baz_01':0 }
groups = {}
@@ -54,7 +55,7 @@ def test_runforever_tick_programs(self):
self.assertEqual(len(mailed), 4)
self.assertEqual(mailed[0], 'To: chrism@plope.com')
self.assertEqual(mailed[1],
- 'Subject: memmon: process baz:baz_01 restarted')
+ 'Subject: memmon [test]: process baz:baz_01 restarted')
self.assertEqual(mailed[2], '')
self.failUnless(mailed[3].startswith('memmon.py restarted'))
@@ -76,7 +77,7 @@ def test_runforever_tick_groups(self):
self.assertEqual(len(mailed), 4)
self.assertEqual(mailed[0], 'To: chrism@plope.com')
self.assertEqual(mailed[1],
- 'Subject: memmon: process foo:foo restarted')
+ 'Subject: memmon [test]: process foo:foo restarted')
self.assertEqual(mailed[2], '')
self.failUnless(mailed[3].startswith('memmon.py restarted'))
@@ -122,7 +123,7 @@ def test_runforever_tick_programs_and_groups(self):
self.assertEqual(len(mailed), 4)
self.assertEqual(mailed[0], 'To: chrism@plope.com')
self.assertEqual(mailed[1],
- 'Subject: memmon: process baz:baz_01 restarted')
+ 'Subject: memmon [test]: process baz:baz_01 restarted')
self.assertEqual(mailed[2], '')
self.failUnless(mailed[3].startswith('memmon.py restarted'))
@@ -188,9 +189,27 @@ def test_stopprocess_fails_to_stop(self):
self.assertEqual(len(mailed), 4)
self.assertEqual(mailed[0], 'To: chrism@plope.com')
self.assertEqual(mailed[1],
- 'Subject: memmon: failed to stop process BAD_NAME:BAD_NAME, exiting')
+ 'Subject: memmon [test]: failed to stop process BAD_NAME:BAD_NAME, exiting')
self.assertEqual(mailed[2], '')
self.failUnless(mailed[3].startswith('Failed'))
-
+
+ def test_subject_no_name(self):
+ """set the name to None to check if subject
+ stays `memmon:...` instead `memmon [<name>]:...`
+ """
+ programs = {}
+ groups = {}
+ any = 0
+ memmon = self._makeOnePopulated(programs, groups, any)
+ memmon.memmonName = None
+ memmon.stdin.write('eventname:TICK len:0\n')
+ memmon.stdin.seek(0)
+ memmon.runforever(test=True)
+
+ mailed = memmon.mailed.split('\n')
+ self.assertEqual(mailed[1],
+ 'Subject: memmon: process baz:baz_01 restarted')
+
+
if __name__ == '__main__':
- unittest.main()
+ unittest.main()

0 comments on commit 7f11b06

Please sign in to comment.