#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8
#
# Munin plugin to monitor nginx cache statuses.
#
# Copyright Igor Borodikhin
#
# License : GPLv3
#
# This plugin generates two graphs - with number of requests and with percents.
# Create these symlinks:
#    ln -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_number
#    ls -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_percent
#
# You can override the log file location.
#
#   [nginx-cache-multi]
#       env.logfile /var/log/nginx/cache-access.log
#
# Nginx must be configured to include "cs=$upstream_cache_status" in the
# log file. Example format:
#
# log_format cache '$remote_addr - $host [$time_local] "$request" $status '
#                  '$body_bytes_sent "$http_referer" '
#                  'rt=$request_time ut="$upstream_response_time" '
#                  'cs=$upstream_cache_status';
#%# capabilities=autoconf
#%# family=contrib

import os, sys, re

# How we've been called
prog_name = sys.argv[0]
prog_name = prog_name[prog_name.rfind("/")+1:]

# What is graph type?
graph_type = prog_name[prog_name.rfind("_")+1:]

# Where to store plugin state
if "MUNIN_PLUGSTATE" in os.environ:
    state_dir = os.environ["MUNIN_PLUGSTATE"]
else:
    state_dir = None

# Log path
if "logfile" in os.environ:
    log_file = os.environ["logfile"]
else:
    log_file = "/var/log/nginx/access.log"

cache_status_list = { "MISS" : 0, "EXPIRED" : 0, "UPDATING" : 0, "STALE" : 0, "HIT" : 0, "NONE" : 0, "TOTAL" : 0 }

if len(sys.argv) == 2 and sys.argv[1] == "autoconf":
    print "yes"
elif len(sys.argv) == 2 and sys.argv[1] == "config":
    graph_args = ""
    if graph_type == "number":
        vlabel = "requests"
    else:
        vlabel = "%"
        graph_args = "graph_args -l 0"

    # Parent graph declaration
    print "multigraph nginx_cache_multi_%s" % graph_type
    print "graph_title Nginx cache status (%s)" % graph_type
    print "graph_category nginx"
    print "graph_vlabel %s" % vlabel
    if graph_args:
         print(graph_args)
    for key in cache_status_list.keys():
        if key != "TOTAL":
            print "%s.label %s" % (key.lower(), key.capitalize())
            print "%s.draw AREASTACK" % key.lower()
            print "%s.min 0" % key.lower()
    print ""

    for key in cache_status_list.keys():
        # Other graphs declaration
        if key != "TOTAL":
            print "multigraph nginx_cache_multi_%s.%s" % (graph_type, key.lower())
            print "graph_title Nginx cache status (%s) - %s" % (graph_type, key.capitalize())
            print "graph_category nginx"
            print "graph_vlabel %s" % vlabel
            print "%s.label %s" % (key.lower(), key.capitalize())
            print "%s.draw LINE1" % key.lower()
            print "%s.min 0" % key.lower()
            print ""
else:
    last_byte_file = "%s/%s_state" % (state_dir, prog_name)
    last_byte_handle = None

    # Load last position in log file
    try:
        last_byte_handle = open(last_byte_file, "r")
        last_byte = int(last_byte_handle.read())
    except Exception:
        last_byte = 0

    if last_byte_handle != None:
        last_byte_handle.close()

    # Open log file for reading
    try:
        log_handle = open(log_file, "r")
    except Exception:
        print "Log file %s not readable" % log_file
        sys.exit(1)

    # Find out log size and set proper position depending on log size
    try:
        log_size = int(os.path.getsize(log_file))
    except ValueError:
        log_size = 0

    if log_size < last_byte:
        last_byte = 0

    # Set position on file
    log_handle.seek(last_byte)

    reg_exp = re.compile(r"cs=([^\s]+)")

    for line in log_handle:
        match = reg_exp.search(line)
        cache_status = match.group(1)
        if cache_status == "-":
            cache_status = "NONE"
        cache_status_list[cache_status] += 1
        cache_status_list["TOTAL"] += 1

    try:
        last_byte_handle = open(last_byte_file, "w")
        last_byte_handle.write(str(log_handle.tell()))
        last_byte_handle.close()
    except Exception:
        sys.exit(1)

    log_handle.close()

    # Handle graph type (_num for absolute numbers and anything else for percents)
    if graph_type != "number":
        for key in cache_status_list.keys():
            if key != "TOTAL":
                cache_status_list[key] = cache_status_list[key] * 100 / cache_status_list["TOTAL"]
        cache_status_list["TOTAL"] = 100


    # Parent graph declaration
    print "multigraph nginx_cache_multi_%s" % graph_type
    #print "total.value %s" % cache_status_list["TOTAL"]
    for key in cache_status_list.keys():
        if key != "TOTAL":
            print "%s.value %s" % (key.lower(), cache_status_list[key])
    print ""

    for key in cache_status_list.keys():
        # Other graphs declaration
        if key != "TOTAL":
            print "multigraph nginx_cache_multi_%s.%s" % (graph_type, key.lower())
            print "total.value %s" % cache_status_list["TOTAL"]
            print "%s.value %s" % (key.lower(), cache_status_list[key])
            print ""
