#!/usr/bin/env python3 """ =head1 NAME monit_parser - Monit status parser plugin for munin. =head1 APPLICABLE SYSTEMS Any system being able to query monit servers via http. Monit needs to be configured with the httpd port enabled, e.g.: set httpd port 2812 Optionally monit authentication can be configured, e.g.: set httpd port 2812 allow munin:s3cr3t read-only =head1 CONFIGURATION By default the monit instance at localhost is queried: [monit_parser] env.port 2812 env.host localhost Optionally monit authentication can be configured, e.g.: [monit_parser] env.username munin env.password s3cr3t =head1 AUTHOR Todd Troxell Lars Kruse =head1 MAGIC MARKERS family=auto capabilities=autoconf =cut """ import xml.dom.minidom import os import sys import urllib.request import base64 MONIT_XML_URL = ("http://{host}:{port}/_status?format=xml" .format(host=os.getenv("host", "localhost"), port=os.getenv("port", "2812"))) def sanitize(s): ok_chars = "abcdefghijklmnopqrstuvwxyz0123456789" return "".join([char for char in s if char in ok_chars]) def get_monit_status_xml(): try: req = urllib.request.Request(url=MONIT_XML_URL) if os.getenv("password"): auth_str = "%s:%s" % (os.getenv("username"), os.getenv("password")) auth_bytes = bytes(auth_str, "utf-8") auth_base64_bytes = base64.b64encode(auth_bytes) auth_base64_str = str(auth_base64_bytes, "ascii") auth_value = "Basic %s" % auth_base64_str req.add_header("Authorization", auth_value) conn = urllib.request.urlopen(req) except urllib.error.URLError: conn = None if conn is None: raise RuntimeError("Failed to open monit status URL: {}".format(MONIT_XML_URL)) else: return xml.dom.minidom.parse(conn) def parse_processes(): dom = get_monit_status_xml() procs = {} for item in dom.getElementsByTagName("service"): if item.getAttribute("type") == "3": # daemon with memory usage and CPU name = item.getElementsByTagName("name")[0].childNodes[0].data memory_usage = item.getElementsByTagName("memory")[0].getElementsByTagName( "kilobytetotal")[0].childNodes[0].data cpu_usage = item.getElementsByTagName("cpu")[0].getElementsByTagName( "percenttotal")[0].childNodes[0].data procs[name] = {} procs[name]["total_memory"] = memory_usage procs[name]["total_cpu"] = cpu_usage return procs action = sys.argv[1] if (len(sys.argv) > 1) else None if action == 'autoconf': try: get_monit_status_xml() print("yes") except RuntimeError: print("no (failed to request monit status)") elif action == 'config': procs = parse_processes() print('graph_title Per process stats from Monit') print('graph_vlabel usage of memory [kB] or cpu [%]') print('graph_category munin') for process in procs: for stat in procs[process]: print("monit_%s_%s.label %s.%s" % (sanitize(process), stat, process, stat)) if stat == 'total_memory': # the allocated memory may never be zero print("monit_%s_%s.warning 1:" % (sanitize(process), stat)) else: for process, stats in parse_processes().items(): for stat_key, stat_value in stats.items(): print("monit_%s_%s.value %s" % (sanitize(process), stat_key, stat_value))