6 Memcached Multi - A Plugin to monitor Memcached Servers (Multigraph)
 
   8  The common difference between this memcached Munin plugin and others that exists, is that
 
   9  others don't expose slab information from memcached, so you can better tune your memcached
 
  10  interaction / stability / etc. With this plugin we leverage multigraph capabilities in
 
  11  Munin to "hide" the slab information underneath of their parent graphs.
 
  13 =head1 MUNIN NODE CONFIGURATION
 
  15 The following configuration information can be overridden by placing environment definitions
 
  16  like shown here, in a file located in /etc/munin/plugin-conf.d
 
  19  env.host 127.0.0.1                             *default*
 
  20  env.port 11211                                 *default*
 
  21  env.timescale 3                                *default*
 
  22  env.cmds get set delete incr decr touch        *default*
 
  23  env.leitime -1                                 *default*
 
  25 =head2 MUNIN NODE ENVIRONMENT CONFIGURATION EXPLANATION
 
  27  host = host we are going to monitor, this can be used to specify a unix socket.
 
  28  port = port we are connecting to, in order to gather stats
 
  29  timescale = what time frame do we want to format our graphs too
 
  30  cmds = cmd types to display on cmd graph, remove cmds you don't want displayed from list.
 
  31  leitime = setting this to 1 will re-enable slab eviction time graphs, see note below.
 
  33 =head2 BASIC TROUBLESHOOTING
 
  35 Please make sure you can telnet to your memcache servers and issue the
 
  36  following commands: stats, stats settings, stats items and stats slabs.
 
  38 =head2 PLUGIN INFORMATION
 
  40 Available Graphs contained in this Plugin
 
  42 bytes => This graphs the current network traffic in and out
 
  44 commands => I<MULTIGRAPH> This graphs the current commands being issued to the memcache machine.
 
  45                                 B<Multigraph breaks this down to per slab.>
 
  47 conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec
 
  48             and is derived from total_conns / uptime.
 
  50 evictions => I<MULTIGRAPH> This graphs the current evictions on the node.
 
  51                                 B<Multigraph breaks this down to per slab.>
 
  53 items => I<MULTIGRAPH> This graphs the current items and total items in the memcached node.
 
  54                                 B<Multigraph breaks this down to per slab.>
 
  56 memory => I<MULTIGRAPH> This graphs the current and max memory allocation.
 
  57                                 B<Multigraph breaks this down to per slab.>
 
  59 unfetched => I<MULTIGRAPH> This graphs the number of items that were never touched by a
 
  60                 get/incr/append/etc before being evicted or expiring from the cache.
 
  61                                 B<Multigraph breaks this down to per slab.>
 
  63 =head1 ADDITIONAL INFORMATION
 
  65 B<NOTE:> The slab plugin for LEI has been disabled since I believe the counters to be inaccurate,
 
  66     or perhaps not being updated as often I thought they would be. They can be re-enabled by
 
  67     setting an environment variable, see munin configuration section at the top.
 
  69 You will find that some of the graphs have LEI on them. This was done in order to save room
 
  70 on space for text and stands for B<Last Evicted Item>.
 
  72 The B<Timescale> variable formats certain graphs based on the following guidelines.
 
  75  3 => Hours  B<*Default*>
 
  78 =head1 ACKNOWLEDGEMENTS
 
  80 Thanks to dormando for putting up with me ;)
 
  84 Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin >
 
  93 #%# capabilities=autoconf suggest
 
 103 if ( basename($0) !~ /(?:([^\/]+)_)?memcached_multi_/ ) {
 
 105 "This script needs to be named (prefix_)?memcached_multi_ to run properly.\n";
 
 109 # tell munin about our multigraph capabilities
 
 112 =head1 Variable Declarations
 
 114     This section of code is to declare the variables used throughout the plugin
 
 115     Some of them are imported as environment variables from munin plugin conf.d
 
 116     file, others are hashes used for storing information that comes from the
 
 117     stats commands issued to memcached.
 
 121 # lets import environment variables for the plugin or use the default
 
 122 my $host = $ENV{host} || "127.0.0.1";
 
 123 my $port = $ENV{port} || 11211;
 
 126 # This gives us the ability to control the timescale our graphs are displaying.
 
 127 # The default it set to divide by hours, if you want to get seconds set it to 1.
 
 128 # Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days
 
 129 my $timescale = $ENV{timescale} || 3;
 
 131 # This gives us the ability to turn the Last Evicted Item time slab graph on.
 
 132 # It was removed because I believe the counter / response to be broken but
 
 133 # perhaps this was useful to someone.
 
 134 my $leitime = $ENV{leitime} || -1;
 
 136 # This gives us the ability to specify which commands we want to display on the
 
 137 # command graph. Allowing finer control since some environments don't leverage
 
 138 # every command possible in memcached.
 
 139 # Options: get set delete incr decr cas touch flush
 
 140 my $commands = $ENV{cmds} || "get set delete incr decr touch";
 
 142 # This hash contains the information contained in two memcache commands
 
 143 # stats and stats settings.
 
 146 # This gives us eviction rates and other hit stats per slab
 
 147 # We track this so we can see if something was evicted earlier than necessary
 
 150 # This gives us the memory size and usage per slab
 
 151 # We track this so we can see what slab is being used the most and has no free chunks
 
 152 # so we can re-tune memcached to allocate more pages for the specified chunk size
 
 155 # Variable for setting up a quick access map for plugin configurations / version adherence
 
 158 =head2 Graph Declarations
 
 160     This block of code builds up all of the graph info for all root / subgraphs.
 
 162     %graphs: is a container for all of the graph definition information. In here is where you'll
 
 163              find the configuration information for munin's graphing procedure.
 
 166     $graph{graph_name} => {
 
 168             You'll find the main graph config stored here
 
 173             Name: name given to data value
 
 174             Attr: Attribute and value, attribute must be valid plugin argument
 
 175             { name => 'Name', info => 'info about graph', ... },
 
 184 # main graph for memcached item count
 
 187         args     => '--base 1000 --lower-limit 0',
 
 188         vlabel   => 'Items in Memcached',
 
 189         category => 'memcached global items',
 
 191         info     => 'Number of items in use by memcached',
 
 194         { name => 'curr_items', label => 'Current Items', min => '0' },
 
 196             name  => 'total_items',
 
 197             label => 'New Items',
 
 204 # main graph for memcached memory usage
 
 207         args     => '--base 1024 --lower-limit 0',
 
 208         vlabel   => 'Bytes Used',
 
 209         category => 'memcached global memory',
 
 210         title    => 'Memory Usage',
 
 211         info     => 'Memory consumption of memcached',
 
 215             name  => 'limit_maxbytes',
 
 217             label => 'Maximum Bytes Allocated',
 
 223             label => 'Current Bytes Used',
 
 229 # main graph for memcached network usage
 
 232         args     => '--base 1000',
 
 233         vlabel   => 'bits in (-) / out (+)',
 
 234         title    => 'Network Traffic',
 
 235         category => 'memcached',
 
 236         info     => 'Network traffic in (-) / out (+) of the machine',
 
 237         order    => 'bytes_read bytes_written',
 
 241             name  => 'bytes_read',
 
 243             label => 'Network Traffic coming in (-)',
 
 245             cdef  => 'bytes_read,8,*',
 
 249             name     => 'bytes_written',
 
 251             label    => 'Traffic in (-) / out (+)',
 
 252             negative => 'bytes_read',
 
 253             cdef     => 'bytes_written,8,*',
 
 259 # graph for memcached connections
 
 262         args     => '--base 1000 --lower-limit 0',
 
 263         vlabel   => 'Connections per ${graph_period}',
 
 264         category => 'memcached',
 
 265         title    => 'Connections',
 
 266         info     => 'Number of connections being handled by memcached',
 
 267         order    => 'max_conns curr_conns avg_conns',
 
 270         { name => 'curr_conns', label => 'Current Connections', min => '0' },
 
 271         { name => 'max_conns',  label => 'Max Connections',     min => '0' },
 
 272         { name => 'avg_conns',  label => 'Avg Connections',     min => '0' },
 
 276 # main graph for memcached commands issued
 
 277 $graphs{commands} = {
 
 279         args     => '--base 1000 --lower-limit 0',
 
 280         vlabel   => 'Commands per ${graph_period}',
 
 281         category => 'memcached global commands',
 
 283         info     => 'Number of commands being handled by memcached',
 
 290             info  => 'Cumulative number of retrieval reqs',
 
 297             info  => 'Cumulative number of storage reqs',
 
 304             info  => 'Cumulative number of flush reqs',
 
 311             info  => 'Cumulative number of touch reqs',
 
 318             info  => 'Number of keys that were requested and found',
 
 322             name  => 'get_misses',
 
 324             label => 'Get Misses',
 
 325             info  => 'Number of keys there were requested and not found',
 
 329             name  => 'delete_hits',
 
 331             label => 'Delete Hits',
 
 333               'Number of delete requests that resulted in a deletion of a key',
 
 337             name  => 'delete_misses',
 
 339             label => 'Delete Misses',
 
 340             info  => 'Number of delete requests for missing key',
 
 346             label => 'Increment Hits',
 
 347             info  => 'Number of successful increment requests',
 
 351             name  => 'incr_misses',
 
 353             label => 'Increment Misses',
 
 354             info  => 'Number of unsuccessful increment requests',
 
 360             label => 'Decrement Hits',
 
 361             info  => 'Number of successful decrement requests',
 
 365             name  => 'decr_misses',
 
 367             label => 'Decrement Misses',
 
 368             info  => 'Number of unsuccessful decrement requests',
 
 372             name  => 'cas_misses',
 
 374             label => 'CAS Misses',
 
 375             info  => 'Number of Compare and Swap requests against missing keys',
 
 382             info  => 'Number of successful Compare and Swap requests',
 
 386             name  => 'cas_badval',
 
 388             label => 'CAS Badval',
 
 389             info  => 'Number of unsuccessful Compare and Swap requests',
 
 393             name  => 'touch_hits',
 
 395             label => 'Touch Hits',
 
 396             info  => 'Number of successfully touched keys',
 
 400             name  => 'touch_misses',
 
 402             label => 'Touch Misses',
 
 403             info  => 'Number of unsuccessful touch keys',
 
 409 # main graph for memcached eviction rates
 
 410 $graphs{evictions} = {
 
 412         args     => '--base 1000 --lower-limit 0',
 
 413         vlabel   => 'Evictions per ${graph_period}',
 
 414         category => 'memcached global evictions',
 
 415         title    => 'Evictions',
 
 416         info     => 'Number of evictions per second',
 
 422             label => 'Evictions',
 
 423             info  => 'Cumulative Evictions Across All Slabs',
 
 427             name  => 'evicted_nonzero',
 
 429             label => 'Evictions prior to Expire',
 
 430             info => 'Cumulative Evictions forced to expire prior to expiration',
 
 436             label => 'Reclaimed Items',
 
 437             info  => 'Cumulative Reclaimed Item Entries Across All Slabs',
 
 443 # main graph for memcached eviction rates
 
 444 $graphs{unfetched} = {
 
 446         args     => '--base 1000 --lower-limit 0',
 
 447         vlabel   => 'Unfetched Items per ${graph_period}',
 
 448         category => 'memcached global unfetched',
 
 449         title    => 'Unfetched Items',
 
 451 'Number of items that were never touched get/incr/append/etc before X occured',
 
 455             name  => 'expired_unfetched',
 
 457             label => 'Expired Unfetched',
 
 460 'Number of items that expired and never had get/incr/append/etc performed'
 
 463             name  => 'evicted_unfetched',
 
 465             label => 'Evictioned Unfetched',
 
 468 'Number of items that evicted and never had get/incr/append/etc performed'
 
 473 # subgraph for breaking memory info down by slab ( subgraph of memory )
 
 474 $graphs{slabchnks} = {
 
 476         args     => '--base 1000 --lower-limit 0',
 
 477         vlabel   => 'Available Chunks for this Slab',
 
 478         category => 'memcached slab chunk usage',
 
 479         title    => 'Chunk Usage for Slab: ',
 
 480         info => 'This graph shows you the chunk usage for this memory slab.',
 
 484             name  => 'total_chunks',
 
 485             label => 'Total Chunks Available',
 
 488         { name => 'used_chunks', label => 'Total Chunks in Use', min => '0' },
 
 490             name  => 'free_chunks',
 
 491             label => 'Total Chunks Not in Use (Free)',
 
 497 # subgraph for breaking commands down by slab ( subgraph of commands )
 
 498 $graphs{slabhits} = {
 
 500         args     => '--base 1000 --lower-limit 0',
 
 501         vlabel   => 'Hits per Slab per ${graph_period}',
 
 502         category => 'memcached slab commands',
 
 503         title    => 'Hits for Slab: ',
 
 505           'This graph shows you the successful hit rate for this memory slab.',
 
 510             label => 'Get Requests',
 
 516             label => 'Set Requests',
 
 521             name  => 'delete_hits',
 
 522             label => 'Delete Requests',
 
 528             label => 'Increment Requests',
 
 534             label => 'Decrement Requests',
 
 540             label => 'Sucessful CAS Requests',
 
 545             name  => 'cas_badval',
 
 546             label => 'UnSucessful CAS Requests',
 
 551             name  => 'touch_hits',
 
 552             label => 'Touch Requests',
 
 559 # subgraph for breaking evictions down by slab ( subgraph of evictions )
 
 560 $graphs{slabevics} = {
 
 562         args     => '--base 1000 --lower-limit 0',
 
 563         vlabel   => 'Evictions per Slab per ${graph_period}',
 
 564         category => 'memcached slab evictions',
 
 565         title    => 'Evictions for Slab: ',
 
 566         info => 'This graph shows you the eviction rate for this memory slab.',
 
 571             label => 'Total Evictions',
 
 574             info  => 'Items evicted from memory slab'
 
 577             name  => 'evicted_nonzero',
 
 579             label => 'Evictions from LRU Prior to Expire',
 
 580             info  => 'Items evicted from memory slab before ttl expiration',
 
 586             label => 'Reclaimed Expired Items',
 
 588               'Number of times an item was stored in expired memory slab space',
 
 594 # subgraph for showing the time between an item was last evicted and requested ( subgraph of evictions )
 
 595 $graphs{slabevictime} = {
 
 597         args     => '--base 1000 --lower-limit 0',
 
 598         vlabel   => ' since Request for LEI',
 
 599         category => 'memcached slab eviction time',
 
 600         title    => 'Eviction Request Time for Slab: ',
 
 602 'This graph shows you the time since we requested the last evicted item',
 
 606             name  => 'evicted_time',
 
 607             label => 'Eviction Time (LEI)',
 
 608             info  => 'Time Since Request for Last Evicted Item',
 
 614 # subgraph for breaking items down by slab ( subgraph of items )
 
 615 $graphs{slabitems} = {
 
 617         args     => '--base 1000 --lower-limit 0',
 
 618         vlabel   => 'Items per Slab',
 
 619         category => 'memcached slab item count',
 
 620         title    => 'Items in Slab: ',
 
 622 'This graph shows you the number of items and reclaimed items per slab.',
 
 629             info  => 'This is the amount of items stored in this slab',
 
 635 # subgraph for showing the age of the eldest item stored in a slab ( subgraph of items )
 
 636 $graphs{slabitemtime} = {
 
 638         args     => '--base 1000 --lower-limit 0',
 
 639         vlabel   => ' since item was stored',
 
 640         category => 'memcached slab item age',
 
 641         title    => 'Age of Eldest Item in Slab: ',
 
 642         info => 'This graph shows you the time of the eldest item in this slab',
 
 645       [ { name => 'age', label => 'Eldest Item\'s Age', min => '0' }, ],
 
 648 # main graph for memcached eviction rates
 
 649 $graphs{slabunfetched} = {
 
 651         args     => '--base 1000 --lower-limit 0',
 
 652         vlabel   => 'Unfetched Items per ${graph_period}',
 
 653         category => 'memcached slab unfetched',
 
 654         title    => 'Unfetched Items in Slab: ',
 
 656 'Number of items that were never touched get/incr/append/etc before X occured',
 
 660             name  => 'expired_unfetched',
 
 662             label => 'Expired Unfetched',
 
 665 'Number of items that expired and never had get/incr/append/etc performed'
 
 668             name  => 'evicted_unfetched',
 
 670             label => 'Evictioned Unfetched',
 
 673 'Number of items that evicted and never had get/incr/append/etc performed'
 
 680     These checks look for config / autoconf / suggest params
 
 684     This block of code looks at the argument that is possibly supplied,
 
 685     should it be config, it then checks to make sure the plugin 
 
 686     specified exists, assuming it does, it will run the do_config 
 
 687     subroutine for the plugin specified, otherwise it dies complaining
 
 688     about an unknown plugin.
 
 692 if ( defined $ARGV[0] && $ARGV[0] eq 'config' ) {
 
 694 # Lets get our plugin from the symlink being called up, we'll also verify its a valid
 
 695 # plugin that we have graph information for
 
 696     $0 =~ /(?:([^\/]+)_)?memcached_multi_(.+)$/;
 
 697     my $prefix = $1 ? $1 : '';
 
 699     die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' )
 
 700       unless $graphs{$plugin};
 
 702 # We need to fetch the stats before we do any config, cause its needed for multigraph
 
 703 # subgraphs which use slab information for title / info per slab
 
 705     $globalmap = buildglobalmap();
 
 707     # Now lets go ahead and print out our config.
 
 708     do_config( $prefix, $plugin );
 
 712 =head2 Autoconf Check
 
 714     This block of code looks at the argument that is possibly supplied,
 
 715     should it be autoconf, we will attempt to connect to the memcached
 
 716     service specified on the host:port, upon successful connection it
 
 717     prints yes, otherwise it prints no.
 
 721 if ( defined $ARGV[0] && $ARGV[0] eq 'autoconf' ) {
 
 723     # Lets attempt to connect to memcached
 
 726     # Lets verify that we did connect to memcached
 
 732         print "no (unable to connect to $connection)\n";
 
 739     This block of code looks at the argument that is possibly supplied,
 
 740     should it be suggest, we are going to print the possible plugins
 
 741     which can be specified. Note we only specify the root graphs for the
 
 742     multigraphs, since the rest of the subgraphs will appear "behind" the
 
 743     root graphs. It also attempts to connect to the memcached service to
 
 744     verify it is infact running.
 
 748 if ( defined $ARGV[0] && $ARGV[0] eq 'suggest' ) {
 
 750     # Lets attempt to connect to memcached
 
 753     # Lets check that we did connect to memcached
 
 757           ( 'bytes', 'conns', 'commands', 'evictions', 'items', 'memory' );
 
 758         if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) {
 
 759             push( @rootplugins, 'unfetched' );
 
 761         foreach my $plugin (@rootplugins) {
 
 767         print "no (unable to connect to $connection)\n";
 
 772 =head1 Output Subroutines
 
 774     Output Subroutine calls to output data values
 
 778     This subroutine is the main call for printing data for the plugin.
 
 779     No parameters are taken as this is the default call if no arguments
 
 780     are supplied from the command line.
 
 784 # Well, no arguments were supplied that we know about, so lets print some data
 
 785 $0 =~ /(?:([^\/]+)_)?memcached_multi_(.+)$/;
 
 786 my $prefix = $1 ? $1 : '';
 
 788 die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' )
 
 789   unless $graphs{$plugin};
 
 791 $globalmap = buildglobalmap();
 
 792 fetch_output( $prefix, $plugin );
 
 795     my ( $prefix, $plugin ) = (@_);
 
 797     # Now lets go ahead and print out our output.
 
 799     if ( $plugin eq 'memory' ) {
 
 800         @subgraphs = ('slabchnks');
 
 801         foreach my $slabid ( sort { $a <=> $b } keys %chnks ) {
 
 802             print_submulti_output( $prefix, $slabid, $plugin, @subgraphs );
 
 804         print_subrootmulti_output( $prefix, $plugin );
 
 805         print_rootmulti_output( $prefix, $plugin );
 
 807     elsif ( $plugin eq 'commands' ) {
 
 808         @subgraphs = ('slabhits');
 
 809         foreach my $slabid ( sort { $a <=> $b } keys %chnks ) {
 
 810             print_submulti_output( $prefix, $slabid, $plugin, @subgraphs );
 
 812         print_subrootmulti_output( $prefix, $plugin );
 
 813         print_rootmulti_output( $prefix, $plugin );
 
 815     elsif ( $plugin eq 'evictions' ) {
 
 816         @subgraphs = ('slabevics');
 
 817         if ( $leitime == 1 ) { push( @subgraphs, 'slabevictime' ); }
 
 818         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
 819             print_submulti_output( $prefix, $slabid, $plugin, @subgraphs );
 
 821         print_subrootmulti_output( $prefix, $plugin );
 
 822         print_rootmulti_output( $prefix, $plugin );
 
 824     elsif ( $plugin eq 'items' ) {
 
 825         @subgraphs = ( 'slabitems', 'slabitemtime' );
 
 826         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
 827             print_submulti_output( $prefix, $slabid, $plugin, @subgraphs );
 
 829         print_subrootmulti_output( $prefix, $plugin );
 
 830         print_rootmulti_output( $prefix, $plugin );
 
 832     elsif ( $plugin eq 'unfetched' ) {
 
 833         @subgraphs = ('slabunfetched');
 
 834         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
 835             print_submulti_output( $prefix, $slabid, $plugin, @subgraphs );
 
 837         print_subrootmulti_output( $prefix, $plugin );
 
 838         print_rootmulti_output( $prefix, $plugin );
 
 841         print_root_output($plugin);
 
 847 =head2 print_root_output
 
 849     This subroutine prints out the return values for our non-multigraph root graphs.
 
 850     It takes one parameter $plugin and returns when completed.
 
 852         $plugin;    graph we are calling up to print data values for 
 
 854     Example: print_root_output($plugin);
 
 858 sub print_root_output {
 
 860     # Lets get our plugin, set our graph reference and print out info for Munin
 
 862     my $graph = $graphs{$plugin};
 
 864     # The conns plugin has some specific needs, looking for plugin type
 
 865     if ( $plugin ne 'conns' ) {
 
 866         foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
 867             my %datasrc = %$dsrc;
 
 868             while ( my ( $key, $value ) = each(%datasrc) ) {
 
 869                 next if ( $key ne 'name' );
 
 870                 my $output = $stats{$value};
 
 871                 print "$dsrc->{name}.value $output\n";
 
 877         foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
 878             my %datasrc = %$dsrc;
 
 879             while ( my ( $key, $value ) = each(%datasrc) ) {
 
 880                 if ( $value eq 'max_conns' ) {
 
 881                     $output = $stats{maxconns};
 
 883                 elsif ( $value eq 'curr_conns' ) {
 
 884                     $output = $stats{curr_connections};
 
 886                 elsif ( $value eq 'avg_conns' ) {
 
 887                     $output = sprintf( "%02d",
 
 888                         $stats{total_connections} / $stats{uptime} );
 
 893                 print "$dsrc->{name}.value $output\n";
 
 900 =head2 print_rootmulti_output
 
 902     This subroutine prints out the return values for our multigraph root graphs.
 
 903     It takes one parameter $plugin and returns when completed.
 
 905         $plugin;    root graph we are calling up to print data values for
 
 907     Example: print_rootmulti_output($plugin);
 
 911 sub print_rootmulti_output {
 
 913     # Lets get our plugin, set our graph reference and print out info for Munin
 
 914     my ( $prefix, $plugin ) = (@_);
 
 915     my $graph = $graphs{$plugin};
 
 917         print "multigraph $prefix\_memcached_multi_$plugin\n";
 
 920         print "multigraph memcached_multi_$plugin\n";
 
 923     # Lets print our data values with their appropriate name
 
 924     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
 926         my %datasrc = %$dsrc;
 
 927         while ( my ( $key, $value ) = each(%datasrc) ) {
 
 928             next if ( $key ne 'name' );
 
 930               if ( ( $plugin eq 'evictions' )
 
 931                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
 
 934               if ( ( $plugin eq 'commands' )
 
 935                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
 
 936             if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) )
 
 938                 foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
 939                     $output += $items{$slabid}->{evicted_nonzero};
 
 943                 $output = $stats{$value};
 
 945             print "$dsrc->{name}.value $output\n";
 
 951 =head2 print_subrootmulti_output
 
 953     This subroutine prints out the return values for our multigraph root graphs, only this set of
 
 954     data will display on the subpage made by the multigraph capabilities of munin and the plugin.
 
 955     It takes one parameter $plugin and returns when completed.
 
 957         $plugin;    root graph we are calling up to print data values for
 
 959     Example: print_rootmulti_output($plugin);
 
 963 sub print_subrootmulti_output {
 
 965     # Lets get our plugin, set our graph reference and print out info for Munin
 
 966     my ( $prefix, $plugin ) = (@_);
 
 967     my $graph = $graphs{$plugin};
 
 969         if ( $plugin eq 'evictions' ) {
 
 970             print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n";
 
 973             print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n";
 
 977         if ( $plugin eq 'evictions' ) {
 
 978             print "multigraph memcached_multi_$plugin.global$plugin\n";
 
 981             print "multigraph memcached_multi_$plugin.$plugin\n";
 
 985     # Lets print our data values with their appropriate name
 
 986     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
 988         my %datasrc = %$dsrc;
 
 989         while ( my ( $key, $value ) = each(%datasrc) ) {
 
 990             next if ( $key ne 'name' );
 
 992               if ( ( $plugin eq 'evictions' )
 
 993                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
 
 996               if ( ( $plugin eq 'commands' )
 
 997                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
 
 998             if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) )
 
1000                 foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
1001                     $output += $items{$slabid}->{evicted_nonzero};
 
1005                 $output = $stats{$value};
 
1007             print "$dsrc->{name}.value $output\n";
 
1013 =head2 print_submulti_output
 
1015     This subroutine prints out the return values for our multigraph subgraphs. It takes
 
1016     three parameters $slabid, $plugin, @subgraphs and then rReturns when completed.
 
1018         $slabid;    slab id that we will use to grab info from and print out
 
1019         $plugin;    root graph being called, used for multigraph output and slab id
 
1020         @subgraphs; graphs we are actually trying to print data values for
 
1022     Example: print_submulti_output($slabid,$plugin,@subgraphs);
 
1026 sub print_submulti_output {
 
1028     # Lets get our slabid, plugin, and subgraphs
 
1029     my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_);
 
1030     my $currslab = undef;
 
1032     # Time to loop over our subgraphs array
 
1033     foreach my $sgraph (@subgraphs) {
 
1035  # Lets set our graph reference for quick calling, and print some info for munin
 
1036         my $graph = $graphs{$sgraph};
 
1039               "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n";
 
1042             print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
 
1045         # Lets figure out what slab info we are trying to call up
 
1046         if (   ( $plugin eq 'evictions' )
 
1047             || ( $plugin eq 'items' )
 
1048             || ( $plugin eq 'unfetched' ) )
 
1050             $currslab = $items{$slabid};
 
1052         elsif ( ( $plugin eq 'memory' ) || ( $plugin eq 'commands' ) ) {
 
1053             $currslab = $chnks{$slabid};
 
1059         # Lets print our data values with their appropriate name
 
1060         foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
1061             my %datasrc = %$dsrc;
 
1062             while ( my ( $key, $value ) = each(%datasrc) ) {
 
1063                 next if ( $key ne 'name' );
 
1065                   if ( ( $sgraph eq 'slabevics' )
 
1066                     && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) )
 
1069                   if ( ( $plugin eq 'commands' )
 
1070                     && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) )
 
1072                 my $output = $currslab->{$value};
 
1073                 if (   ( $sgraph eq 'slabevictime' )
 
1074                     || ( $sgraph eq 'slabitemtime' ) )
 
1076                     $output = time_scale( 'data', $output );
 
1078                 print "$dsrc->{name}.value $output\n";
 
1085 =head1 Config Subroutines
 
1087     These subroutines handle the config portion of munin calls.
 
1091     This is the main call issued assuming we call up config and plugin specified exists
 
1092     The subroutine takes one parameter $plugin, and returns when completed.
 
1094         $plugin; root graph being called
 
1096     Example: do_config($prefix, $plugin);
 
1101     my ( $prefix, $plugin ) = (@_);
 
1103     if ( $plugin eq 'memory' ) {
 
1104         @subgraphs = ('slabchnks');
 
1105         foreach my $slabid ( sort { $a <=> $b } keys %chnks ) {
 
1106             print_submulti_config( $prefix, $slabid, $plugin, @subgraphs );
 
1108         print_subrootmulti_config( $prefix, $plugin );
 
1109         print_rootmulti_config( $prefix, $plugin );
 
1111     elsif ( $plugin eq 'commands' ) {
 
1112         @subgraphs = ('slabhits');
 
1113         foreach my $slabid ( sort { $a <=> $b } keys %chnks ) {
 
1114             print_submulti_config( $prefix, $slabid, $plugin, @subgraphs );
 
1116         print_subrootmulti_config( $prefix, $plugin );
 
1117         print_rootmulti_config( $prefix, $plugin );
 
1119     elsif ( $plugin eq 'evictions' ) {
 
1120         @subgraphs = ('slabevics');
 
1121         if ( $leitime == 1 ) { push( @subgraphs, 'slabevictime' ); }
 
1122         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
1123             print_submulti_config( $prefix, $slabid, $plugin, @subgraphs );
 
1125         print_subrootmulti_config( $prefix, $plugin );
 
1126         print_rootmulti_config( $prefix, $plugin );
 
1128     elsif ( $plugin eq 'items' ) {
 
1129         @subgraphs = ( 'slabitems', 'slabitemtime' );
 
1130         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
1131             print_submulti_config( $prefix, $slabid, $plugin, @subgraphs );
 
1133         print_subrootmulti_config( $prefix, $plugin );
 
1134         print_rootmulti_config( $prefix, $plugin );
 
1136     elsif ( $plugin eq 'unfetched' ) {
 
1137         @subgraphs = ('slabunfetched');
 
1138         foreach my $slabid ( sort { $a <=> $b } keys %items ) {
 
1139             print_submulti_config( $prefix, $slabid, $plugin, @subgraphs );
 
1141         print_subrootmulti_config( $prefix, $plugin );
 
1142         print_rootmulti_config( $prefix, $plugin );
 
1145         print_root_config( $prefix, $plugin );
 
1151 =head2 print_root_config
 
1153     This subroutine prints out the config information for all of the non-multigraph root graphs.
 
1154     It takes one parameter, $plugin, returns when completed.
 
1156         $prefix;    possible prefix used to allow multiple plugins per machine
 
1157         $plugin;    root graph used for multigraph call
 
1159     Example:  print_root_config($prefix,$plugin);
 
1163 sub print_root_config {
 
1165     # Lets get our plugin, set our graph reference and our graph config info
 
1166     my ( $prefix, $plugin ) = (@_);
 
1167     my $graph     = $graphs{$plugin};
 
1168     my %graphconf = %{ $graph->{config} };
 
1170   # Lets tell munin about the graph we are referencing and print the main config
 
1171     while ( my ( $key, $value ) = each(%graphconf) ) {
 
1172         if ( $key eq 'title' ) {
 
1174                 print "graph_$key " . ucfirst($prefix) . " $value\n";
 
1177                 print "graph_$key $value\n";
 
1181             print "graph_$key $value\n";
 
1185     # Lets tell munin about our data values and how to treat them
 
1186     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
1187         my %datasrc = %$dsrc;
 
1188         while ( my ( $key, $value ) = each(%datasrc) ) {
 
1189             next if ( $key eq 'name' );
 
1190             print "$dsrc->{name}.$key $value\n";
 
1196 =head2 print_rootmulti_config
 
1198     This subroutine prints out the config information for all of the multigraph root graphs.
 
1199     It takes one parameter, $plugin, returns when completed.
 
1201         $prefix;    possible prefix used to allow multiple plugins per machine
 
1202         $plugin;    root graph used for multigraph call
 
1204     Example:  print_rootmulti_config($prefix,$plugin);
 
1208 sub print_rootmulti_config {
 
1210     # Lets get out plugin, set our graph reference and our graph config info
 
1211     my ( $prefix, $plugin ) = (@_);
 
1212     my $graph     = $graphs{$plugin};
 
1213     my %graphconf = %{ $graph->{config} };
 
1215   # Lets tell munin about the graph we are referencing and print the main config
 
1217         print "multigraph $prefix\_memcached_multi_$plugin\n";
 
1220         print "multigraph memcached_multi_$plugin\n";
 
1222     while ( my ( $key, $value ) = each(%graphconf) ) {
 
1223         if ( $key eq 'category' ) {
 
1224             print "graph_$key memcached\n";
 
1226         elsif ( $key eq 'title' ) {
 
1228                 print "graph_$key " . ucfirst($prefix) . " $value\n";
 
1231                 print "graph_$key $value\n";
 
1235             print "graph_$key $value\n";
 
1239     # Lets tell munin about our data values and how to treat them
 
1240     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
1241         my %datasrc = %$dsrc;
 
1242         while ( my ( $key, $value ) = each(%datasrc) ) {
 
1243             next if ( $key eq 'name' );
 
1245               if ( ( $plugin eq 'evictions' )
 
1246                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
 
1249               if ( ( $plugin eq 'commands' )
 
1250                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
 
1251             print "$dsrc->{name}.$key $value\n";
 
1257 =head2 print_subrootmulti_config
 
1259     This subroutine prints out the config information for all of the multigraph root graph, only this
 
1260     graph of the data will display on the subpage made by the multigraph capabilities of munin and
 
1261     the plugin. It takes one parameter, $plugin, returns when completed.
 
1263         $prefix;    possible prefix used to allow multiple plugins per machine
 
1264         $plugin;    root graph used for multigraph call
 
1266     Example:  print_rootmulti_config($prefix,$plugin);
 
1270 sub print_subrootmulti_config {
 
1272     # Lets get out plugin, set our graph reference and our graph config info
 
1273     my ( $prefix, $plugin ) = (@_);
 
1274     my $graph     = $graphs{$plugin};
 
1275     my %graphconf = %{ $graph->{config} };
 
1277         if ( $plugin eq 'evictions' ) {
 
1278             print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n";
 
1281             print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n";
 
1285         if ( $plugin eq 'evictions' ) {
 
1286             print "multigraph memcached_multi_$plugin.global$plugin\n";
 
1289             print "multigraph memcached_multi_$plugin.$plugin\n";
 
1293     while ( my ( $key, $value ) = each(%graphconf) ) {
 
1294         if ( $key eq 'title' ) {
 
1296                 print "graph_$key " . ucfirst($prefix) . " $value\n";
 
1299                 print "graph_$key $value\n";
 
1303             print "graph_$key $value\n";
 
1307     # Lets tell munin about our data values and how to treat them
 
1308     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
1309         my %datasrc = %$dsrc;
 
1310         while ( my ( $key, $value ) = each(%datasrc) ) {
 
1311             next if ( $key eq 'name' );
 
1313               if ( ( $plugin eq 'evictions' )
 
1314                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
 
1317               if ( ( $plugin eq 'commands' )
 
1318                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
 
1319             print "$dsrc->{name}.$key $value\n";
 
1325 =head2 print_submulti_config
 
1327     This subroutine prints out the config information for all of the multigraph subgraphs.
 
1328     It takes three parameters, $slabid, $plugin and @subgraphs, returns when completed.
 
1330         $prefix;    possible prefix used to allow multiple plugins per machine
 
1331         $slabid;    slab id that we will use to grab info from and print out
 
1332         $plugin;    root graph being called, used for multigraph output and slab id
 
1333         @subgraphs; graphs we are actually trying to print data values for
 
1335     Example:  print_submulti_config($prefix,$slabid,$plugin,@subgraphs);
 
1339 sub print_submulti_config {
 
1341     # Lets get our slabid, plugin, and subgraphs
 
1342     my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_);
 
1343     my ( $slabitems, $slabchnks ) = undef;
 
1345     # Time to loop over our subgraphs array
 
1346     foreach my $sgraph (@subgraphs) {
 
1348         # Lets set our graph reference, and main graph config for easy handling
 
1349         my $graph     = $graphs{$sgraph};
 
1350         my %graphconf = %{ $graph->{config} };
 
1352 # Lets tell munin which graph we are graphing, and what our main graph config info is
 
1355               "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n";
 
1358             print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
 
1360         while ( my ( $key, $value ) = each(%graphconf) ) {
 
1361             if ( $key eq 'title' ) {
 
1367                       . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
 
1370                     print "graph_$key $value"
 
1372                       . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
 
1376                 ( $key eq 'vlabel' )
 
1377                 && (   ( $sgraph eq 'slabevictime' )
 
1378                     || ( $sgraph eq 'slabitemtime' ) )
 
1381                 $value = time_scale( 'config', $value );
 
1382                 print "graph_$key $value\n";
 
1385                 print "graph_$key $value\n";
 
1389         # Lets tell munin about our data values and how to treat them
 
1390         foreach my $dsrc ( @{ $graph->{datasrc} } ) {
 
1391             my %datasrc = %$dsrc;
 
1392             while ( my ( $key, $value ) = each(%datasrc) ) {
 
1393                 next if ( $key eq 'name' );
 
1395                   if ( ( $sgraph eq 'slabevics' )
 
1396                     && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) )
 
1399                   if ( ( $plugin eq 'commands' )
 
1400                     && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) )
 
1402                 print "$dsrc->{name}.$key $value\n";
 
1409 =head1 Misc Subroutines
 
1411     These subroutines are misc ones, and are referenced inside of the code. They
 
1412     should never be called up by Munin.
 
1416     This subroutine returns a socket connection
 
1423     # check if we want to use sockets instead of tcp
 
1424     my ($sock) = ( $host =~ /unix:\/\/(.+)*$/ );
 
1427         $connection = "unix:\/\/$sock";
 
1428         $s = IO::Socket::UNIX->new( Peer => $sock );
 
1431         $connection = "$host:$port";
 
1432         $s          = IO::Socket::INET->new(
 
1444     This subroutine fetches the information from memcached and stores it into our
 
1445     hashes for later referencing throughout the graph. Returns when completed
 
1451     # Lets try and connect to memcached
 
1454     # Die if we can't establish a connection to memcached
 
1455     die "Error: Unable to Connect to $connection\n" unless $s;
 
1457     # Lets print the stats command and store the info from the output
 
1458     print $s "stats\r\n";
 
1459     while ( my $line = <$s> ) {
 
1460         if ( $line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/ ) {
 
1461             my ( $skey, $svalue ) = ( $1, $2 );
 
1462             $stats{$skey} = $svalue;
 
1464         last if $line =~ /^END/;
 
1467     # Lets print the stats settings command and store the info from the output
 
1468     print $s "stats settings\r\n";
 
1469     while ( my $line = <$s> ) {
 
1470         if ( $line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/ ) {
 
1471             my ( $skey, $svalue ) = ( $1, $2 );
 
1472             if ( $skey eq 'evictions' ) {
 
1473                 $skey = 'evictions_active';
 
1475             $stats{$skey} = $svalue;
 
1477         last if $line =~ /^END/;
 
1480     # Lets print the stats slabs command and store the info from the output
 
1481     print $s "stats slabs\r\n";
 
1482     while ( my $line = <$s> ) {
 
1483         if ( $line =~ /STAT\s(\d+):(.+)\s(\d+)/ ) {
 
1484             my ( $slabid, $slabkey, $slabvalue ) = ( $1, $2, $3 );
 
1485             $chnks{$slabid}->{$slabkey} = $slabvalue;
 
1487         last if $line =~ /^END/;
 
1490     # Lets print the stats items command and store the info from the output
 
1491     print $s "stats items\r\n";
 
1492     while ( my $line = <$s> ) {
 
1493         if ( $line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/ ) {
 
1494             my ( $itemid, $itemkey, $itemvalue ) = ( $1, $2, $3 );
 
1495             $items{$itemid}->{$itemkey} = $itemvalue;
 
1497         last if $line =~ /^END/;
 
1503     This subroutine is here for me to adjust the timescale of the time graphs
 
1504     for last evicted item and age of eldest item in cache.
 
1506         Please note, after long usage I have noticed these counters may not
 
1507         be accurate, I believe the developers are aware and have submitted
 
1514     # Lets get our config option and value to adjust
 
1515     my ( $configopt, $origvalue ) = (@_);
 
1518     # If config is defined, it returns the config info for time scale
 
1519     # If data is defined, it returns the original value after its been adjusted
 
1520     if ( $configopt eq 'config' ) {
 
1521         if ( $timescale == 1 ) {
 
1522             $value = "Seconds" . $origvalue;
 
1524         elsif ( $timescale == 2 ) {
 
1525             $value = "Minutes" . $origvalue;
 
1527         elsif (( $timescale == 3 )
 
1528             || ( $timescale > 4 )
 
1529             || ( !defined($timescale) ) )
 
1531             $value = "Hours" . $origvalue;
 
1533         elsif ( $timescale == 4 ) {
 
1534             $value = "Days" . $origvalue;
 
1537     elsif ( $configopt eq 'data' ) {
 
1538         if ( $timescale == 1 ) {
 
1539             $value = sprintf( "%02.2f", $origvalue / 1 );
 
1541         elsif ( $timescale == 2 ) {
 
1542             $value = sprintf( "%02.2f", $origvalue / 60 );
 
1544         elsif (( $timescale == 3 )
 
1545             || ( $timescale > 4 )
 
1546             || ( !defined($timescale) ) )
 
1548             $value = sprintf( "%02.2f", $origvalue / 3600 );
 
1550         elsif ( $timescale == 4 ) {
 
1551             $value = sprintf( "%02.2f", $origvalue / 86400 );
 
1555         die "Unknown time_scale option given: either [config/data]\n";
 
1560 =head2 buildglobalmap
 
1562     This subroutine looks at the specified commands inputted, and generates
 
1563     a hashref containing two arrays, one for global command keys and one for
 
1568 sub buildglobalmap {
 
1570     my @cmds = split( ' ', $commands );
 
1571     foreach my $cmd (@cmds) {
 
1572         if ( $cmd eq "get" ) {
 
1573             $results->{globalcmds}->{cmd_get}    = 1;
 
1574             $results->{globalcmds}->{get_hits}   = 1;
 
1575             $results->{globalcmds}->{get_misses} = 1;
 
1576             $results->{slabcmds}->{get_hits}     = 1;
 
1578         elsif ( $cmd eq "set" ) {
 
1579             $results->{globalcmds}->{cmd_set} = 1;
 
1580             $results->{slabcmds}->{cmd_set}   = 1;
 
1582         elsif ( $cmd eq "delete" ) {
 
1583             $results->{globalcmds}->{delete_hits}   = 1;
 
1584             $results->{globalcmds}->{delete_misses} = 1;
 
1585             $results->{slabcmds}->{delete_hits}     = 1;
 
1587         elsif ( $cmd eq "incr" ) {
 
1588             $results->{globalcmds}->{incr_hits}   = 1;
 
1589             $results->{globalcmds}->{incr_misses} = 1;
 
1590             $results->{slabcmds}->{incr_hits}     = 1;
 
1592         elsif ( $cmd eq "decr" ) {
 
1593             $results->{globalcmds}->{decr_hits}   = 1;
 
1594             $results->{globalcmds}->{decr_misses} = 1;
 
1595             $results->{slabcmds}->{decr_hits}     = 1;
 
1597         elsif ( $cmd eq "cas" ) {
 
1598             $results->{globalcmds}->{cas_hits}   = 1;
 
1599             $results->{globalcmds}->{cas_misses} = 1;
 
1600             $results->{globalcmds}->{cas_badval} = 1;
 
1601             $results->{slabcmds}->{cas_hits}     = 1;
 
1602             $results->{slabcmds}->{cas_badval}   = 1;
 
1604         elsif ( $cmd eq "touch" ) {
 
1605             if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) {
 
1606                 $results->{globalcmds}->{cmd_touch}    = 1;
 
1607                 $results->{globalcmds}->{touch_hits}   = 1;
 
1608                 $results->{globalcmds}->{touch_misses} = 1;
 
1609                 $results->{slabcmds}->{touch_hits}     = 1;
 
1612         elsif ( $cmd eq "flush" ) {
 
1613             if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) {
 
1614                 $results->{globalcmds}->{cmd_flush} = 1;
 
1619             # Do absolutely nothing...
 
1622     $results->{globalevics}->{evictions}       = 1;
 
1623     $results->{globalevics}->{evicted_nonzero} = 1;
 
1624     $results->{slabevics}->{evicted}           = 1;
 
1625     $results->{slabevics}->{evicted_nonzero}   = 1;
 
1626     if ( $stats{version} !~ /^1\.4\.[0-2]$/ ) {
 
1627         $results->{globalevics}->{reclaimed} = 1;
 
1628         $results->{slabevics}->{reclaimed}   = 1;