Skip to content

Instantly share code, notes, and snippets.

@matbor
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matbor/7d872b94a19a89771e14 to your computer and use it in GitHub Desktop.
Save matbor/7d872b94a19a89771e14 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
#
# Based on https://github.com/Supervisor/superlance/blob/master/superlance/crashmail.py
#
# A event listener meant to be subscribed to PROCESS_STATE_CHANGE
# events. It will publish an MQTT message when processes that are children of
# supervisord transition unexpectedly to the EXITED state.
# A supervisor config snippet that tells supervisor to use this script
# as a listener is below.
#
# [eventlistener:crashmqtt]
# command =
# /usr/bin/crashmqtt
# -p program -a
# -h mqttbroker -o mqttport
# -u mqttuser -P mqttpwd
# -t mqtttopic
# -m mqttmessage
#
# events=PROCESS_STATE
#
doc = """\ crashmqtt.py [-p processname] [-a]
[-b mqttbroker] [-o mqttport] [-u mqttuser] [-P mqttpwd]
[-t mqtttopic]
Options:
-p -- specify a supervisor process_name. Publish MQTT when this process
transitions to the EXITED state unexpectedly. If this process is
part of a group, it can be specified using the
'process_name:group_name' syntax.
-a -- Publish MQTT when any child of the supervisord transitions
unexpectedly to the EXITED state unexpectedly. Overrides any -p
parameters passed in the same crashmqtt process invocation.
-b -- MQTT broker address. Defaults to localhost.
-o -- MQTT broker port. Defaults to 1883.
-u -- MQTT broker user.
-P -- MQTT broker password.
-t -- MQTT topic to publish to. Will substitute event details in topic string.
-m -- MQTT message to publish. Will substitute event details in message string.
The -p option may be specified more than once, allowing for
specification of multiple processes. Specifying -a overrides any
selection of -p.
A sample invocation:
crashmqtt.py -p program1 -p group1:program2 -t monitor/{processname} -m 'terminated'
"""
import paho.mqtt.publish as mqtt
import os
import sys
from supervisor import childutils
def usage():
print(doc)
sys.exit(255)
class CrashMqtt:
def __init__(self, programs, any,
mqttbroker, mqttport, mqttuser, mqttpwd,
mqtttopic, mqttmsg):
self.programs = programs
self.any = any
self.mqttbroker = mqttbroker
self.mqttport = mqttport
self.mqttuser = mqttuser
self.mqttpwd = mqttpwd
self.mqtttopic = mqtttopic
self.mqttmsg = mqttmsg
self.stdin = sys.stdin
self.stdout = sys.stdout
self.stderr = sys.stderr
self.mqttclient = "crashmqtt"
self.mqttqos = 0
self.mqttretain = False
def runforever(self, test=False):
while 1:
# we explicitly use self.stdin, self.stdout, and self.stderr
# instead of sys.* so we can unit test this code
headers, payload = childutils.listener.wait(
self.stdin, self.stdout)
if not headers['eventname'] == 'PROCESS_STATE_EXITED':
# do nothing with non-TICK events
childutils.listener.ok(self.stdout)
if test:
self.stderr.write('non-exited event\n')
self.stderr.flush()
break
continue
pheaders, pdata = childutils.eventdata(payload+'\n')
if int(pheaders['expected']):
childutils.listener.ok(self.stdout)
if test:
self.stderr.write('expected exit\n')
self.stderr.flush()
break
continue
topic = self.mqtttopic.format(**pheaders)
msg = self.mqttmsg.format(**pheaders)
self.stderr.write('unexpected exit, publishing MQTT\n')
self.stderr.flush()
self.mqtt_pub(topic, msg)
childutils.listener.ok(self.stdout)
if test:
break
def mqtt_pub(self, topic, msg):
params = {
'hostname' : self.mqttbroker,
'port' : self.mqttport,
'client_id' : self.mqttclient,
'qos' : self.mqttqos,
'retain' : self.mqttretain,
}
auth = None
tls = None
if self.mqttuser is not None:
auth = {
'username' : self.mqttuser,
'password' : self.mqttpwd
}
self.stderr.write('publishing \'%s\' to [%s:%d]%s...' %
(msg, self.mqttbroker, self.mqttport, topic))
mqtt.single(topic, msg, auth=auth, tls=tls, **params)
self.stderr.write('success\n')
self.stderr.flush()
def main(argv=sys.argv):
import getopt
short_args = "hp:ab:o:u:P:t:m:"
long_args = [
"help",
"program=",
"any",
"mqtt_broker=",
"mqtt_port=",
"mqtt_user=",
"mqtt_pwd=",
"mqtt_topic=",
"mqtt_msg=",
]
arguments = argv[1:]
try:
opts, args = getopt.getopt(arguments, short_args, long_args)
except:
usage()
programs = []
any = False
mqttbroker = "localhost"
mqttport = 1883
mqttuser = None
mqttpwd = None
mqttmsg = "0"
for option, value in opts:
if option in ('-h', '--help'):
usage()
if option in ('-p', '--program'):
programs.append(value)
if option in ('-a', '--any'):
any = True
if option in ('-b', '--mqtt_broker'):
mqttbroker = value
if option in ('-o', '--mqtt_port'):
mqttport = int(value)
if option in ('-u', '--mqtt_user'):
mqttuser = value
if option in ('-P', '--mqtt_pwd'):
mqttpwd = value
if option in ('-t', '--mqtt_topic'):
mqtttopic = value
if option in ('-m', '--mqtt_msg'):
mqttmsg = value
if not 'SUPERVISOR_SERVER_URL' in os.environ:
sys.stderr.write('crashmqtt must be run as a supervisor event '
'listener\n')
sys.stderr.flush()
return
prog = CrashMqtt(programs, any,
mqttbroker, mqttport, mqttuser, mqttpwd,
mqtttopic, mqttmsg)
prog.runforever()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment