#!/usr/bin/env perl
=head1 NAME

memcached_servers_ - Munin multigraph plugin to monitor multiple memcache servers

=head1 CONFIGURATION

You need to configure the to-be-used servers and the corresponding labels separated
by spaace in your environment. Example:

[memcached_servers_*]
  env.addresses cache1.server.com:11211 cache2.server.com:11211
  env.labels master slave

Please note that the number of labels and addresses must be equal.


=head1 MULTIGRAPH

With munin multigraph capabilities, you can generate different graphs. Available
graphs include:

=over

=item bytes: memcached bytes usage

=item hits: get and set hits

=item items: number of stored items

=item requests: number of requests

=item traffic: traffic

=back

Link the plugin to get the desirec output, for example: memcached_multi_bytes


=head1 DEPENDENCIES

=over

=item Cache::Memcached

=back


=head1 ACKNOWLEDGEMENTS

This plugin is based on the available memcached plugins at
L<https://github.com/munin-monitoring/contrib/tree/master/plugins/memcached>

=head1 AUTHORS

Jonas Kaufmann <jonas@windfinder.com>

=cut

use strict;
use warnings;
use Cache::Memcached;
use File::Basename;

#Configuration of used servers in environment
my @addresses = split(/ /, $ENV{addresses});
my @labels = split(/ /, $ENV{labels});

if ($#addresses < 0 || $#labels < 0) {
	print "Error: you need to configure addresses and labels in your environment.\n";
	exit 1;
}

if ($#addresses != $#labels) {
	print "Error: number of addresses and labels must be equal.\n";
	exit 1;
}

# Configuration of modes for multigraph
my @modes = qw(bytes hits items requests traffic);

my $mode = substr(basename($0), length('memcached_servers_'));
if (($mode eq '') || ! grep($_ eq $mode, @modes)) {
	$mode = $modes[0];
}


# Output for munin config
my $cmd = shift || '';
if ($cmd eq 'config') {
	# Labels and basic graph information
	if ($mode eq 'bytes') {
		print "graph_title Memcached bytes used\n";
		print "graph_args --base 1024 -l 0\n";
		print "graph_vlabel bytes\n";
		print "graph_category memory\n";
		print "graph_info This graph monitors the size of the memcached cache.\n";
	} elsif ($mode eq 'hits') {
		print "graph_title Memcached cache hits and misses\n";
		print "graph_args --base 1000 -l 0\n";
		print "graph_vlabel requests\n";
		print "graph_category memory\n";
		print "graph_info This graph monitors the number of cache hits and misses.\n";
	} elsif ($mode eq 'items') {
		print "graph_title Memcached cached items\n";
		print "graph_args --base 1000 -l 0\n";
		print "graph_vlabel items\n";
		print "graph_category memory\n";
		print "graph_info This graph monitors the number of items stored by the memcached server.\n";
	} elsif ($mode eq 'requests') {
		print "graph_title Memcached requests\n";
		print "graph_args --base 1000 -l 0\n";
		print "graph_vlabel requests\n";
		print "graph_category memory\n";
		print "graph_info This graph monitors the number of get and set requests.\n";
	} elsif ($mode eq 'traffic') {
		print "graph_title Memcached network traffic\n";
		print "graph_args --base 1000 -l 0\n";
		print "graph_vlabel bits per \${graph_period}\n";
		print "graph_category memory\n";
		print "graph_info This graph monitors the network traffic of the memcached server.\n";
	}


	# Graph configuration
	for (my $i = 0; $i < @labels; $i++) {
		if ($mode eq 'bytes') {
			print "bytes_" . $labels[$i] . ".label bytes used (" . $labels[$i] . ")\n";
			print "bytes_" . $labels[$i] . ".info Number of bytes currently used (" . $labels[$i] . ")\n";
			print "bytes_" . $labels[$i] . ".min 0\n";
			print "maxbytes_" . $labels[$i] . ".label maximum available (" . $labels[$i] . ")\n";
			print "maxbytes_" . $labels[$i] . ".info The configured cache size (" . $labels[$i] . ")\n";
			print "maxbytes_" . $labels[$i] . ".min 0\n";
		} elsif ($mode eq 'hits') {
			print "hits_" . $labels[$i] . ".label hits (" . $labels[$i] . ")\n";
			print "hits_" . $labels[$i] . ".info Number of cache hits (" . $labels[$i] . ")\n";
			print "hits_" . $labels[$i] . ".min 0\n";
			print "hits_" . $labels[$i] . ".type DERIVE\n";
			print "misses_" . $labels[$i] . ".label misses (" . $labels[$i] . ")\n";
			print "misses_" . $labels[$i] . ".info Number of cache misses (" . $labels[$i] . ")\n";
			print "misses_" . $labels[$i] . ".min 0\n";
			print "misses_" . $labels[$i] . ".type DERIVE\n";
		} elsif ($mode eq 'items') {
			print "items_" . $labels[$i] . ".label items (" . $labels[$i] . ")\n";
			print "items_" . $labels[$i] . ".info Number of cached items (" . $labels[$i] . ")\n";
			print "items_" . $labels[$i] . ".min 0\n";
		} elsif ($mode eq 'requests') {
			print "gets_" . $labels[$i] . ".label gets (" . $labels[$i] . ")\n";
			print "gets_" . $labels[$i] . ".info Number of get requests (" . $labels[$i] . ")\n";
			print "gets_" . $labels[$i] . ".min 0\n";
			print "gets_" . $labels[$i] . ".type DERIVE\n";
			print "sets_" . $labels[$i] . ".label sets (" . $labels[$i] . ")\n";
			print "sets_" . $labels[$i] . ".info Number of set requests (" . $labels[$i] . ")\n";
			print "sets_" . $labels[$i] . ".min 0\n";
			print "sets_" . $labels[$i] . ".type DERIVE\n";
		} elsif ($mode eq 'traffic') {
			print "up_" . $labels[$i] . ".label bits in (" . $labels[$i] . ")\n";
			print "up_" . $labels[$i] . ".info Traffic received by memcached (" . $labels[$i] . ")\n";
			print "up_" . $labels[$i] . ".min 0\n";
			print "up_" . $labels[$i] . ".cdef up_" . $labels[$i] . ",8,*\n";
			print "up_" . $labels[$i] . ".type COUNTER\n";
			print "down_" . $labels[$i] . ".label bits out (" . $labels[$i] . ")\n";
			print "down_" . $labels[$i] . ".info Traffic sent by memcached (" . $labels[$i] . ")\n";
			print "down_" . $labels[$i] . ".min 0\n";
			print "down_" . $labels[$i] . ".cdef down_" . $labels[$i] . ",8,*\n";
			print "down_" . $labels[$i] . ".type COUNTER\n";
		}
	}
	exit 0;
}



# Output for data queries
for (my $i = 0; $i < @addresses; $i++) {
	my $memd = new Cache::Memcached { 'servers' => [$addresses[$i]] };
	my $memstats = $memd->stats(['misc']);

	if ($mode eq 'bytes') {
		print "bytes_" . $labels[$i] . ".value " .
	    	$memstats->{hosts}->{$addresses[$i]}->{misc}->{bytes} . "\n";
		print "maxbytes_" . $labels[$i] . ".value " .
			$memstats->{hosts}->{$addresses[$i]}->{misc}->{limit_maxbytes} . "\n";
	} elsif ($mode eq 'hits') {
		print "hits_" . $labels[$i] . ".value " .
			$memstats->{hosts}->{$addresses[$i]}->{misc}->{get_hits} . "\n";
		print "misses_" . $labels[$i] . ".value " .
			$memstats->{hosts}->{$addresses[$i]}->{misc}->{get_misses} . "\n";
	} elsif ($mode eq 'items') {
		print "items_" . $labels[$i] . ".value " .
			$memstats->{hosts}->{$addresses[$i]}->{misc}->{curr_items} . "\n";
	} elsif ($mode eq 'requests') {
		print "gets_" . $labels[$i] . ".value " . $memstats->{hosts}->{$addresses[$i]}->{misc}->{cmd_get} . "\n";
		print "sets_" . $labels[$i] . ".value " . $memstats->{hosts}->{$addresses[$i]}->{misc}->{cmd_set} . "\n";
	} elsif ($mode eq 'traffic') {
		print "up_" . $labels[$i] . ".value " . $memstats->{hosts}->{$addresses[$i]}->{misc}->{bytes_read} . "\n";
		print "down_" . $labels[$i] . ".value " . $memstats->{hosts}->{$addresses[$i]}->{misc}->{bytes_written} . "\n";
	}

}

