#!@@PERL@@
# -*- cperl -*-
#
# Plugin to monitor memcache statistics
#
# This module has 3 seperate graph datasets:
# rates
# bytes
# counters
#
# For each of them, symlink the memcached_ script to memcached_FOO where foo is
# the datset name.
#
# Parameters supported:
#
#  config
#  autoconf
#  suggest
#
# Configurable variables
#
# host  Host of the memcache daemon
# port  Port of the memcache daemon
#
# Author: Robin H. Johnson <robbat2@gentoo.org>
# Slightly based on the original version by Joshua Thijssen
# <jthijssen@noxlogic.nl>
#
# Magic markers:
#%# family=contrib
#%# capabilities=autoconf
use strict;
my $ret = undef;
if (! eval "require Cache::Memcached;") {
	$ret = "Cache::Memcached not found";
}
my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "127.0.0.1";
my $PORT = exists $ENV{'port'} ? $ENV{'port'} : 11211;
if ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) {
	if ($ret) {
		print "no ($ret)\n";
	} else {
		use Net::Ping;
		my $p = Net::Ping->new();
		$p->port_number($PORT);
		if ($p->ping($HOST, 1)) {
			print "yes\n";
			exit 0;
		}
		print "no (cannot connect to $HOST on port $PORT)\n";
	}
	exit 0;
}

if (exists $ARGV[0] and $ARGV[0] eq "suggest") {
	print "bytes\n";
	print "counters\n";
	print "rates\n";
	exit 0;
}

if($ret) {
	# refuse to execute "config" or "fetch" due to a missing requirement
	print "memcached_ plugin fails to run: $ret\n";
	exit 1;
}

# We do everything by this array
my %all_vars = (
		rates => {
			master => {
				graph_title => 'Memcached - Commands',
				graph_args => '--base 1000',
				graph_vlabel => '/${graph_period}',
				graph_category => 'memcache',
			},
			memcache_cache_hits => {
				label => 'Cache hits',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				draw => 'LINE2',
				info => 'Number of cache hits',
				stat_group => 'misc',
				stat_name => 'get_hits'
			},
			memcache_cache_misses  => {
				label => 'Cache misses',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				draw => 'LINE2',
				info => 'Number of cache misses',
				stat_group => 'misc',
				stat_name => 'get_misses'
			},
			memcache_cmd_get => {
				label => 'GET requests',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				draw => 'LINE2',
				info => 'Number of GET commands seen',
				stat_group => 'misc',
				stat_name => 'cmd_get'
			},
			memcache_cmd_set => {
				label => 'SET requests',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				draw => 'LINE2',
				info => 'Number of SET commands seen',
				stat_group => 'misc',
				stat_name => 'cmd_set'
			},
			memcache_total_items => {
				label => 'New items*5',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				draw => 'LINE2',
				info => 'New items*5',
				cdef => 'memcache_total_items,5,*',
				stat_group => 'misc',
				stat_name => 'total_items'
			},
			memcache_total_connections => {
				label => 'New connections*100',
				type => 'DERIVE',
				min => '0',
				max => '5000',
				cdef => 'memcache_total_connections,100,*',
				draw => 'LINE2',
				info => 'New connections*100',
				stat_group => 'misc',
				stat_name => 'total_connections'
			},
		},
		bytes => {
			master => {
				graph_title => 'Memcached - Network traffic',
				graph_args => '--base 1000',
				graph_vlabel => 'bytes in(-)/out(+) per ${graph_period}',
				graph_category => 'memcache',
			},
			memcache_bytes_read => {
				label => 'Bytes read',
				type => 'COUNTER',
				draw => 'LINE2',
				max => '1000000',
				info => 'Bytes read from network',
				graph => 'no',
				stat_group => 'misc',
				stat_name => 'bytes_read'
			},
			memcache_bytes_written => {
				label => 'Bytes written',
				negative => 'memcache_bytes_read',
				type => 'COUNTER',
				max => '1000000',
				draw => 'LINE2',
				info => 'Bytes written to network',
				stat_group => 'misc',
				stat_name => 'bytes_written'
			},
		},
		counters => {
			master => {
				graph_title => 'Memcached - Current values',
				graph_args => '--base 1000',
				#graph_args => '--base 1000 --loga',
				graph_vlabel => 'Totals',
				graph_category => 'memcache',
				graph_scale => 'no',
			},
			memcache_curr_items => {
				label => 'Current items',
				type => 'GAUGE',
				min => '0',
				draw => 'LINE2',
				info => 'Number of items in cache',
				stat_group => 'misc',
				stat_name => 'curr_items'
			},
			memcache_curr_connections => {
				label => 'Current connections*100',
				type => 'GAUGE',
				min => '0',
				draw => 'LINE2',
				cdef => 'memcache_curr_connections,100,*',
				info => 'Number of connections*100',
				stat_group => 'misc',
				stat_name => 'curr_connections'
			},
			memcache_bytes_allocated => {
				label => 'Bytes allocated (KiB)',
				type => 'GAUGE',
				min => '0',
				draw => 'LINE2',
				cdef => 'memcache_bytes_allocated,1024,/',
				info => 'Bytes allocated (KiB)',
				stat_group => 'misc',
				stat_name => 'bytes'
			},
		}
		);

$0 =~ /memcached_(.+)*$/;
my $func = $1;
exit 2 unless defined $func;
my %vars = %{$all_vars{$func}};

#	   STAT rusage_user 3941.052868
#	   STAT rusage_system 18436.366246
#	   STAT connection_structures 1112
#	   STAT bytes 382985002
#	   STAT limit_maxbytes 536870912

if ( exists $ARGV[0] and $ARGV[0] eq "config" ) {
	my %v = %{$vars{'master'}};
	foreach my $k ( keys %v ) {
		print "$k ".$v{$k}."\n"
	}
	print 'graph_order ';
	foreach my $k ( sort(keys %vars) ) {
		if($k eq 'master') { next; }
		print $k." ";
	}
	print "\n";
	foreach my $k ( sort(keys %vars) ) {
		if($k eq 'master') { next; }
		my %v = %{$vars{$k}};
		foreach my $k2 (keys %v) {
			if($k2 eq 'stat_group' or $k2 eq 'stat_name') { next; }
			print "$k.$k2 ".$v{"$k2"}."\n";
		}
	}
	exit 0;
}
my $mc = new Cache::Memcached { 'servers' => [ "$HOST:$PORT" ] };
my $stats = $mc->stats ('misc');

foreach my $k ( sort(keys %vars) ) {
	if($k eq 'master') { next; }
	my %v = %{$vars{$k}};
	if($v{type} eq 'COMPUTE') { next; }
	my $sg = $v{stat_group};
	my $sn = $v{stat_name};
	my $value = $stats->{hosts}->{"$HOST:$PORT"}->{$sg}->{$sn};
	defined($value) or $value = 'U';
	print "$k.value ".$value."\n";
}
# vim:syntax=perl ts=4 sw=4:
