]> git.openstreetmap.org Git - chef.git/blob - cookbooks/munin/files/default/plugins/memcached_multi_
Merge pull request #109 from zerebubuth/munin-dump
[chef.git] / cookbooks / munin / files / default / plugins / memcached_multi_
1 #!/usr/bin/perl
2 #
3
4 =head1 MEMCACHED MULTI
5
6 Memcached Multi - A Plugin to monitor Memcached Servers (Multigraph)
7
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.
12
13 =head1 MUNIN NODE CONFIGURATION
14
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
17
18 [memcached_multi_*]
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*
24
25 =head2 MUNIN NODE ENVIRONMENT CONFIGURATION EXPLANATION
26
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.
32
33 =head2 BASIC TROUBLESHOOTING
34
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.
37
38 =head2 PLUGIN INFORMATION
39
40 Available Graphs contained in this Plugin
41
42 bytes => This graphs the current network traffic in and out
43
44 commands => I<MULTIGRAPH> This graphs the current commands being issued to the memcache machine.
45                                 B<Multigraph breaks this down to per slab.>
46
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.
49
50 evictions => I<MULTIGRAPH> This graphs the current evictions on the node.
51                                 B<Multigraph breaks this down to per slab.>
52
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.>
55
56 memory => I<MULTIGRAPH> This graphs the current and max memory allocation.
57                                 B<Multigraph breaks this down to per slab.>
58
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.>
62
63 =head1 ADDITIONAL INFORMATION
64
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.
68
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>.
71
72 The B<Timescale> variable formats certain graphs based on the following guidelines.
73  1 => Seconds
74  2 => Minutes
75  3 => Hours  B<*Default*>
76  4 => Days
77
78 =head1 ACKNOWLEDGEMENTS
79
80 Thanks to dormando for putting up with me ;)
81
82 =head1 AUTHOR
83
84 Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin >
85
86 =head1 LICENSE
87
88 GPLv2
89
90 =head1 MAGIC MARKERS
91
92 #%# family=auto
93 #%# capabilities=autoconf suggest
94
95 =cut
96
97 use strict;
98 use warnings;
99 use IO::Socket;
100 use Munin::Plugin;
101 use File::Basename;
102
103 if ( basename($0) !~ /(?:([^\/]+)_)?memcached_multi_/ ) {
104     print
105 "This script needs to be named (prefix_)?memcached_multi_ to run properly.\n";
106     exit 1;
107 }
108
109 # tell munin about our multigraph capabilities
110 need_multigraph();
111
112 =head1 Variable Declarations
113
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.
118
119 =cut
120
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;
124 my $connection;
125
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;
130
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;
135
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";
141
142 # This hash contains the information contained in two memcache commands
143 # stats and stats settings.
144 my %stats;
145
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
148 my %items;
149
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
153 my %chnks;
154
155 # Variable for setting up a quick access map for plugin configurations / version adherence
156 my $globalmap;
157
158 =head2 Graph Declarations
159
160     This block of code builds up all of the graph info for all root / subgraphs.
161
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.
164     Format:
165
166     $graph{graph_name} => {
167         config => {
168             You'll find the main graph config stored here
169             { key => value },
170             { ... },
171         },
172         datasrc => [
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', ... },
176             { ... },
177         ],
178     }
179
180 =cut
181
182 my %graphs;
183
184 # main graph for memcached item count
185 $graphs{items} = {
186     config => {
187         args     => '--base 1000 --lower-limit 0',
188         vlabel   => 'Items in Memcached',
189         category => 'memcached global items',
190         title    => 'Items',
191         info     => 'Number of items in use by memcached',
192     },
193     datasrc => [
194         { name => 'curr_items', label => 'Current Items', min => '0' },
195         {
196             name  => 'total_items',
197             label => 'New Items',
198             min   => '0',
199             type  => 'DERIVE'
200         },
201     ],
202 };
203
204 # main graph for memcached memory usage
205 $graphs{memory} = {
206     config => {
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',
212     },
213     datasrc => [
214         {
215             name  => 'limit_maxbytes',
216             draw  => 'AREA',
217             label => 'Maximum Bytes Allocated',
218             min   => '0'
219         },
220         {
221             name  => 'bytes',
222             draw  => 'AREA',
223             label => 'Current Bytes Used',
224             min   => '0'
225         },
226     ],
227 };
228
229 # main graph for memcached network usage
230 $graphs{bytes} = {
231     config => {
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',
238     },
239     datasrc => [
240         {
241             name  => 'bytes_read',
242             type  => 'DERIVE',
243             label => 'Network Traffic coming in (-)',
244             graph => 'no',
245             cdef  => 'bytes_read,8,*',
246             min   => '0'
247         },
248         {
249             name     => 'bytes_written',
250             type     => 'DERIVE',
251             label    => 'Traffic in (-) / out (+)',
252             negative => 'bytes_read',
253             cdef     => 'bytes_written,8,*',
254             min      => '0'
255         },
256     ],
257 };
258
259 # graph for memcached connections
260 $graphs{conns} = {
261     config => {
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',
268     },
269     datasrc => [
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' },
273     ],
274 };
275
276 # main graph for memcached commands issued
277 $graphs{commands} = {
278     config => {
279         args     => '--base 1000 --lower-limit 0',
280         vlabel   => 'Commands per ${graph_period}',
281         category => 'memcached global commands',
282         title    => 'Commands',
283         info     => 'Number of commands being handled by memcached',
284     },
285     datasrc => [
286         {
287             name  => 'cmd_get',
288             type  => 'DERIVE',
289             label => 'Gets',
290             info  => 'Cumulative number of retrieval reqs',
291             min   => '0'
292         },
293         {
294             name  => 'cmd_set',
295             type  => 'DERIVE',
296             label => 'Sets',
297             info  => 'Cumulative number of storage reqs',
298             min   => '0'
299         },
300         {
301             name  => 'cmd_flush',
302             type  => 'DERIVE',
303             label => 'Flushes',
304             info  => 'Cumulative number of flush reqs',
305             min   => '0'
306         },
307         {
308             name  => 'cmd_touch',
309             type  => 'DERIVE',
310             label => 'Touches',
311             info  => 'Cumulative number of touch reqs',
312             min   => '0'
313         },
314         {
315             name  => 'get_hits',
316             type  => 'DERIVE',
317             label => 'Get Hits',
318             info  => 'Number of keys that were requested and found',
319             min   => '0'
320         },
321         {
322             name  => 'get_misses',
323             type  => 'DERIVE',
324             label => 'Get Misses',
325             info  => 'Number of keys there were requested and not found',
326             min   => '0'
327         },
328         {
329             name  => 'delete_hits',
330             type  => 'DERIVE',
331             label => 'Delete Hits',
332             info =>
333               'Number of delete requests that resulted in a deletion of a key',
334             min => '0'
335         },
336         {
337             name  => 'delete_misses',
338             type  => 'DERIVE',
339             label => 'Delete Misses',
340             info  => 'Number of delete requests for missing key',
341             min   => '0'
342         },
343         {
344             name  => 'incr_hits',
345             type  => 'DERIVE',
346             label => 'Increment Hits',
347             info  => 'Number of successful increment requests',
348             min   => '0'
349         },
350         {
351             name  => 'incr_misses',
352             type  => 'DERIVE',
353             label => 'Increment Misses',
354             info  => 'Number of unsuccessful increment requests',
355             min   => '0'
356         },
357         {
358             name  => 'decr_hits',
359             type  => 'DERIVE',
360             label => 'Decrement Hits',
361             info  => 'Number of successful decrement requests',
362             min   => '0'
363         },
364         {
365             name  => 'decr_misses',
366             type  => 'DERIVE',
367             label => 'Decrement Misses',
368             info  => 'Number of unsuccessful decrement requests',
369             min   => '0'
370         },
371         {
372             name  => 'cas_misses',
373             type  => 'DERIVE',
374             label => 'CAS Misses',
375             info  => 'Number of Compare and Swap requests against missing keys',
376             min   => '0'
377         },
378         {
379             name  => 'cas_hits',
380             type  => 'DERIVE',
381             label => 'CAS Hits',
382             info  => 'Number of successful Compare and Swap requests',
383             min   => '0'
384         },
385         {
386             name  => 'cas_badval',
387             type  => 'DERIVE',
388             label => 'CAS Badval',
389             info  => 'Number of unsuccessful Compare and Swap requests',
390             min   => '0'
391         },
392         {
393             name  => 'touch_hits',
394             type  => 'DERIVE',
395             label => 'Touch Hits',
396             info  => 'Number of successfully touched keys',
397             min   => '0'
398         },
399         {
400             name  => 'touch_misses',
401             type  => 'DERIVE',
402             label => 'Touch Misses',
403             info  => 'Number of unsuccessful touch keys',
404             min   => '0'
405         },
406     ],
407 };
408
409 # main graph for memcached eviction rates
410 $graphs{evictions} = {
411     config => {
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',
417     },
418     datasrc => [
419         {
420             name  => 'evictions',
421             type  => 'DERIVE',
422             label => 'Evictions',
423             info  => 'Cumulative Evictions Across All Slabs',
424             min   => '0'
425         },
426         {
427             name  => 'evicted_nonzero',
428             type  => 'DERIVE',
429             label => 'Evictions prior to Expire',
430             info => 'Cumulative Evictions forced to expire prior to expiration',
431             min  => '0'
432         },
433         {
434             name  => 'reclaimed',
435             type  => 'DERIVE',
436             label => 'Reclaimed Items',
437             info  => 'Cumulative Reclaimed Item Entries Across All Slabs',
438             min   => '0'
439         },
440     ],
441 };
442
443 # main graph for memcached eviction rates
444 $graphs{unfetched} = {
445     config => {
446         args     => '--base 1000 --lower-limit 0',
447         vlabel   => 'Unfetched Items per ${graph_period}',
448         category => 'memcached global unfetched',
449         title    => 'Unfetched Items',
450         info =>
451 'Number of items that were never touched get/incr/append/etc before X occured',
452     },
453     datasrc => [
454         {
455             name  => 'expired_unfetched',
456             type  => 'DERIVE',
457             label => 'Expired Unfetched',
458             min   => '0',
459             info =>
460 'Number of items that expired and never had get/incr/append/etc performed'
461         },
462         {
463             name  => 'evicted_unfetched',
464             type  => 'DERIVE',
465             label => 'Evictioned Unfetched',
466             min   => '0',
467             info =>
468 'Number of items that evicted and never had get/incr/append/etc performed'
469         },
470     ],
471 };
472
473 # subgraph for breaking memory info down by slab ( subgraph of memory )
474 $graphs{slabchnks} = {
475     config => {
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.',
481     },
482     datasrc => [
483         {
484             name  => 'total_chunks',
485             label => 'Total Chunks Available',
486             min   => '0'
487         },
488         { name => 'used_chunks', label => 'Total Chunks in Use', min => '0' },
489         {
490             name  => 'free_chunks',
491             label => 'Total Chunks Not in Use (Free)',
492             min   => '0'
493         },
494     ],
495 };
496
497 # subgraph for breaking commands down by slab ( subgraph of commands )
498 $graphs{slabhits} = {
499     config => {
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: ',
504         info =>
505           'This graph shows you the successful hit rate for this memory slab.',
506     },
507     datasrc => [
508         {
509             name  => 'get_hits',
510             label => 'Get Requests',
511             type  => 'DERIVE',
512             min   => '0'
513         },
514         {
515             name  => 'cmd_set',
516             label => 'Set Requests',
517             type  => 'DERIVE',
518             min   => '0'
519         },
520         {
521             name  => 'delete_hits',
522             label => 'Delete Requests',
523             type  => 'DERIVE',
524             min   => '0'
525         },
526         {
527             name  => 'incr_hits',
528             label => 'Increment Requests',
529             type  => 'DERIVE',
530             min   => '0'
531         },
532         {
533             name  => 'decr_hits',
534             label => 'Decrement Requests',
535             type  => 'DERIVE',
536             min   => '0'
537         },
538         {
539             name  => 'cas_hits',
540             label => 'Sucessful CAS Requests',
541             type  => 'DERIVE',
542             min   => '0'
543         },
544         {
545             name  => 'cas_badval',
546             label => 'UnSucessful CAS Requests',
547             type  => 'DERIVE',
548             min   => '0'
549         },
550         {
551             name  => 'touch_hits',
552             label => 'Touch Requests',
553             type  => 'DERIVE',
554             min   => '0'
555         },
556     ],
557 };
558
559 # subgraph for breaking evictions down by slab ( subgraph of evictions )
560 $graphs{slabevics} = {
561     config => {
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.',
567     },
568     datasrc => [
569         {
570             name  => 'evicted',
571             label => 'Total Evictions',
572             type  => 'DERIVE',
573             min   => '0',
574             info  => 'Items evicted from memory slab'
575         },
576         {
577             name  => 'evicted_nonzero',
578             type  => 'DERIVE',
579             label => 'Evictions from LRU Prior to Expire',
580             info  => 'Items evicted from memory slab before ttl expiration',
581             min   => '0'
582         },
583         {
584             name  => 'reclaimed',
585             type  => 'DERIVE',
586             label => 'Reclaimed Expired Items',
587             info =>
588               'Number of times an item was stored in expired memory slab space',
589             min => '0'
590         },
591     ],
592 };
593
594 # subgraph for showing the time between an item was last evicted and requested ( subgraph of evictions )
595 $graphs{slabevictime} = {
596     config => {
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: ',
601         info =>
602 'This graph shows you the time since we requested the last evicted item',
603     },
604     datasrc => [
605         {
606             name  => 'evicted_time',
607             label => 'Eviction Time (LEI)',
608             info  => 'Time Since Request for Last Evicted Item',
609             min   => '0'
610         },
611     ],
612 };
613
614 # subgraph for breaking items down by slab ( subgraph of items )
615 $graphs{slabitems} = {
616     config => {
617         args     => '--base 1000 --lower-limit 0',
618         vlabel   => 'Items per Slab',
619         category => 'memcached slab item count',
620         title    => 'Items in Slab: ',
621         info =>
622 'This graph shows you the number of items and reclaimed items per slab.',
623     },
624     datasrc => [
625         {
626             name  => 'number',
627             label => 'Items',
628             draw  => 'AREA',
629             info  => 'This is the amount of items stored in this slab',
630             min   => '0'
631         },
632     ],
633 };
634
635 # subgraph for showing the age of the eldest item stored in a slab ( subgraph of items )
636 $graphs{slabitemtime} = {
637     config => {
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',
643     },
644     datasrc =>
645       [ { name => 'age', label => 'Eldest Item\'s Age', min => '0' }, ],
646 };
647
648 # main graph for memcached eviction rates
649 $graphs{slabunfetched} = {
650     config => {
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: ',
655         info =>
656 'Number of items that were never touched get/incr/append/etc before X occured',
657     },
658     datasrc => [
659         {
660             name  => 'expired_unfetched',
661             type  => 'DERIVE',
662             label => 'Expired Unfetched',
663             min   => '0',
664             info =>
665 'Number of items that expired and never had get/incr/append/etc performed'
666         },
667         {
668             name  => 'evicted_unfetched',
669             type  => 'DERIVE',
670             label => 'Evictioned Unfetched',
671             min   => '0',
672             info =>
673 'Number of items that evicted and never had get/incr/append/etc performed'
674         },
675     ],
676 };
677
678 =head1 Munin Checks
679
680     These checks look for config / autoconf / suggest params
681
682 =head2 Config Check
683
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.
689
690 =cut
691
692 if ( defined $ARGV[0] && $ARGV[0] eq 'config' ) {
693
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 : '';
698     my $plugin = $2;
699     die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' )
700       unless $graphs{$plugin};
701
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
704     fetch_stats();
705     $globalmap = buildglobalmap();
706
707     # Now lets go ahead and print out our config.
708     do_config( $prefix, $plugin );
709     exit 0;
710 }
711
712 =head2 Autoconf Check
713
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.
718
719 =cut
720
721 if ( defined $ARGV[0] && $ARGV[0] eq 'autoconf' ) {
722
723     # Lets attempt to connect to memcached
724     my $s = get_conn();
725
726     # Lets verify that we did connect to memcached
727     if ( defined($s) ) {
728         print "yes\n";
729         exit 0;
730     }
731     else {
732         print "no (unable to connect to $connection)\n";
733         exit 0;
734     }
735 }
736
737 =head2 Suggest Check
738
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.
745
746 =cut
747
748 if ( defined $ARGV[0] && $ARGV[0] eq 'suggest' ) {
749
750     # Lets attempt to connect to memcached
751     my $s = get_conn();
752
753     # Lets check that we did connect to memcached
754     if ( defined($s) ) {
755         fetch_stats();
756         my @rootplugins =
757           ( 'bytes', 'conns', 'commands', 'evictions', 'items', 'memory' );
758         if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) {
759             push( @rootplugins, 'unfetched' );
760         }
761         foreach my $plugin (@rootplugins) {
762             print "$plugin\n";
763         }
764         exit 0;
765     }
766     else {
767         print "no (unable to connect to $connection)\n";
768         exit 0;
769     }
770 }
771
772 =head1 Output Subroutines
773
774     Output Subroutine calls to output data values
775
776 =head2 fetch_output
777
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.
781
782 =cut
783
784 # Well, no arguments were supplied that we know about, so lets print some data
785 $0 =~ /(?:([^\/]+)_)?memcached_multi_(.+)$/;
786 my $prefix = $1 ? $1 : '';
787 my $plugin = $2;
788 die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' )
789   unless $graphs{$plugin};
790 fetch_stats();
791 $globalmap = buildglobalmap();
792 fetch_output( $prefix, $plugin );
793
794 sub fetch_output {
795     my ( $prefix, $plugin ) = (@_);
796
797     # Now lets go ahead and print out our output.
798     my @subgraphs;
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 );
803         }
804         print_subrootmulti_output( $prefix, $plugin );
805         print_rootmulti_output( $prefix, $plugin );
806     }
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 );
811         }
812         print_subrootmulti_output( $prefix, $plugin );
813         print_rootmulti_output( $prefix, $plugin );
814     }
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 );
820         }
821         print_subrootmulti_output( $prefix, $plugin );
822         print_rootmulti_output( $prefix, $plugin );
823     }
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 );
828         }
829         print_subrootmulti_output( $prefix, $plugin );
830         print_rootmulti_output( $prefix, $plugin );
831     }
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 );
836         }
837         print_subrootmulti_output( $prefix, $plugin );
838         print_rootmulti_output( $prefix, $plugin );
839     }
840     else {
841         print_root_output($plugin);
842     }
843
844     return;
845 }
846
847 =head2 print_root_output
848
849     This subroutine prints out the return values for our non-multigraph root graphs.
850     It takes one parameter $plugin and returns when completed.
851
852         $plugin;    graph we are calling up to print data values for 
853
854     Example: print_root_output($plugin);
855
856 =cut
857
858 sub print_root_output {
859
860     # Lets get our plugin, set our graph reference and print out info for Munin
861     my ($plugin) = (@_);
862     my $graph = $graphs{$plugin};
863
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";
872             }
873         }
874     }
875     else {
876         my $output;
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};
882                 }
883                 elsif ( $value eq 'curr_conns' ) {
884                     $output = $stats{curr_connections};
885                 }
886                 elsif ( $value eq 'avg_conns' ) {
887                     $output = sprintf( "%02d",
888                         $stats{total_connections} / $stats{uptime} );
889                 }
890                 else {
891                     next;
892                 }
893                 print "$dsrc->{name}.value $output\n";
894             }
895         }
896     }
897     return;
898 }
899
900 =head2 print_rootmulti_output
901
902     This subroutine prints out the return values for our multigraph root graphs.
903     It takes one parameter $plugin and returns when completed.
904
905         $plugin;    root graph we are calling up to print data values for
906
907     Example: print_rootmulti_output($plugin);
908
909 =cut
910
911 sub print_rootmulti_output {
912
913     # Lets get our plugin, set our graph reference and print out info for Munin
914     my ( $prefix, $plugin ) = (@_);
915     my $graph = $graphs{$plugin};
916     if ($prefix) {
917         print "multigraph $prefix\_memcached_multi_$plugin\n";
918     }
919     else {
920         print "multigraph memcached_multi_$plugin\n";
921     }
922
923     # Lets print our data values with their appropriate name
924     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
925         my $output  = 0;
926         my %datasrc = %$dsrc;
927         while ( my ( $key, $value ) = each(%datasrc) ) {
928             next if ( $key ne 'name' );
929             next
930               if ( ( $plugin eq 'evictions' )
931                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
932               );
933             next
934               if ( ( $plugin eq 'commands' )
935                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
936             if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) )
937             {
938                 foreach my $slabid ( sort { $a <=> $b } keys %items ) {
939                     $output += $items{$slabid}->{evicted_nonzero};
940                 }
941             }
942             else {
943                 $output = $stats{$value};
944             }
945             print "$dsrc->{name}.value $output\n";
946         }
947     }
948     return;
949 }
950
951 =head2 print_subrootmulti_output
952
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.
956
957         $plugin;    root graph we are calling up to print data values for
958
959     Example: print_rootmulti_output($plugin);
960
961 =cut
962
963 sub print_subrootmulti_output {
964
965     # Lets get our plugin, set our graph reference and print out info for Munin
966     my ( $prefix, $plugin ) = (@_);
967     my $graph = $graphs{$plugin};
968     if ($prefix) {
969         if ( $plugin eq 'evictions' ) {
970             print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n";
971         }
972         else {
973             print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n";
974         }
975     }
976     else {
977         if ( $plugin eq 'evictions' ) {
978             print "multigraph memcached_multi_$plugin.global$plugin\n";
979         }
980         else {
981             print "multigraph memcached_multi_$plugin.$plugin\n";
982         }
983     }
984
985     # Lets print our data values with their appropriate name
986     foreach my $dsrc ( @{ $graph->{datasrc} } ) {
987         my $output  = 0;
988         my %datasrc = %$dsrc;
989         while ( my ( $key, $value ) = each(%datasrc) ) {
990             next if ( $key ne 'name' );
991             next
992               if ( ( $plugin eq 'evictions' )
993                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
994               );
995             next
996               if ( ( $plugin eq 'commands' )
997                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
998             if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) )
999             {
1000                 foreach my $slabid ( sort { $a <=> $b } keys %items ) {
1001                     $output += $items{$slabid}->{evicted_nonzero};
1002                 }
1003             }
1004             else {
1005                 $output = $stats{$value};
1006             }
1007             print "$dsrc->{name}.value $output\n";
1008         }
1009     }
1010     return;
1011 }
1012
1013 =head2 print_submulti_output
1014
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.
1017
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
1021
1022     Example: print_submulti_output($slabid,$plugin,@subgraphs);
1023
1024 =cut
1025
1026 sub print_submulti_output {
1027
1028     # Lets get our slabid, plugin, and subgraphs
1029     my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_);
1030     my $currslab = undef;
1031
1032     # Time to loop over our subgraphs array
1033     foreach my $sgraph (@subgraphs) {
1034
1035  # Lets set our graph reference for quick calling, and print some info for munin
1036         my $graph = $graphs{$sgraph};
1037         if ($prefix) {
1038             print
1039               "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n";
1040         }
1041         else {
1042             print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
1043         }
1044
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' ) )
1049         {
1050             $currslab = $items{$slabid};
1051         }
1052         elsif ( ( $plugin eq 'memory' ) || ( $plugin eq 'commands' ) ) {
1053             $currslab = $chnks{$slabid};
1054         }
1055         else {
1056             return;
1057         }
1058
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' );
1064                 next
1065                   if ( ( $sgraph eq 'slabevics' )
1066                     && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) )
1067                   );
1068                 next
1069                   if ( ( $plugin eq 'commands' )
1070                     && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) )
1071                   );
1072                 my $output = $currslab->{$value};
1073                 if (   ( $sgraph eq 'slabevictime' )
1074                     || ( $sgraph eq 'slabitemtime' ) )
1075                 {
1076                     $output = time_scale( 'data', $output );
1077                 }
1078                 print "$dsrc->{name}.value $output\n";
1079             }
1080         }
1081     }
1082     return;
1083 }
1084
1085 =head1 Config Subroutines
1086
1087     These subroutines handle the config portion of munin calls.
1088
1089 =head2 do_config
1090
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.
1093
1094         $plugin; root graph being called
1095
1096     Example: do_config($prefix, $plugin);
1097
1098 =cut
1099
1100 sub do_config {
1101     my ( $prefix, $plugin ) = (@_);
1102     my @subgraphs;
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 );
1107         }
1108         print_subrootmulti_config( $prefix, $plugin );
1109         print_rootmulti_config( $prefix, $plugin );
1110     }
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 );
1115         }
1116         print_subrootmulti_config( $prefix, $plugin );
1117         print_rootmulti_config( $prefix, $plugin );
1118     }
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 );
1124         }
1125         print_subrootmulti_config( $prefix, $plugin );
1126         print_rootmulti_config( $prefix, $plugin );
1127     }
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 );
1132         }
1133         print_subrootmulti_config( $prefix, $plugin );
1134         print_rootmulti_config( $prefix, $plugin );
1135     }
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 );
1140         }
1141         print_subrootmulti_config( $prefix, $plugin );
1142         print_rootmulti_config( $prefix, $plugin );
1143     }
1144     else {
1145         print_root_config( $prefix, $plugin );
1146     }
1147
1148     return;
1149 }
1150
1151 =head2 print_root_config
1152
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.
1155
1156         $prefix;    possible prefix used to allow multiple plugins per machine
1157         $plugin;    root graph used for multigraph call
1158
1159     Example:  print_root_config($prefix,$plugin);
1160
1161 =cut
1162
1163 sub print_root_config {
1164
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} };
1169
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' ) {
1173             if ($prefix) {
1174                 print "graph_$key " . ucfirst($prefix) . " $value\n";
1175             }
1176             else {
1177                 print "graph_$key $value\n";
1178             }
1179         }
1180         else {
1181             print "graph_$key $value\n";
1182         }
1183     }
1184
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";
1191         }
1192     }
1193     return;
1194 }
1195
1196 =head2 print_rootmulti_config
1197
1198     This subroutine prints out the config information for all of the multigraph root graphs.
1199     It takes one parameter, $plugin, returns when completed.
1200
1201         $prefix;    possible prefix used to allow multiple plugins per machine
1202         $plugin;    root graph used for multigraph call
1203
1204     Example:  print_rootmulti_config($prefix,$plugin);
1205
1206 =cut
1207
1208 sub print_rootmulti_config {
1209
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} };
1214
1215   # Lets tell munin about the graph we are referencing and print the main config
1216     if ($prefix) {
1217         print "multigraph $prefix\_memcached_multi_$plugin\n";
1218     }
1219     else {
1220         print "multigraph memcached_multi_$plugin\n";
1221     }
1222     while ( my ( $key, $value ) = each(%graphconf) ) {
1223         if ( $key eq 'category' ) {
1224             print "graph_$key memcached\n";
1225         }
1226         elsif ( $key eq 'title' ) {
1227             if ($prefix) {
1228                 print "graph_$key " . ucfirst($prefix) . " $value\n";
1229             }
1230             else {
1231                 print "graph_$key $value\n";
1232             }
1233         }
1234         else {
1235             print "graph_$key $value\n";
1236         }
1237     }
1238
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' );
1244             next
1245               if ( ( $plugin eq 'evictions' )
1246                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
1247               );
1248             next
1249               if ( ( $plugin eq 'commands' )
1250                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
1251             print "$dsrc->{name}.$key $value\n";
1252         }
1253     }
1254     return;
1255 }
1256
1257 =head2 print_subrootmulti_config
1258
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.
1262
1263         $prefix;    possible prefix used to allow multiple plugins per machine
1264         $plugin;    root graph used for multigraph call
1265
1266     Example:  print_rootmulti_config($prefix,$plugin);
1267
1268 =cut
1269
1270 sub print_subrootmulti_config {
1271
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} };
1276     if ($prefix) {
1277         if ( $plugin eq 'evictions' ) {
1278             print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n";
1279         }
1280         else {
1281             print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n";
1282         }
1283     }
1284     else {
1285         if ( $plugin eq 'evictions' ) {
1286             print "multigraph memcached_multi_$plugin.global$plugin\n";
1287         }
1288         else {
1289             print "multigraph memcached_multi_$plugin.$plugin\n";
1290         }
1291     }
1292
1293     while ( my ( $key, $value ) = each(%graphconf) ) {
1294         if ( $key eq 'title' ) {
1295             if ($prefix) {
1296                 print "graph_$key " . ucfirst($prefix) . " $value\n";
1297             }
1298             else {
1299                 print "graph_$key $value\n";
1300             }
1301         }
1302         else {
1303             print "graph_$key $value\n";
1304         }
1305     }
1306
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' );
1312             next
1313               if ( ( $plugin eq 'evictions' )
1314                 && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) )
1315               );
1316             next
1317               if ( ( $plugin eq 'commands' )
1318                 && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) );
1319             print "$dsrc->{name}.$key $value\n";
1320         }
1321     }
1322     return;
1323 }
1324
1325 =head2 print_submulti_config
1326
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.
1329
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
1334
1335     Example:  print_submulti_config($prefix,$slabid,$plugin,@subgraphs);
1336
1337 =cut
1338
1339 sub print_submulti_config {
1340
1341     # Lets get our slabid, plugin, and subgraphs
1342     my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_);
1343     my ( $slabitems, $slabchnks ) = undef;
1344
1345     # Time to loop over our subgraphs array
1346     foreach my $sgraph (@subgraphs) {
1347
1348         # Lets set our graph reference, and main graph config for easy handling
1349         my $graph     = $graphs{$sgraph};
1350         my %graphconf = %{ $graph->{config} };
1351
1352 # Lets tell munin which graph we are graphing, and what our main graph config info is
1353         if ($prefix) {
1354             print
1355               "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n";
1356         }
1357         else {
1358             print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
1359         }
1360         while ( my ( $key, $value ) = each(%graphconf) ) {
1361             if ( $key eq 'title' ) {
1362                 if ($prefix) {
1363                     print "graph_$key "
1364                       . ucfirst($prefix)
1365                       . " $value"
1366                       . "$slabid"
1367                       . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
1368                 }
1369                 else {
1370                     print "graph_$key $value"
1371                       . "$slabid"
1372                       . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
1373                 }
1374             }
1375             elsif (
1376                 ( $key eq 'vlabel' )
1377                 && (   ( $sgraph eq 'slabevictime' )
1378                     || ( $sgraph eq 'slabitemtime' ) )
1379               )
1380             {
1381                 $value = time_scale( 'config', $value );
1382                 print "graph_$key $value\n";
1383             }
1384             else {
1385                 print "graph_$key $value\n";
1386             }
1387         }
1388
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' );
1394                 next
1395                   if ( ( $sgraph eq 'slabevics' )
1396                     && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) )
1397                   );
1398                 next
1399                   if ( ( $plugin eq 'commands' )
1400                     && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) )
1401                   );
1402                 print "$dsrc->{name}.$key $value\n";
1403             }
1404         }
1405     }
1406     return;
1407 }
1408
1409 =head1 Misc Subroutines
1410
1411     These subroutines are misc ones, and are referenced inside of the code. They
1412     should never be called up by Munin.
1413
1414 =head2 get_conn
1415
1416     This subroutine returns a socket connection
1417
1418 =cut
1419
1420 sub get_conn {
1421     my $s = undef;
1422
1423     # check if we want to use sockets instead of tcp
1424     my ($sock) = ( $host =~ /unix:\/\/(.+)*$/ );
1425
1426     if ($sock) {
1427         $connection = "unix:\/\/$sock";
1428         $s = IO::Socket::UNIX->new( Peer => $sock );
1429     }
1430     else {
1431         $connection = "$host:$port";
1432         $s          = IO::Socket::INET->new(
1433             Proto    => "tcp",
1434             PeerAddr => $host,
1435             PeerPort => $port,
1436             Timeout  => 10,
1437         );
1438     }
1439     return $s;
1440 }
1441
1442 =head2 fetch_stats
1443
1444     This subroutine fetches the information from memcached and stores it into our
1445     hashes for later referencing throughout the graph. Returns when completed
1446
1447 =cut
1448
1449 sub fetch_stats {
1450
1451     # Lets try and connect to memcached
1452     my $s = get_conn();
1453
1454     # Die if we can't establish a connection to memcached
1455     die "Error: Unable to Connect to $connection\n" unless $s;
1456
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;
1463         }
1464         last if $line =~ /^END/;
1465     }
1466
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';
1474             }
1475             $stats{$skey} = $svalue;
1476         }
1477         last if $line =~ /^END/;
1478     }
1479
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;
1486         }
1487         last if $line =~ /^END/;
1488     }
1489
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;
1496         }
1497         last if $line =~ /^END/;
1498     }
1499 }
1500
1501 =head2 time_scale
1502
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.
1505
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
1508         a patch upstream.
1509
1510 =cut
1511
1512 sub time_scale {
1513
1514     # Lets get our config option and value to adjust
1515     my ( $configopt, $origvalue ) = (@_);
1516     my $value;
1517
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;
1523         }
1524         elsif ( $timescale == 2 ) {
1525             $value = "Minutes" . $origvalue;
1526         }
1527         elsif (( $timescale == 3 )
1528             || ( $timescale > 4 )
1529             || ( !defined($timescale) ) )
1530         {
1531             $value = "Hours" . $origvalue;
1532         }
1533         elsif ( $timescale == 4 ) {
1534             $value = "Days" . $origvalue;
1535         }
1536     }
1537     elsif ( $configopt eq 'data' ) {
1538         if ( $timescale == 1 ) {
1539             $value = sprintf( "%02.2f", $origvalue / 1 );
1540         }
1541         elsif ( $timescale == 2 ) {
1542             $value = sprintf( "%02.2f", $origvalue / 60 );
1543         }
1544         elsif (( $timescale == 3 )
1545             || ( $timescale > 4 )
1546             || ( !defined($timescale) ) )
1547         {
1548             $value = sprintf( "%02.2f", $origvalue / 3600 );
1549         }
1550         elsif ( $timescale == 4 ) {
1551             $value = sprintf( "%02.2f", $origvalue / 86400 );
1552         }
1553     }
1554     else {
1555         die "Unknown time_scale option given: either [config/data]\n";
1556     }
1557     return $value;
1558 }
1559
1560 =head2 buildglobalmap
1561
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
1564     slab command keys.
1565
1566 =cut
1567
1568 sub buildglobalmap {
1569     my $results;
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;
1577         }
1578         elsif ( $cmd eq "set" ) {
1579             $results->{globalcmds}->{cmd_set} = 1;
1580             $results->{slabcmds}->{cmd_set}   = 1;
1581         }
1582         elsif ( $cmd eq "delete" ) {
1583             $results->{globalcmds}->{delete_hits}   = 1;
1584             $results->{globalcmds}->{delete_misses} = 1;
1585             $results->{slabcmds}->{delete_hits}     = 1;
1586         }
1587         elsif ( $cmd eq "incr" ) {
1588             $results->{globalcmds}->{incr_hits}   = 1;
1589             $results->{globalcmds}->{incr_misses} = 1;
1590             $results->{slabcmds}->{incr_hits}     = 1;
1591         }
1592         elsif ( $cmd eq "decr" ) {
1593             $results->{globalcmds}->{decr_hits}   = 1;
1594             $results->{globalcmds}->{decr_misses} = 1;
1595             $results->{slabcmds}->{decr_hits}     = 1;
1596         }
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;
1603         }
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;
1610             }
1611         }
1612         elsif ( $cmd eq "flush" ) {
1613             if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) {
1614                 $results->{globalcmds}->{cmd_flush} = 1;
1615             }
1616         }
1617         else {
1618
1619             # Do absolutely nothing...
1620         }
1621     }
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;
1629     }
1630     return $results;
1631 }