#!/usr/bin/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




=head1 CONFIGURATION

By default the monit instance at localhost is queried:

  [monit_parser]
  env.port 2812
  env.host localhost


=head1 AUTHOR

  Todd Troxell <ttroxell@debian.org>
  Lars Kruse <devel@sumpfralle.de>


=head1 MAGIC MARKERS

  family=auto
  capabilities=autoconf

=cut
"""

import xml.dom.minidom
import os
import sys
import urllib.request

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:
        conn = urllib.request.urlopen(MONIT_XML_URL)
    except urllib.error.URLError as exc:
        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 monit')
    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))
