Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / memcached / memcached_multi_ @ 09b88141

Historique | Voir | Annoter | Télécharger (43,4 ko)

1
#!/usr/bin/perl
2
#
3
=head1 MEMCACHED
4

    
5
Memcached Multi - A Plugin to monitor Memcached Servers (Multigraph)
6

    
7
 The common difference between this memcached Munin plugin and others that exists, is that
8
 others don't expose slab information from memcached, so you can better tune your memcached
9
 interaction / stability / etc. With this plugin we leverage multigraph capabilities in
10
 Munin to "hide" the slab information underneath of their parent graphs.
11

    
12
=head1 MUNIN NODE CONFIGURATION
13

    
14
The following configuration information can be overridden by placing environment definitions
15
 like shown here, in a file located in /etc/munin/plugin-conf.d
16

    
17
 [memcached_multi_*]
18
 env.host 127.0.0.1                             *default*
19
 env.port 11211                                 *default*
20
 env.timescale 3                                *default*
21
 env.cmds get set delete incr decr touch        *default*
22
 env.leitime -1                                 *default*
23

    
24
=head2 MUNIN NODE ENVIRONMENT CONFIGURATION EXPLANATION
25

    
26
 host = host we are going to monitor, this can be used to specify a unix socket.
27
 port = port we are connecting to, in order to gather stats
28
 timescale = what time frame do we want to format our graphs too
29
 cmds = cmd types to display on cmd graph, remove cmds you don't want displayed from list.
30
 leitime = setting this to 1 will re-enable slab eviction time graphs, see note below.
31

    
32
=head2 BASIC TROUBLESHOOTING
33

    
34
Please make sure you can telnet to your memcache servers and issue the
35
following commands: stats, stats settings, stats items and stats slabs.
36

    
37
=head2 PLUGIN INFORMATION
38

    
39
Available Graphs contained in this Plugin
40

    
41
bytes => This graphs the current network traffic in and out
42

    
43
commands => I<MULTIGRAPH> This graphs the current commands being issued to the memcache machine.
44
                                B<Multigraph breaks this down to per slab.>
45

    
46
conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec
47
            and is derived from total_conns / uptime.
48

    
49
evictions => I<MULTIGRAPH> This graphs the current evictions on the node.
50
                                B<Multigraph breaks this down to per slab.>
51

    
52
items => I<MULTIGRAPH> This graphs the current items and total items in the memcached node.
53
                                B<Multigraph breaks this down to per slab.>
54

    
55
memory => I<MULTIGRAPH> This graphs the current and max memory allocation.
56
                                B<Multigraph breaks this down to per slab.>
57

    
58
unfetched => I<MULTIGRAPH> This graphs the number of items that were never touched by a
59
                get/incr/append/etc before being evicted or expiring from the cache.
60
                                B<Multigraph breaks this down to per slab.>
61

    
62
=head1 ADDITIONAL INFORMATION
63

    
64
B<NOTE:> The slab plugin for LEI has been disabled since I believe the counters to be inaccurate,
65
    or perhaps not being updated as often I thought they would be. They can be re-enabled by
66
    setting an environment variable, see munin configuration section at the top.
67

    
68
You will find that some of the graphs have LEI on them. This was done in order to save room
69
on space for text and stands for B<Last Evicted Item>.
70

    
71
The B<Timescale> variable formats certain graphs based on the following guidelines.
72
 1 => Seconds
73
 2 => Minutes
74
 3 => Hours  B<*Default*>
75
 4 => Days
76

    
77
=head1 ACKNOWLEDGEMENTS
78

    
79
Thanks to dormando for putting up with me ;)
80

    
81
=head1 AUTHOR
82

    
83
Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin >
84

    
85
=head1 LICENSE
86

    
87
GPLv2
88

    
89
=head1 MAGIC MARKERS
90

    
91
 #%# family=auto
92
 #%# capabilities=autoconf suggest
93

    
94
=cut
95

    
96
use strict;
97
use warnings;
98
use IO::Socket;
99
use Munin::Plugin;
100
use File::Basename;
101

    
102
if (basename($0) !~ /^memcached_multi_/) {
103
    print "This script needs to be named memcached_multi_ and have symlinks which start the same.\n";
104
    exit 1;
105
}
106

    
107
# tell munin about our multigraph capabilities
108
need_multigraph();
109

    
110
=head1 Variable Declarations
111

    
112
    This section of code is to declare the variables used throughout the plugin
113
    Some of them are imported as environment variables from munin plugin conf.d
114
    file, others are hashes used for storing information that comes from the
115
    stats commands issued to memcached.
116

    
117
=cut
118

    
119
# lets import environment variables for the plugin or use the default
120
my $host = $ENV{host} || "127.0.0.1";
121
my $port = $ENV{port} || 11211;
122

    
123
# This gives us the ability to control the timescale our graphs are displaying.
124
# The default it set to divide by hours, if you want to get seconds set it to 1.
125
# Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days
126
my $timescale = $ENV{timescale} || 3;
127

    
128
# This gives us the ability to turn the Last Evicted Item time slab graph on.
129
# It was removed because I believe the counter / response to be broken but
130
# perhaps this was useful to someone.
131
my $leitime = $ENV{leitime} || -1;
132

    
133
# This gives us the ability to specify which commands we want to display on the
134
# command graph. Allowing finer control since some environments don't leverage
135
# every command possible in memcached.
136
# Options: get set delete incr decr case touch flush
137
my $commands = $ENV{cmds} || "get set delete incr decr touch";
138

    
139
# This hash contains the information contained in two memcache commands
140
# stats and stats settings.
141
my %stats;
142

    
143
# This gives us eviction rates and other hit stats per slab
144
# We track this so we can see if something was evicted earlier than necessary
145
my %items;
146

    
147
# This gives us the memory size and usage per slab
148
# We track this so we can see what slab is being used the most and has no free chunks
149
# so we can re-tune memcached to allocate more pages for the specified chunk size
150
my %chnks;
151

    
152
# Variable for setting up a quick access map for plugin configurations / version adherence
153
my $globalmap;
154

    
155
=head2 Graph Declarations
156

    
157
    This block of code builds up all of the graph info for all root / subgraphs.
158

    
159
    %graphs: is a container for all of the graph definition information. In here is where you'll
160
             find the configuration information for munin's graphing procedure.
161
    Format:
162

    
163
    $graph{graph_name} => {
164
        config => {
165
            You'll find the main graph config stored here
166
            { key => value },
167
            { ... },
168
        },
169
        datasrc => [
170
            Name: name given to data value
171
            Attr: Attribute and value, attribute must be valid plugin argument
172
            { name => 'Name', info => 'info about graph', ... },
173
            { ... },
174
        ],
175
    }
176

    
177
=cut
178

    
179
my %graphs;
180

    
181
# main graph for memcached item count
182
$graphs{items} = {
183
    config => {
184
        args => '--base 1000 --lower-limit 0',
185
        vlabel => 'Items in Memcached',
186
        category => 'memory',
187
        title => 'Items',
188
        info => 'Number of items in use by memcached',
189
    },
190
    datasrc => [
191
        { name => 'curr_items', label => 'Current Items', min => '0' },
192
        { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' },
193
    ],
194
};
195
# main graph for memcached memory usage
196
$graphs{memory} = {
197
    config => {
198
        args => '--base 1024 --lower-limit 0',
199
        vlabel => 'Bytes Used',
200
        category => 'memory',
201
        title => 'Memory Usage',
202
        info => 'Memory consumption of memcached',
203
    },
204
    datasrc => [
205
        { name => 'limit_maxbytes', draw => 'AREA', label => 'Maximum Bytes Allocated', min => '0' },
206
        { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' },
207
    ],
208
};
209
# main graph for memcached network usage
210
$graphs{bytes} = {
211
    config => {
212
        args => '--base 1000',
213
        vlabel => 'bits in (-) / out (+)',
214
        title => 'Network Traffic',
215
        category => 'memory',
216
        info => 'Network traffic in (-) / out (+) of the machine',
217
        order => 'bytes_read bytes_written',
218
    },
219
    datasrc => [
220
        { name => 'bytes_read', type => 'DERIVE', label => 'Network Traffic coming in (-)',
221
            graph => 'no', cdef => 'bytes_read,8,*', min => '0' },
222
        { name => 'bytes_written', type => 'DERIVE', label => 'Traffic in (-) / out (+)',
223
            negative => 'bytes_read', cdef => 'bytes_written,8,*', min => '0' },
224
    ],
225
};
226
# graph for memcached connections
227
$graphs{conns} = {
228
    config => {
229
        args => '--base 1000 --lower-limit 0',
230
        vlabel => 'Connections per ${graph_period}',
231
        category => 'memory',
232
        title => 'Connections',
233
        info => 'Number of connections being handled by memcached',
234
        order => 'max_conns curr_conns avg_conns',
235
    },
236
    datasrc => [
237
        { name => 'curr_conns', label => 'Current Connections', min => '0' },
238
        { name => 'max_conns', label => 'Max Connections', min => '0' },
239
        { name => 'avg_conns' , label => 'Avg Connections', min => '0' },
240
    ],
241
};
242
# main graph for memcached commands issued
243
$graphs{commands} = {
244
    config => {
245
        args => '--base 1000 --lower-limit 0',
246
        vlabel => 'Commands per ${graph_period}',
247
        category => 'memory',
248
        title => 'Commands',
249
        info => 'Number of commands being handled by memcached',
250
    },
251
    datasrc => [
252
        { name => 'cmd_get', type => 'DERIVE', label => 'Gets',
253
            info => 'Cumulative number of retrieval reqs', min => '0' },
254
        { name => 'cmd_set', type => 'DERIVE', label => 'Sets',
255
            info => 'Cumulative number of storage reqs', min => '0' },
256
        { name => 'cmd_flush', type => 'DERIVE', label => 'Flushes',
257
            info => 'Cumulative number of flush reqs', min => '0' },
258
        { name => 'cmd_touch', type => 'DERIVE', label => 'Touches',
259
            info => 'Cumulative number of touch reqs', min => '0' },
260
        { name => 'get_hits', type => 'DERIVE', label => 'Get Hits',
261
            info => 'Number of keys that were requested and found', min => '0' },
262
        { name => 'get_misses', type => 'DERIVE', label => 'Get Misses',
263
            info => 'Number of keys there were requested and not found', min => '0' },
264
        { name => 'delete_hits', type => 'DERIVE', label => 'Delete Hits',
265
            info => 'Number of delete requests that resulted in a deletion of a key', min => '0' },
266
        { name => 'delete_misses', type => 'DERIVE', label => 'Delete Misses',
267
            info => 'Number of delete requests for missing key', min => '0' },
268
        { name => 'incr_hits', type => 'DERIVE', label => 'Increment Hits',
269
            info => 'Number of successful increment requests', min => '0' },
270
        { name => 'incr_misses', type => 'DERIVE', label => 'Increment Misses',
271
            info => 'Number of unsuccessful increment requests', min => '0' },
272
        { name => 'decr_hits', type => 'DERIVE', label => 'Decrement Hits',
273
            info => 'Number of successful decrement requests', min => '0' },
274
        { name => 'decr_misses', type => 'DERIVE', label => 'Decrement Misses',
275
            info => 'Number of unsuccessful decrement requests', min => '0' },
276
        { name => 'cas_misses', type => 'DERIVE', label => 'CAS Misses',
277
            info => 'Number of Compare and Swap requests against missing keys', min => '0' },
278
        { name => 'cas_hits', type => 'DERIVE', label => 'CAS Hits',
279
            info => 'Number of successful Compare and Swap requests', min => '0' },
280
        { name => 'cas_badval', type => 'DERIVE', label => 'CAS Badval',
281
            info => 'Number of unsuccessful Compare and Swap requests', min => '0' },
282
        { name => 'touch_hits', type => 'DERIVE', label => 'Touch Hits',
283
            info => 'Number of successfully touched keys', min => '0' },
284
        { name => 'touch_misses', type => 'DERIVE', label => 'Touch Misses',
285
            info => 'Number of unsuccessful touch keys', min => '0' },
286
    ],
287
};
288
# main graph for memcached eviction rates
289
$graphs{evictions} = {
290
    config => {
291
        args => '--base 1000 --lower-limit 0',
292
        vlabel => 'Evictions per ${graph_period}',
293
        category => 'memory',
294
        title => 'Evictions',
295
        info => 'Number of evictions per second',
296
    },
297
    datasrc => [
298
        { name => 'evictions', type => 'DERIVE', label => 'Evictions',
299
            info => 'Cumulative Evictions Across All Slabs', min => '0' },
300
        { name => 'evicted_nonzero', type => 'DERIVE', label => 'Evictions prior to Expire',
301
            info => 'Cumulative Evictions forced to expire prior to expiration', min => '0' },
302
        { name => 'reclaimed', type => 'DERIVE', label => 'Reclaimed Items',
303
            info => 'Cumulative Reclaimed Item Entries Across All Slabs', min => '0' },
304
    ],
305
};
306
# main graph for memcached eviction rates
307
$graphs{unfetched} = {
308
    config => {
309
        args => '--base 1000 --lower-limit 0',
310
        vlabel => 'Unfetched Items per ${graph_period}',
311
        category => 'memory',
312
        title => 'Unfetched Items',
313
        info => 'Number of items that were never touched get/incr/append/etc before X occurred',
314
    },
315
    datasrc => [
316
        { name => 'expired_unfetched', type => 'DERIVE', label => 'Expired Unfetched', min => '0',
317
            info => 'Number of items that expired and never had get/incr/append/etc performed'},
318
        { name => 'evicted_unfetched', type => 'DERIVE', label => 'Evictioned Unfetched', min => '0',
319
            info => 'Number of items that evicted and never had get/incr/append/etc performed'},
320
    ],
321
};
322
# subgraph for breaking memory info down by slab ( subgraph of memory )
323
$graphs{slabchnks} = {
324
    config => {
325
        args => '--base 1000 --lower-limit 0',
326
        vlabel => 'Available Chunks for this Slab',
327
        category => 'memory',
328
        title => 'Chunk Usage for Slab: ',
329
        info => 'This graph shows you the chunk usage for this memory slab.',
330
    },
331
    datasrc => [
332
        { name => 'total_chunks', label => 'Total Chunks Available', min => '0' },
333
        { name => 'used_chunks', label => 'Total Chunks in Use', min => '0' },
334
        { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' },
335
    ],
336
};
337
# subgraph for breaking commands down by slab ( subgraph of commands )
338
$graphs{slabhits} = {
339
    config => {
340
        args => '--base 1000 --lower-limit 0',
341
        vlabel => 'Hits per Slab per ${graph_period}',
342
        category => 'memory',
343
        title => 'Hits for Slab: ',
344
        info => 'This graph shows you the successful hit rate for this memory slab.',
345
    },
346
    datasrc => [
347
        { name => 'get_hits', label => 'Get Requests', type => 'DERIVE', min => '0' },
348
        { name => 'cmd_set', label => 'Set Requests', type => 'DERIVE', min => '0' },
349
        { name => 'delete_hits', label => 'Delete Requests', type => 'DERIVE', min => '0' },
350
        { name => 'incr_hits', label => 'Increment Requests', type => 'DERIVE', min => '0' },
351
        { name => 'decr_hits', label => 'Decrement Requests', type => 'DERIVE', min => '0' },
352
        { name => 'cas_hits', label => 'Successful CAS Requests', type => 'DERIVE', min => '0' },
353
        { name => 'cas_badval', label => 'UnSuccessful CAS Requests', type => 'DERIVE', min => '0' },
354
        { name => 'touch_hits', label => 'Touch Requests', type => 'DERIVE', min => '0' },
355
    ],
356
};
357
# subgraph for breaking evictions down by slab ( subgraph of evictions )
358
$graphs{slabevics} = {
359
    config => {
360
        args => '--base 1000 --lower-limit 0',
361
        vlabel => 'Evictions per Slab per ${graph_period}',
362
        category => 'memory',
363
        title => 'Evictions for Slab: ',
364
        info => 'This graph shows you the eviction rate for this memory slab.',
365
    },
366
    datasrc => [
367
        { name => 'evicted', label => 'Total Evictions', type => 'DERIVE', min => '0',
368
            info => 'Items evicted from memory slab' },
369
        { name => 'evicted_nonzero', type => 'DERIVE', label => 'Evictions from LRU Prior to Expire',
370
            info => 'Items evicted from memory slab before ttl expiration', min => '0' },
371
        { name => 'reclaimed', type => 'DERIVE', label => 'Reclaimed Expired Items',
372
            info => 'Number of times an item was stored in expired memory slab space', min => '0' },
373
    ],
374
};
375
# subgraph for showing the time between an item was last evicted and requested ( subgraph of evictions )
376
$graphs{slabevictime} = {
377
    config => {
378
        args => '--base 1000 --lower-limit 0',
379
        vlabel => ' since Request for LEI',
380
        category => 'memory',
381
        title => 'Eviction Request Time for Slab: ',
382
        info => 'This graph shows you the time since we requested the last evicted item',
383
    },
384
    datasrc => [
385
        { name => 'evicted_time', label => 'Eviction Time (LEI)',
386
            info => 'Time Since Request for Last Evicted Item', min => '0' },
387
    ],
388
};
389
# subgraph for breaking items down by slab ( subgraph of items )
390
$graphs{slabitems} = {
391
    config => {
392
        args => '--base 1000 --lower-limit 0',
393
        vlabel => 'Items per Slab',
394
        category => 'memory',
395
        title => 'Items in Slab: ',
396
        info => 'This graph shows you the number of items and reclaimed items per slab.',
397
    },
398
    datasrc => [
399
        { name => 'number', label => 'Items', draw => 'AREA',
400
            info => 'This is the amount of items stored in this slab', min => '0' },
401
    ],
402
};
403
# subgraph for showing the age of the eldest item stored in a slab ( subgraph of items )
404
$graphs{slabitemtime} = {
405
    config => {
406
        args => '--base 1000 --lower-limit 0',
407
        vlabel => ' since item was stored',
408
        category => 'memory',
409
        title => 'Age of Eldest Item in Slab: ',
410
        info => 'This graph shows you the time of the eldest item in this slab',
411
    },
412
    datasrc => [
413
        { name => 'age', label => 'Eldest Item\'s Age', min => '0' },
414
    ],
415
};
416
# main graph for memcached eviction rates
417
$graphs{slabunfetched} = {
418
    config => {
419
        args => '--base 1000 --lower-limit 0',
420
        vlabel => 'Unfetched Items per ${graph_period}',
421
        category => 'memory',
422
        title => 'Unfetched Items in Slab: ',
423
        info => 'Number of items that were never touched get/incr/append/etc before X occurred',
424
    },
425
    datasrc => [
426
        { name => 'expired_unfetched', type => 'DERIVE', label => 'Expired Unfetched', min => '0',
427
            info => 'Number of items that expired and never had get/incr/append/etc performed'},
428
        { name => 'evicted_unfetched', type => 'DERIVE', label => 'Evictioned Unfetched', min => '0',
429
            info => 'Number of items that evicted and never had get/incr/append/etc performed'},
430
    ],
431
};
432

    
433
=head1 Munin Checks
434

    
435
    These checks look for config / autoconf / suggest params
436

    
437
=head2 Config Check
438

    
439
    This block of code looks at the argument that is possibly supplied,
440
    should it be config, it then checks to make sure the plugin
441
    specified exists, assuming it does, it will run the do_config
442
    subroutine for the plugin specified, otherwise it dies complaining
443
    about an unknown plugin.
444

    
445
=cut
446

    
447
if (defined $ARGV[0] && $ARGV[0] eq 'config') {
448
    # Lets get our plugin from the symlink being called up, we'll also verify its a valid
449
    # plugin that we have graph information for
450
    $0 =~ /memcached_multi_(.+)*/;
451
    my $plugin = $1;
452
    die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
453
    # We need to fetch the stats before we do any config, cause its needed for multigraph
454
    # subgraphs which use slab information for title / info per slab
455
    fetch_stats();
456
    $globalmap = buildglobalmap();
457
    # Now lets go ahead and print out our config.
458
    do_config($plugin);
459
    exit 0;
460
}
461

    
462
=head2 Autoconf Check
463

    
464
    This block of code looks at the argument that is possibly supplied,
465
    should it be autoconf, we will attempt to connect to the memcached
466
    service specified on the host:port, upon successful connection it
467
    prints yes, otherwise it prints no.
468

    
469
=cut
470

    
471
if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') {
472
    # Lets attempt to connect to memcached
473
    my $s = get_conn();
474
    # Lets verify that we did connect to memcached
475
    if (defined($s)) {
476
        print "yes\n";
477
        exit 0;
478
    } else {
479
        print "no (unable to connect to $host\[:$port\])\n";
480
        exit 0;
481
    }
482
}
483

    
484
=head2 Suggest Check
485

    
486
    This block of code looks at the argument that is possibly supplied,
487
    should it be suggest, we are going to print the possible plugins
488
    which can be specified. Note we only specify the root graphs for the
489
    multigraphs, since the rest of the subgraphs will appear "behind" the
490
    root graphs. It also attempts to connect to the memcached service to
491
    verify it is in fact running.
492

    
493
=cut
494

    
495
if (defined $ARGV[0] && $ARGV[0] eq 'suggest') {
496
    # Lets attempt to connect to memcached
497
    my $s = get_conn();
498
    # Lets check that we did connect to memcached
499
    if (defined($s)) {
500
        fetch_stats();
501
        my @rootplugins = ('bytes','conns','commands','evictions','items','memory');
502
        if ($stats{version} !~ /^1\.4\.[0-7]$/) {
503
            push(@rootplugins, 'unfetched');
504
        }
505
        foreach my $plugin (@rootplugins) {
506
            print "$plugin\n";
507
        }
508
        exit 0;
509
    } else {
510
        print "no (unable to connect to $host\[:$port\])\n";
511
        exit 0;
512
    }
513
}
514

    
515
=head1 Output Subroutines
516

    
517
    Output Subroutine calls to output data values
518

    
519
=head2 fetch_output
520

    
521
    This subroutine is the main call for printing data for the plugin.
522
    No parameters are taken as this is the default call if no arguments
523
    are supplied from the command line.
524

    
525
=cut
526

    
527
# Well, no arguments were supplied that we know about, so lets print some data
528
$0 =~ /memcached_multi_(.+)*/;
529
my $plugin = $1;
530
die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
531
fetch_stats();
532
$globalmap = buildglobalmap();
533
fetch_output($plugin);
534

    
535
sub fetch_output {
536
    my ($plugin) = (@_);
537
    # Now lets go ahead and print out our output.
538
    my @subgraphs;
539
    if ($plugin eq 'memory') {
540
        @subgraphs = ('slabchnks');
541
        foreach my $slabid(sort{$a <=> $b} keys %chnks) {
542
            print_submulti_output($slabid,$plugin,@subgraphs);
543
        }
544
        print_subrootmulti_output($plugin);
545
        print_rootmulti_output($plugin);
546
    } elsif ($plugin eq 'commands') {
547
        @subgraphs = ('slabhits');
548
        foreach my $slabid(sort{$a <=> $b} keys %chnks) {
549
            print_submulti_output($slabid,$plugin,@subgraphs);
550
        }
551
        print_subrootmulti_output($plugin);
552
        print_rootmulti_output($plugin);
553
    } elsif ($plugin eq 'evictions') {
554
        @subgraphs = ('slabevics');
555
        if ($leitime == 1) { push(@subgraphs, 'slabevictime'); }
556
        foreach my $slabid (sort{$a <=> $b} keys %items) {
557
            print_submulti_output($slabid,$plugin,@subgraphs);
558
        }
559
        print_subrootmulti_output($plugin);
560
        print_rootmulti_output($plugin);
561
    } elsif ($plugin eq 'items') {
562
        @subgraphs = ('slabitems','slabitemtime');
563
        foreach my $slabid (sort{$a <=> $b} keys %items) {
564
            print_submulti_output($slabid,$plugin,@subgraphs);
565
        }
566
        print_subrootmulti_output($plugin);
567
        print_rootmulti_output($plugin);
568
    } elsif ($plugin eq 'unfetched') {
569
        @subgraphs = ('slabunfetched');
570
        foreach my $slabid (sort{$a <=> $b} keys %items) {
571
            print_submulti_output($slabid,$plugin,@subgraphs);
572
        }
573
        print_subrootmulti_output($plugin);
574
        print_rootmulti_output($plugin);
575
    } else {
576
        print_root_output($plugin);
577
    }
578

    
579
    return;
580
}
581

    
582
=head2 print_root_output
583

    
584
    This subroutine prints out the return values for our non-multigraph root graphs.
585
    It takes one parameter $plugin and returns when completed.
586

    
587
        $plugin;    graph we are calling up to print data values for
588

    
589
    Example: print_root_output($plugin);
590

    
591
=cut
592

    
593
sub print_root_output {
594
    # Lets get our plugin, set our graph reference and print out info for Munin
595
    my ($plugin) = (@_);
596
    my $graph = $graphs{$plugin};
597
    # The conns plugin has some specific needs, looking for plugin type
598
    if ($plugin ne 'conns') {
599
        foreach my $dsrc (@{$graph->{datasrc}}) {
600
            my %datasrc = %$dsrc;
601
            while ( my ($key, $value) = each(%datasrc)) {
602
                next if ($key ne 'name');
603
                my $output = $stats{$value};
604
                print "$dsrc->{name}.value $output\n";
605
            }
606
        }
607
    } else {
608
        my $output;
609
        foreach my $dsrc (@{$graph->{datasrc}}) {
610
            my %datasrc = %$dsrc;
611
            while ( my ($key, $value) = each(%datasrc)) {
612
                if ($value eq 'max_conns') {
613
                    $output = $stats{maxconns};
614
                } elsif ($value eq 'curr_conns') {
615
                    $output = $stats{curr_connections};
616
                } elsif ($value eq 'avg_conns') {
617
                    $output = sprintf("%02d", $stats{total_connections} / $stats{uptime});
618
                } else {
619
                    next;
620
                }
621
                print "$dsrc->{name}.value $output\n";
622
            }
623
        }
624
    }
625
    return;
626
}
627

    
628
=head2 print_rootmulti_output
629

    
630
    This subroutine prints out the return values for our multigraph root graphs.
631
    It takes one parameter $plugin and returns when completed.
632

    
633
        $plugin;    root graph we are calling up to print data values for
634

    
635
    Example: print_rootmulti_output($plugin);
636

    
637
=cut
638

    
639
sub print_rootmulti_output {
640
    # Lets get our plugin, set our graph reference and print out info for Munin
641
    my ($plugin) = (@_);
642
    my $graph = $graphs{$plugin};
643
    print "multigraph memcached_multi_$plugin\n";
644
    # Lets print our data values with their appropriate name
645
    foreach my $dsrc (@{$graph->{datasrc}}) {
646
        my $output = 0;
647
        my %datasrc = %$dsrc;
648
        while ( my ($key, $value) = each(%datasrc)) {
649
            next if ($key ne 'name');
650
            next if (($plugin eq 'evictions') && (!exists($globalmap->{globalevics}->{$dsrc->{name}})));
651
            next if (($plugin eq 'commands') && (!exists($globalmap->{globalcmds}->{$dsrc->{name}})));
652
            if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) {
653
                foreach my $slabid (sort{$a <=> $b} keys %items) {
654
                    $output += $items{$slabid}->{evicted_nonzero};
655
                }
656
            } else {
657
                $output = $stats{$value};
658
            }
659
            print "$dsrc->{name}.value $output\n";
660
        }
661
    }
662
    return;
663
}
664

    
665
=head2 print_subrootmulti_output
666

    
667
    This subroutine prints out the return values for our multigraph root graphs, only this set of
668
    data will display on the subpage made by the multigraph capabilities of munin and the plugin.
669
    It takes one parameter $plugin and returns when completed.
670

    
671
        $plugin;    root graph we are calling up to print data values for
672

    
673
    Example: print_rootmulti_output($plugin);
674

    
675
=cut
676

    
677
sub print_subrootmulti_output {
678
    # Lets get our plugin, set our graph reference and print out info for Munin
679
    my ($plugin) = (@_);
680
    my $graph = $graphs{$plugin};
681
    if ($plugin eq 'evictions') {
682
        print "multigraph memcached_multi_$plugin.global$plugin\n";
683
    } else {
684
        print "multigraph memcached_multi_$plugin.$plugin\n";
685
    }
686
    # Lets print our data values with their appropriate name
687
    foreach my $dsrc (@{$graph->{datasrc}}) {
688
        my $output = 0;
689
        my %datasrc = %$dsrc;
690
        while ( my ($key, $value) = each(%datasrc)) {
691
            next if ($key ne 'name');
692
            next if (($plugin eq 'evictions') && (!exists($globalmap->{globalevics}->{$dsrc->{name}})));
693
            next if (($plugin eq 'commands') && (!exists($globalmap->{globalcmds}->{$dsrc->{name}})));
694
            if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) {
695
                foreach my $slabid (sort{$a <=> $b} keys %items) {
696
                    $output += $items{$slabid}->{evicted_nonzero};
697
                }
698
            } else {
699
                $output = $stats{$value};
700
            }
701
            print "$dsrc->{name}.value $output\n";
702
        }
703
    }
704
    return;
705
}
706

    
707
=head2 print_submulti_output
708

    
709
    This subroutine prints out the return values for our multigraph subgraphs. It takes
710
    three parameters $slabid, $plugin, @subgraphs and then rReturns when completed.
711

    
712
        $slabid;    slab id that we will use to grab info from and print out
713
        $plugin;    root graph being called, used for multigraph output and slab id
714
        @subgraphs; graphs we are actually trying to print data values for
715

    
716
    Example: print_submulti_output($slabid,$plugin,@subgraphs);
717

    
718
=cut
719

    
720
sub print_submulti_output {
721
    # Lets get our slabid, plugin, and subgraphs
722
    my ($slabid,$plugin,@subgraphs) = (@_);
723
    my $currslab = undef;
724
    # Time to loop over our subgraphs array
725
    foreach my $sgraph (@subgraphs) {
726
        # Lets set our graph reference for quick calling, and print some info for munin
727
        my $graph = $graphs{$sgraph};
728
        print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
729
        # Lets figure out what slab info we are trying to call up
730
        if (($plugin eq 'evictions') || ($plugin eq 'items') || ($plugin eq 'unfetched')) {
731
            $currslab = $items{$slabid};
732
        } elsif (($plugin eq 'memory') || ($plugin eq 'commands')) {
733
            $currslab = $chnks{$slabid};
734
        } else {
735
            return;
736
        }
737
        # Lets print our data values with their appropriate name
738
        foreach my $dsrc (@{$graph->{datasrc}}) {
739
            my %datasrc = %$dsrc;
740
            while ( my ($key, $value) = each(%datasrc)) {
741
                next if ($key ne 'name');
742
                next if (($sgraph eq 'slabevics') && (!exists($globalmap->{slabevics}->{$dsrc->{name}})));
743
                next if (($plugin eq 'commands') && (!exists($globalmap->{slabcmds}->{$dsrc->{name}})));
744
                my $output = $currslab->{$value};
745
                if (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime')) {
746
                    $output = time_scale('data',$output); ;
747
                }
748
                print "$dsrc->{name}.value $output\n";
749
            }
750
        }
751
    }
752
    return;
753
}
754

    
755
=head1 Config Subroutines
756

    
757
    These subroutines handle the config portion of munin calls.
758

    
759
=head2 do_config
760

    
761
    This is the main call issued assuming we call up config and plugin specified exists
762
    The subroutine takes one parameter $plugin, and returns when completed.
763

    
764
        $plugin; root graph being called
765

    
766
    Example: do_config($plugin);
767

    
768
=cut
769

    
770
sub do_config {
771
    my ($plugin) = (@_);
772
    my @subgraphs;
773
    if ($plugin eq 'memory') {
774
        @subgraphs = ('slabchnks');
775
        foreach my $slabid (sort{$a <=> $b} keys %chnks) {
776
            print_submulti_config($slabid,$plugin,@subgraphs);
777
        }
778
        print_subrootmulti_config($plugin);
779
        print_rootmulti_config($plugin);
780
    } elsif ($plugin eq 'commands') {
781
        @subgraphs = ('slabhits');
782
        foreach my $slabid (sort{$a <=> $b} keys %chnks) {
783
            print_submulti_config($slabid,$plugin,@subgraphs);
784
        }
785
        print_subrootmulti_config($plugin);
786
        print_rootmulti_config($plugin);
787
    } elsif ($plugin eq 'evictions') {
788
        @subgraphs = ('slabevics');
789
        if ($leitime == 1) { push(@subgraphs, 'slabevictime'); }
790
        foreach my $slabid (sort{$a <=> $b}  keys %items) {
791
            print_submulti_config($slabid,$plugin,@subgraphs);
792
        }
793
        print_subrootmulti_config($plugin);
794
        print_rootmulti_config($plugin);
795
    } elsif ($plugin eq 'items') {
796
        @subgraphs = ('slabitems','slabitemtime');
797
        foreach my $slabid (sort{$a <=> $b} keys %items) {
798
            print_submulti_config($slabid,$plugin,@subgraphs);
799
        }
800
        print_subrootmulti_config($plugin);
801
        print_rootmulti_config($plugin);
802
    } elsif ($plugin eq 'unfetched') {
803
        @subgraphs = ('slabunfetched');
804
        foreach my $slabid (sort{$a <=> $b}  keys %items) {
805
            print_submulti_config($slabid,$plugin,@subgraphs);
806
        }
807
        print_subrootmulti_config($plugin);
808
        print_rootmulti_config($plugin);
809
    } else {
810
        print_root_config($plugin);
811
    }
812

    
813
    return;
814
}
815

    
816
=head2 print_root_config
817

    
818
    This subroutine prints out the config information for all of the non-multigraph root graphs.
819
    It takes one parameter, $plugin, returns when completed.
820

    
821
        $plugin;    root graph used for multigraph call
822

    
823
    Example:  print_root_config($plugin);
824

    
825
=cut
826

    
827
sub print_root_config {
828
    # Lets get our plugin, set our graph reference and our graph config info
829
    my ($plugin) = (@_);
830
    my $graph = $graphs{$plugin};
831
    my %graphconf = %{$graph->{config}};
832
    # Lets tell munin about the graph we are referencing and print the main config
833
    while ( my ($key, $value) = each(%graphconf)) {
834
        print "graph_$key $value\n";
835
    }
836
    # Lets tell munin about our data values and how to treat them
837
    foreach my $dsrc (@{$graph->{datasrc}}) {
838
        my %datasrc = %$dsrc;
839
        while ( my ($key, $value) = each(%datasrc)) {
840
            next if ($key eq 'name');
841
            print "$dsrc->{name}.$key $value\n";
842
        }
843
    }
844
    return;
845
}
846

    
847
=head2 print_rootmulti_config
848

    
849
    This subroutine prints out the config information for all of the multigraph root graphs.
850
    It takes one parameter, $plugin, returns when completed.
851

    
852
        $plugin;    root graph used for multigraph call
853

    
854
    Example:  print_rootmulti_config($plugin);
855

    
856
=cut
857

    
858
sub print_rootmulti_config {
859
    # Lets get out plugin, set our graph reference and our graph config info
860
    my ($plugin) = (@_);
861
    my $graph = $graphs{$plugin};
862
    my %graphconf = %{$graph->{config}};
863
    # Lets tell munin about the graph we are referencing and print the main config
864
    print "multigraph memcached_multi_$plugin\n";
865
    while ( my ($key, $value) = each(%graphconf)) {
866
        if ($key eq 'category') { $value = 'memory' };
867
        print "graph_$key $value\n";
868
    }
869
    # Lets tell munin about our data values and how to treat them
870
    foreach my $dsrc (@{$graph->{datasrc}}) {
871
        my %datasrc = %$dsrc;
872
        while ( my ($key, $value) = each(%datasrc)) {
873
            next if ($key eq 'name');
874
            next if (($plugin eq 'evictions') && (!exists($globalmap->{globalevics}->{$dsrc->{name}})));
875
            next if (($plugin eq 'commands') && (!exists($globalmap->{globalcmds}->{$dsrc->{name}})));
876
            print "$dsrc->{name}.$key $value\n";
877
        }
878
    }
879
    return;
880
}
881

    
882
=head2 print_subrootmulti_config
883

    
884
    This subroutine prints out the config information for all of the multigraph root graph, only this
885
    graph of the data will display on the subpage made by the multigraph capabilities of munin and
886
    the plugin. It takes one parameter, $plugin, returns when completed.
887

    
888
        $plugin;    root graph used for multigraph call
889

    
890
    Example:  print_rootmulti_config($plugin);
891

    
892
=cut
893

    
894
sub print_subrootmulti_config {
895
    # Lets get out plugin, set our graph reference and our graph config info
896
    my ($plugin) = (@_);
897
    my $graph = $graphs{$plugin};
898
    my %graphconf = %{$graph->{config}};
899
    if ($plugin eq 'evictions') {
900
        print "multigraph memcached_multi_$plugin.global$plugin\n";
901
    } else {
902
        print "multigraph memcached_multi_$plugin.$plugin\n";
903
    }
904
    while ( my ($key, $value) = each(%graphconf)) {
905
        print "graph_$key $value\n";
906
    }
907
    # Lets tell munin about our data values and how to treat them
908
    foreach my $dsrc (@{$graph->{datasrc}}) {
909
        my %datasrc = %$dsrc;
910
        while ( my ($key, $value) = each(%datasrc)) {
911
            next if ($key eq 'name');
912
            next if (($plugin eq 'evictions') && (!exists($globalmap->{globalevics}->{$dsrc->{name}})));
913
            next if (($plugin eq 'commands') && (!exists($globalmap->{globalcmds}->{$dsrc->{name}})));
914
            print "$dsrc->{name}.$key $value\n";
915
        }
916
    }
917
    return;
918
}
919

    
920
=head2 print_submulti_config
921

    
922
    This subroutine prints out the config information for all of the multigraph subgraphs.
923
    It takes three parameters, $slabid, $plugin and @subgraphs, returns when completed.
924

    
925
        $slabid;    slab id that we will use to grab info from and print out
926
        $plugin;    root graph being called, used for multigraph output and slab id
927
        @subgraphs; graphs we are actually trying to print data values for
928

    
929
    Example:  print_submulti_config($slabid,$plugin,@subgraphs);
930

    
931
=cut
932

    
933
sub print_submulti_config {
934
    # Lets get our slabid, plugin, and subgraphs
935
    my ($slabid,$plugin,@subgraphs) = (@_);
936
    my ($slabitems,$slabchnks) = undef;
937
    # Time to loop over our subgraphs array
938
    foreach my $sgraph (@subgraphs) {
939
        # Lets set our graph reference, and main graph config for easy handling
940
        my $graph = $graphs{$sgraph};
941
        my %graphconf = %{$graph->{config}};
942
        # Lets tell munin which graph we are graphing, and what our main graph config info is
943
        print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n";
944
        while ( my ($key, $value) = each(%graphconf)) {
945
            if ($key eq 'title') {
946
                print "graph_$key $value" . "$slabid" . " ($chnks{$slabid}->{chunk_size} Bytes)\n";
947
            } elsif (($key eq 'vlabel') && (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime'))) {
948
                $value = time_scale('config',$value);
949
                print "graph_$key $value\n";
950
            } else {
951
                print "graph_$key $value\n";
952
            }
953
        }
954
        # Lets tell munin about our data values and how to treat them
955
        foreach my $dsrc (@{$graph->{datasrc}}) {
956
            my %datasrc = %$dsrc;
957
            while ( my ($key, $value) = each(%datasrc)) {
958
                next if ($key eq 'name');
959
                next if (($sgraph eq 'slabevics') && (!exists($globalmap->{slabevics}->{$dsrc->{name}})));
960
                next if (($plugin eq 'commands') && (!exists($globalmap->{slabcmds}->{$dsrc->{name}})));
961
                print "$dsrc->{name}.$key $value\n";
962
            }
963
        }
964
    }
965
    return;
966
}
967

    
968
=head1 Misc Subroutines
969

    
970
    These subroutines are misc ones, and are referenced inside of the code. They
971
    should never be called up by Munin.
972

    
973
=head2 get_conn
974

    
975
    This subroutine returns a socket connection
976

    
977
=cut
978

    
979
sub get_conn {
980
    my $s = undef;
981

    
982
    # check if we want to use sockets instead of tcp
983
    my ($sock) = ($host =~ /unix:\/\/(.+)*$/);
984

    
985
    if ($sock) {
986
       $s = IO::Socket::UNIX->new(
987
            Peer     => $sock
988
      );
989
    } else {
990
       $s = IO::Socket::INET->new(
991
            Proto    => "tcp",
992
            PeerAddr => $host,
993
            PeerPort => $port,
994
            Timeout  => 10,
995
       );
996
    }
997
    return $s;
998
}
999

    
1000
=head2 fetch_stats
1001

    
1002
    This subroutine fetches the information from memcached and stores it into our
1003
    hashes for later referencing throughout the graph. Returns when completed
1004

    
1005
=cut
1006

    
1007
sub fetch_stats {
1008
    # Lets try and connect to memcached
1009
    my $s = get_conn();
1010
    # Die if we can't establish a connection to memcached
1011
    die "Error: Unable to Connect to $host\[:$port\]\n" unless $s;
1012
    # Lets print the stats command and store the info from the output
1013
    print $s "stats\r\n";
1014
    while (my $line = <$s>) {
1015
        if ($line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/) {
1016
            my ($skey,$svalue) = ($1,$2);
1017
            $stats{$skey} = $svalue;
1018
        }
1019
        last if $line =~ /^END/;
1020
    }
1021
    # Lets print the stats settings command and store the info from the output
1022
    print $s "stats settings\r\n";
1023
    while (my $line = <$s>) {
1024
        if ($line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/) {
1025
            my ($skey,$svalue) = ($1,$2);
1026
            if ($skey eq 'evictions') {
1027
                $skey = 'evictions_active';
1028
            }
1029
            $stats{$skey} = $svalue;
1030
        }
1031
        last if $line =~ /^END/;
1032
    }
1033
    # Lets print the stats slabs command and store the info from the output
1034
    print $s "stats slabs\r\n";
1035
    while (my $line = <$s>) {
1036
        if ($line =~ /STAT\s(\d+):(.+)\s(\d+)/) {
1037
            my ($slabid,$slabkey,$slabvalue) = ($1,$2,$3);
1038
            $chnks{$slabid}->{$slabkey} = $slabvalue;
1039
        }
1040
        last if $line =~ /^END/;
1041
    }
1042
    # Lets print the stats items command and store the info from the output
1043
    print $s "stats items\r\n";
1044
    while (my $line = <$s>) {
1045
        if ($line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/) {
1046
            my ($itemid,$itemkey,$itemvalue) = ($1,$2,$3);
1047
            $items{$itemid}->{$itemkey} = $itemvalue;
1048
        }
1049
        last if $line =~ /^END/;
1050
    }
1051
}
1052

    
1053
=head2 time_scale
1054

    
1055
    This subroutine is here for me to adjust the timescale of the time graphs
1056
    for last evicted item and age of eldest item in cache.
1057

    
1058
        Please note, after long usage I have noticed these counters may not
1059
        be accurate, I believe the developers are aware and have submitted
1060
        a patch upstream.
1061

    
1062
=cut
1063

    
1064
sub time_scale {
1065
    # Lets get our config option and value to adjust
1066
    my ($configopt,$origvalue) = (@_);
1067
    my $value;
1068
    # If config is defined, it returns the config info for time scale
1069
    # If data is defined, it returns the original value after its been adjusted
1070
    if ($configopt eq 'config') {
1071
        if ($timescale == 1) {
1072
            $value = "Seconds" . $origvalue;
1073
        } elsif ($timescale == 2) {
1074
            $value = "Minutes" . $origvalue;
1075
        } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) {
1076
            $value = "Hours" . $origvalue;
1077
        } elsif ($timescale == 4) {
1078
            $value = "Days" . $origvalue;
1079
        }
1080
    } elsif ($configopt eq 'data') {
1081
        if ($timescale == 1) {
1082
            $value = sprintf("%02.2f", $origvalue / 1);
1083
        } elsif ($timescale == 2) {
1084
            $value = sprintf("%02.2f", $origvalue / 60);
1085
        } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) {
1086
            $value = sprintf("%02.2f", $origvalue / 3600);
1087
        } elsif ($timescale == 4) {
1088
            $value = sprintf("%02.2f", $origvalue / 86400);
1089
        }
1090
    } else {
1091
        die "Unknown time_scale option given: either [config/data]\n";
1092
    }
1093
    return $value;
1094
}
1095

    
1096
=head2 buildglobalmap
1097

    
1098
    This subroutine looks at the specified commands inputted, and generates
1099
    a hashref containing two arrays, one for global command keys and one for
1100
    slab command keys.
1101

    
1102
=cut
1103

    
1104
sub buildglobalmap {
1105
    my $results;
1106
    my @cmds = split(' ', $commands);
1107
    foreach my $cmd ( @cmds ) {
1108
        if ($cmd eq "get") {
1109
            $results->{globalcmds}->{cmd_get} = 1;
1110
            $results->{globalcmds}->{get_hits} = 1;
1111
            $results->{globalcmds}->{get_misses} = 1;
1112
            $results->{slabcmds}->{get_hits} = 1;
1113
        } elsif ($cmd eq "set" ) {
1114
            $results->{globalcmds}->{cmd_set} = 1;
1115
            $results->{slabcmds}->{cmd_set} = 1;
1116
        } elsif ($cmd eq "delete" ) {
1117
            $results->{globalcmds}->{delete_hits} = 1;
1118
            $results->{globalcmds}->{delete_misses} = 1;
1119
            $results->{slabcmds}->{delete_hits} = 1;
1120
        } elsif ($cmd eq "incr" ) {
1121
            $results->{globalcmds}->{incr_hits} = 1;
1122
            $results->{globalcmds}->{incr_misses} = 1;
1123
            $results->{slabcmds}->{incr_hits} = 1;
1124
        } elsif ($cmd eq "decr" ) {
1125
            $results->{globalcmds}->{decr_hits} = 1;
1126
            $results->{globalcmds}->{decr_misses} = 1;
1127
            $results->{slabcmds}->{decr_hits} = 1;
1128
        } elsif ($cmd eq "cas") {
1129
            $results->{globalcmds}->{cas_hits} = 1;
1130
            $results->{globalcmds}->{cas_misses} = 1;
1131
            $results->{globalcmds}->{cas_badval} = 1;
1132
            $results->{slabcmds}->{cas_hits} = 1;
1133
            $results->{slabcmds}->{cas_badval} = 1;
1134
        } elsif ($cmd eq "touch") {
1135
            if ($stats{version} !~ /^1\.4\.[0-7]$/) {
1136
                $results->{globalcmds}->{cmd_touch} = 1;
1137
                $results->{globalcmds}->{touch_hits} = 1;
1138
                $results->{globalcmds}->{touch_misses} = 1;
1139
                $results->{slabcmds}->{touch_hits} = 1;
1140
            }
1141
        } elsif ($cmd eq "flush") {
1142
            if ($stats{version} !~ /^1\.4\.[0-7]$/) {
1143
                $results->{globalcmds}->{cmd_flush} = 1;
1144
            }
1145
        } else {
1146
            # Do absolutely nothing...
1147
        }
1148
    }
1149
    $results->{globalevics}->{evictions} = 1;
1150
    $results->{globalevics}->{evicted_nonzero}= 1;
1151
    $results->{slabevics}->{evicted} = 1;
1152
    $results->{slabevics}->{evicted_nonzero} = 1;
1153
    if ($stats{version} !~ /^1\.4\.[0-2]$/) {
1154
        $results->{globalevics}->{reclaimed} = 1;
1155
        $results->{slabevics}->{reclaimed} = 1;
1156
    }
1157
    return $results;
1158
}