#!/usr/bin/python

'''
Wildcard plugin to monitor Apache Tomcat connectors and/or JBoss' JVM.

To use this plugin:
1. Set site, username and password variables before you do anything else.
2. Run plugin with suggest argument to get all the available options.
3. Copy plugin to munin plugins folder and make it executable.
4. Create symbolic links based on output from step 2. Examples:
    tomcat_jvm - monitor JVM usage
    tomcat_ajp-127.0.0.1-8009 - monitor ajp connector
    tomcat_http-127.0.0.1-8080 - monitor http connector
5. Check links by running them.
6. Restart munin-node.
7. Enjoy graphs.

Munin's muni-node-configure can be used to do steps 2, 3 and 4 for you.

Example of using munin-node configuration to configure the plugin:

[tomcat_jvm]
    env.site http://127.0.0.1:8080/status?XML=true
    env.username admin
    env.password admin
    env.url_timeout 5
    timeout 10

url_timeout - timeout for urllib2
timeout - plugin timeout

Plugin must return before timeout value runs out, otherwise bye, bye
graphs.

Magic markers
#%# capabilities=autoconf suggest
#%# family=auto
'''

import urllib2
import base64
import xml.dom.minidom
import sys, os, re

# Configure me ...
site = 'http://127.0.0.1:8080/status?XML=true'
url_timeout = 5
username = 'admin'
password = 'admin'

# Or do it with the environment variables in munin-node configuration.
if os.environ.has_key('site'):
    site = os.environ['site']

if os.environ.has_key('url_timeout'):
    url_timeout = os.environ['url_timeout']

if os.environ.has_key('username'):
    username = os.environ['username']

if os.environ.has_key('password'):
    password = os.environ['password']

# Timeout for urlopen.
required_version = (2, 6)
current_version = sys.version_info[:2]

connector_attrs = (
    'maxThreads',
    'minSpareThreads',
    'maxSpareThreads',
    'currentThreadCount',
    'currentThreadBusy'
)

jvm_attrs = (
    'free',
    'total',
    'max'
)

ctx = sys.argv[0].rstrip('.py').split('_')[1]

def site_auth():
    # Prepare base64 encoded string
    enc_string = base64.encodestring('%s:%s' % (username, password))

    # Prepare request and add headers
    request = urllib2.Request(url=site, headers={"Authorization": "Basic %s" % enc_string})
    try:
        if current_version >= required_version:
            return (0, urllib2.urlopen(request, timeout=url_timeout).read())
        else:
            import socket
            socket.setdefaulttimeout(url_timeout)
            return (0, urllib2.urlopen(request).read())
    except:
        return (-1, "Failed to access %s" % site)

def jvm_data(data):
    document = data.documentElement
    for sub_document in document.childNodes:
        if sub_document.nodeName == 'jvm':
            node = sub_document.firstChild
            for attr in jvm_attrs:
                print "%s.value %s" % (attr, int(node.getAttribute(attr)))

def connector_data(data, connector_name):
    document = data.documentElement
    for sub_document in document.childNodes:
        if sub_document.nodeName == 'connector' and sub_document.getAttribute('name') == connector_name:
            node = sub_document.firstChild
            for attr in connector_attrs:
                try:
                    print "%s.value %s" % (attr, int(node.getAttribute(attr)))
                except:
                    pass

def suggest(data):
    document = data.documentElement
    for sub_document in document.childNodes:
        if sub_document.nodeName == 'jvm':
            print "jvm"
        elif sub_document.nodeName == 'connector':
            print sub_document.getAttribute('name')
        else:
            pass

def configure():
    print "graph_title Tomcat status - %s" % ctx
    print "graph_category tomcat"
    if ctx == 'jvm':
        print "graph_args --base 1024 -l 0"
        print "graph_scale yes"
        print "graph_vlabel JVM in bytes"
        print "graph_info This graph shows JVM usage of Tomcat."
        for attr in jvm_attrs:
            print "%s.label %s" % (attr, attr)
            print "%s.type GAUGE" % (attr)
            print "%s.min 0" % (attr)
            print "%s.draw LINE1" % (attr)
            print "%s.info %s %s in bytes" % (attr, ctx, attr)
    else:
        print "graph_args --base 1000 -l 0"
        print "graph_scale no"
        print "graph_vlabel Connector threads"
        print "graph_info This graph shows connector threads for %s" % ctx
        for attr in connector_attrs:
            print "%s.label %s" % (attr, attr)
            print "%s.type GAUGE" % (attr)
            print "%s.min 0" % (attr)
            print "%s.draw LINE1" % (attr)
            print "%s.info %s %s count" % (attr, ctx, attr)

if __name__ == "__main__":
    status, data = site_auth()
    if len(sys.argv) == 2 and sys.argv[1] == 'config':
        configure()
        sys.exit(0)

    elif len(sys.argv) == 2 and sys.argv[1] == 'suggest':
        suggest(xml.dom.minidom.parseString(data))
        sys.exit(0)

    elif len(sys.argv) == 2 and sys.argv[1] == 'autoconf':
        if status == 0:
            print "yes"
        else:
            print "no (%s)" % data
        sys.exit(0)

    else:
        if ctx == 'jvm':
            if status == -1:
                for attr in jvm_attrs:
                    print "%s.value 0" % (attr)
            else:
                jvm_data(xml.dom.minidom.parseString(data))
        else:
            if status == -1:
                    for attr in connector_attrs:
                        print "%s.value 0" % (attr)
            else:
                connector_data(xml.dom.minidom.parseString(data), ctx)
