X-Git-Url: https://git.openstreetmap.org./chef.git/blobdiff_plain/7b9ec4b60ee39614d1d083d7220e76b07d2b275f..b9f9f8c373873c0629c9acdfd08aa9e8ebb20b6e:/cookbooks/munin/files/default/plugins/memcached_multi_ diff --git a/cookbooks/munin/files/default/plugins/memcached_multi_ b/cookbooks/munin/files/default/plugins/memcached_multi_ index a2028995b..12027269b 100755 --- a/cookbooks/munin/files/default/plugins/memcached_multi_ +++ b/cookbooks/munin/files/default/plugins/memcached_multi_ @@ -1,46 +1,71 @@ #!/usr/bin/perl # -=head1 NAME -Memcached - A Plugin to monitor Memcached Servers (Multigraph) +=head1 MEMCACHED MULTI -=head1 MUNIN CONFIGURATION +Memcached Multi - A Plugin to monitor Memcached Servers (Multigraph) -[memcached_*] - env.host 127.0.0.1 *default* - env.port 11211 *default* - env.timescale 3 *default* + The common difference between this memcached Munin plugin and others that exists, is that + others don't expose slab information from memcached, so you can better tune your memcached + interaction / stability / etc. With this plugin we leverage multigraph capabilities in + Munin to "hide" the slab information underneath of their parent graphs. -=head2 MUNIN ENVIRONMENT CONFIGURATION EXPLANATION +=head1 MUNIN NODE CONFIGURATION - host = host we are going to monitor +The following configuration information can be overridden by placing environment definitions + like shown here, in a file located in /etc/munin/plugin-conf.d + +[memcached_multi_*] + env.host 127.0.0.1 *default* + env.port 11211 *default* + env.timescale 3 *default* + env.cmds get set delete incr decr touch *default* + env.leitime -1 *default* + +=head2 MUNIN NODE ENVIRONMENT CONFIGURATION EXPLANATION + + host = host we are going to monitor, this can be used to specify a unix socket. port = port we are connecting to, in order to gather stats timescale = what time frame do we want to format our graphs too + cmds = cmd types to display on cmd graph, remove cmds you don't want displayed from list. + leitime = setting this to 1 will re-enable slab eviction time graphs, see note below. -=head1 NODE CONFIGURATION +=head2 BASIC TROUBLESHOOTING Please make sure you can telnet to your memcache servers and issue the following commands: stats, stats settings, stats items and stats slabs. +=head2 PLUGIN INFORMATION + Available Graphs contained in this Plugin bytes => This graphs the current network traffic in and out -commands => I This graphs the current commands being issued to the memcache machine. B +commands => I This graphs the current commands being issued to the memcache machine. + B -conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec is derived from total_conns / uptime. +conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec + and is derived from total_conns / uptime. -evictions => I This graphs the current evictions on the node. B +evictions => I This graphs the current evictions on the node. + B -items => I This graphs the current items and total items in the memcached node. B +items => I This graphs the current items and total items in the memcached node. + B -memory => I This graphs the current and max memory allocation B +memory => I This graphs the current and max memory allocation. + B -The following example holds true for all graphing options in this plugin. - Example: ln -s /usr/share/munin/plugins/memcached_multi_ /etc/munin/plugins/memcached_multi_bytes +unfetched => I This graphs the number of items that were never touched by a + get/incr/append/etc before being evicted or expiring from the cache. + B =head1 ADDITIONAL INFORMATION +B The slab plugin for LEI has been disabled since I believe the counters to be inaccurate, + or perhaps not being updated as often I thought they would be. They can be re-enabled by + setting an environment variable, see munin configuration section at the top. + You will find that some of the graphs have LEI on them. This was done in order to save room on space for text and stands for B. @@ -52,13 +77,11 @@ The B variable formats certain graphs based on the following guidelin =head1 ACKNOWLEDGEMENTS -The core of this plugin is based on the mysql_ plugin maintained by Kjell-Magne Ãierud - -Thanks to dormando as well for putting up with me ;) +Thanks to dormando for putting up with me ;) =head1 AUTHOR -Matt West < https://code.google.com/p/memcached-munin-plugin/ > +Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin > =head1 LICENSE @@ -66,703 +89,1543 @@ GPLv2 =head1 MAGIC MARKERS - #%# family=auto - #%# capabilities=autoconf suggest +#%# family=auto +#%# capabilities=autoconf suggest =cut use strict; +use warnings; use IO::Socket; use Munin::Plugin; +use File::Basename; + +if ( basename($0) !~ /(?:([^\/]+)_)?memcached_multi_/ ) { + print +"This script needs to be named (prefix_)?memcached_multi_ to run properly.\n"; + exit 1; +} +# tell munin about our multigraph capabilities need_multigraph(); +=head1 Variable Declarations + + This section of code is to declare the variables used throughout the plugin + Some of them are imported as environment variables from munin plugin conf.d + file, others are hashes used for storing information that comes from the + stats commands issued to memcached. + +=cut + +# lets import environment variables for the plugin or use the default my $host = $ENV{host} || "127.0.0.1"; my $port = $ENV{port} || 11211; +my $connection; + +# This gives us the ability to control the timescale our graphs are displaying. +# The default it set to divide by hours, if you want to get seconds set it to 1. +# Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days +my $timescale = $ENV{timescale} || 3; + +# This gives us the ability to turn the Last Evicted Item time slab graph on. +# It was removed because I believe the counter / response to be broken but +# perhaps this was useful to someone. +my $leitime = $ENV{leitime} || -1; + +# This gives us the ability to specify which commands we want to display on the +# command graph. Allowing finer control since some environments don't leverage +# every command possible in memcached. +# Options: get set delete incr decr cas touch flush +my $commands = $ENV{cmds} || "get set delete incr decr touch"; -my %stats; # This hash contains the information contained in two memcache commands # stats and stats settings. +my %stats; -my %items; # This gives us eviction rates and other hit stats per slab # We track this so we can see if something was evicted earlier than necessary +my %items; -my %chnks; # This gives us the memory size and usage per slab -# We track this so we can see what slab is being used the most and has no free chunks +# We track this so we can see what slab is being used the most and has no free chunks # so we can re-tune memcached to allocate more pages for the specified chunk size +my %chnks; -my $timescale = $ENV{timescale} || 3; -# This gives us the ability to control the timescale our graphs are displaying. -# The default it set to divide by hours, if you want to get seconds set it to 1. -# Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days +# Variable for setting up a quick access map for plugin configurations / version adherence +my $globalmap; + +=head2 Graph Declarations + + This block of code builds up all of the graph info for all root / subgraphs. + + %graphs: is a container for all of the graph definition information. In here is where you'll + find the configuration information for munin's graphing procedure. + Format: + + $graph{graph_name} => { + config => { + You'll find the main graph config stored here + { key => value }, + { ... }, + }, + datasrc => [ + Name: name given to data value + Attr: Attribute and value, attribute must be valid plugin argument + { name => 'Name', info => 'info about graph', ... }, + { ... }, + ], + } + +=cut -# So I was trying to figure out how to build this up, and looking at some good examples -# I decided to use the format, or for the most part, the format from the mysql_ munin plugin -# for Innodb by Kjell-Magne Ãierud, it just spoke ease of flexibility especially with multigraphs -# thanks btw ;) -# -# %graphs is a container for all of the graph definition information. In here is where you'll -# find the configuration information for munin's graphing procedure. -# Format: -# -# $graph{graph_name} => { -# config => { -# # You'll find keys and values stored here for graph manipulation -# }, -# datasrc => [ -# # Name: name given to data value -# # Attr: Attribute for given value -# { name => 'Name', (Attr) }, -# { ... }, -# ], -# } my %graphs; +# main graph for memcached item count $graphs{items} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Items in Memcached', - category => 'memcached', - title => 'Items', - info => 'This graph shows the number of items in use by memcached', + args => '--base 1000 --lower-limit 0', + vlabel => 'Items in Memcached', + category => 'memcached global items', + title => 'Items', + info => 'Number of items in use by memcached', }, datasrc => [ { name => 'curr_items', label => 'Current Items', min => '0' }, - { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' }, + { + name => 'total_items', + label => 'New Items', + min => '0', + type => 'DERIVE' + }, ], }; +# main graph for memcached memory usage $graphs{memory} = { config => { - args => '--base 1024 --lower-limit 0', - vlabel => 'Bytes Used', - category => 'memcached', - title => 'Memory Usage', - info => 'This graph shows the memory consumption of memcached', + args => '--base 1024 --lower-limit 0', + vlabel => 'Bytes Used', + category => 'memcached global memory', + title => 'Memory Usage', + info => 'Memory consumption of memcached', }, datasrc => [ - { name => 'limit_maxbytes', draw => 'AREA', label => 'Maximum Bytes Allocated', min => '0' }, - { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' }, + { + name => 'limit_maxbytes', + draw => 'AREA', + label => 'Maximum Bytes Allocated', + min => '0' + }, + { + name => 'bytes', + draw => 'AREA', + label => 'Current Bytes Used', + min => '0' + }, ], }; +# main graph for memcached network usage $graphs{bytes} = { config => { - args => '--base 1000', - vlabel => 'bits in (-) / out (+)', - title => 'Network Traffic', + args => '--base 1000', + vlabel => 'bits in (-) / out (+)', + title => 'Network Traffic', category => 'memcached', - info => 'This graph shows the network traffic in (-) / out (+) of the machine', - order => 'bytes_read bytes_written', + info => 'Network traffic in (-) / out (+) of the machine', + order => 'bytes_read bytes_written', }, datasrc => [ - { name => 'bytes_read', type => 'DERIVE', label => 'Network Traffic coming in (-)', graph => 'no', cdef => 'bytes_read,8,*', min => '0' }, - { name => 'bytes_written', type => 'DERIVE', label => 'Traffic in (-) / out (+)', negative => 'bytes_read', cdef => 'bytes_written,8,*', min => '0' }, + { + name => 'bytes_read', + type => 'DERIVE', + label => 'Network Traffic coming in (-)', + graph => 'no', + cdef => 'bytes_read,8,*', + min => '0' + }, + { + name => 'bytes_written', + type => 'DERIVE', + label => 'Traffic in (-) / out (+)', + negative => 'bytes_read', + cdef => 'bytes_written,8,*', + min => '0' + }, ], }; +# graph for memcached connections $graphs{conns} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Connections per ${graph_period}', + args => '--base 1000 --lower-limit 0', + vlabel => 'Connections per ${graph_period}', category => 'memcached', - title => 'Connections', - info => 'This graph shows the number of connections being handled by memcached', - order => 'max_conns curr_conns avg_conns', + title => 'Connections', + info => 'Number of connections being handled by memcached', + order => 'max_conns curr_conns avg_conns', }, datasrc => [ { name => 'curr_conns', label => 'Current Connections', min => '0' }, - { name => 'max_conns', label => 'Max Connections', min => '0' }, - { name => 'avg_conns' , label => 'Avg Connections', min => '0' }, + { name => 'max_conns', label => 'Max Connections', min => '0' }, + { name => 'avg_conns', label => 'Avg Connections', min => '0' }, ], }; +# main graph for memcached commands issued $graphs{commands} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Commands per ${graph_period}', - category => 'memcached', - title => 'Commands', - info => 'This graph shows the number of commands being handled by memcached', + args => '--base 1000 --lower-limit 0', + vlabel => 'Commands per ${graph_period}', + category => 'memcached global commands', + title => 'Commands', + info => 'Number of commands being handled by memcached', }, datasrc => [ - { name => 'cmd_get', type => 'DERIVE', label => 'Gets', info => 'Cumulative number of retrieval reqs', min => '0' }, - { name => 'cmd_set', type => 'DERIVE', label => 'Sets', info => 'Cumulative number of storage reqs', min => '0' }, - { name => 'get_hits', type => 'DERIVE', label => 'Get Hits', info => 'Number of keys that were requested and found', min => '0' }, - { name => 'get_misses', type => 'DERIVE', label => 'Get Misses', info => 'Number of keys there were requested and not found', min => '0' }, - { name => 'delete_hits', type => 'DERIVE', label => 'Delete Hits', info => 'Number of delete requests that resulted in a deletion of a key', min => '0' }, - { name => 'delete_misses', type => 'DERIVE', label => 'Delete Misses', info => 'Number of delete requests for missing key', min => '0' }, - { name => 'incr_hits', type => 'DERIVE', label => 'Increment Hits', info => 'Number of successful increment requests', min => '0' }, - { name => 'incr_misses', type => 'DERIVE', label => 'Increment Misses', info => 'Number of unsuccessful increment requests', min => '0' }, - { name => 'decr_hits', type => 'DERIVE', label => 'Decrement Hits', info => 'Number of successful decrement requests', min => '0' }, - { name => 'decr_misses', type => 'DERIVE', label => 'Decrement Misses', info => 'Number of unsuccessful decrement requests', min => '0' }, + { + name => 'cmd_get', + type => 'DERIVE', + label => 'Gets', + info => 'Cumulative number of retrieval reqs', + min => '0' + }, + { + name => 'cmd_set', + type => 'DERIVE', + label => 'Sets', + info => 'Cumulative number of storage reqs', + min => '0' + }, + { + name => 'cmd_flush', + type => 'DERIVE', + label => 'Flushes', + info => 'Cumulative number of flush reqs', + min => '0' + }, + { + name => 'cmd_touch', + type => 'DERIVE', + label => 'Touches', + info => 'Cumulative number of touch reqs', + min => '0' + }, + { + name => 'get_hits', + type => 'DERIVE', + label => 'Get Hits', + info => 'Number of keys that were requested and found', + min => '0' + }, + { + name => 'get_misses', + type => 'DERIVE', + label => 'Get Misses', + info => 'Number of keys there were requested and not found', + min => '0' + }, + { + name => 'delete_hits', + type => 'DERIVE', + label => 'Delete Hits', + info => + 'Number of delete requests that resulted in a deletion of a key', + min => '0' + }, + { + name => 'delete_misses', + type => 'DERIVE', + label => 'Delete Misses', + info => 'Number of delete requests for missing key', + min => '0' + }, + { + name => 'incr_hits', + type => 'DERIVE', + label => 'Increment Hits', + info => 'Number of successful increment requests', + min => '0' + }, + { + name => 'incr_misses', + type => 'DERIVE', + label => 'Increment Misses', + info => 'Number of unsuccessful increment requests', + min => '0' + }, + { + name => 'decr_hits', + type => 'DERIVE', + label => 'Decrement Hits', + info => 'Number of successful decrement requests', + min => '0' + }, + { + name => 'decr_misses', + type => 'DERIVE', + label => 'Decrement Misses', + info => 'Number of unsuccessful decrement requests', + min => '0' + }, + { + name => 'cas_misses', + type => 'DERIVE', + label => 'CAS Misses', + info => 'Number of Compare and Swap requests against missing keys', + min => '0' + }, + { + name => 'cas_hits', + type => 'DERIVE', + label => 'CAS Hits', + info => 'Number of successful Compare and Swap requests', + min => '0' + }, + { + name => 'cas_badval', + type => 'DERIVE', + label => 'CAS Badval', + info => 'Number of unsuccessful Compare and Swap requests', + min => '0' + }, + { + name => 'touch_hits', + type => 'DERIVE', + label => 'Touch Hits', + info => 'Number of successfully touched keys', + min => '0' + }, + { + name => 'touch_misses', + type => 'DERIVE', + label => 'Touch Misses', + info => 'Number of unsuccessful touch keys', + min => '0' + }, ], }; +# main graph for memcached eviction rates $graphs{evictions} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Evictions per ${graph_period}', - category => 'memcached', - title => 'Evictions', - info => 'This graph shows the number of evictions per second', + args => '--base 1000 --lower-limit 0', + vlabel => 'Evictions per ${graph_period}', + category => 'memcached global evictions', + title => 'Evictions', + info => 'Number of evictions per second', }, datasrc => [ - { name => 'evictions', label => 'Evictions', info => 'Cumulative Evictions Across All Slabs', type => 'DERIVE', min => '0' }, - { name => 'evicted_nonzero', label => 'Evictions prior to Expire', info => 'Cumulative Evictions forced to expire prior to expiration', type => 'DERIVE', min => '0' }, - { name => 'reclaimed', label => 'Reclaimed Items', info => 'Cumulative Reclaimed Item Entries Across All Slabs', type => 'DERIVE', min => '0' }, + { + name => 'evictions', + type => 'DERIVE', + label => 'Evictions', + info => 'Cumulative Evictions Across All Slabs', + min => '0' + }, + { + name => 'evicted_nonzero', + type => 'DERIVE', + label => 'Evictions prior to Expire', + info => 'Cumulative Evictions forced to expire prior to expiration', + min => '0' + }, + { + name => 'reclaimed', + type => 'DERIVE', + label => 'Reclaimed Items', + info => 'Cumulative Reclaimed Item Entries Across All Slabs', + min => '0' + }, ], }; +# main graph for memcached eviction rates +$graphs{unfetched} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Unfetched Items per ${graph_period}', + category => 'memcached global unfetched', + title => 'Unfetched Items', + info => +'Number of items that were never touched get/incr/append/etc before X occured', + }, + datasrc => [ + { + name => 'expired_unfetched', + type => 'DERIVE', + label => 'Expired Unfetched', + min => '0', + info => +'Number of items that expired and never had get/incr/append/etc performed' + }, + { + name => 'evicted_unfetched', + type => 'DERIVE', + label => 'Evictioned Unfetched', + min => '0', + info => +'Number of items that evicted and never had get/incr/append/etc performed' + }, + ], +}; + +# subgraph for breaking memory info down by slab ( subgraph of memory ) $graphs{slabchnks} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Available Chunks for this Slab', - category => 'memcached', - title => 'Chunk Usage for Slab: ', + args => '--base 1000 --lower-limit 0', + vlabel => 'Available Chunks for this Slab', + category => 'memcached slab chunk usage', + title => 'Chunk Usage for Slab: ', info => 'This graph shows you the chunk usage for this memory slab.', }, datasrc => [ - { name => 'total_chunks', label => 'Total Chunks Available', min => '0' }, + { + name => 'total_chunks', + label => 'Total Chunks Available', + min => '0' + }, { name => 'used_chunks', label => 'Total Chunks in Use', min => '0' }, - { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' }, + { + name => 'free_chunks', + label => 'Total Chunks Not in Use (Free)', + min => '0' + }, ], }; +# subgraph for breaking commands down by slab ( subgraph of commands ) $graphs{slabhits} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Hits per Slab per ${graph_period}', - category => 'memcached', - title => 'Hits for Slab: ', - info => 'This graph shows you the successful hit rate for this memory slab.', + args => '--base 1000 --lower-limit 0', + vlabel => 'Hits per Slab per ${graph_period}', + category => 'memcached slab commands', + title => 'Hits for Slab: ', + info => + 'This graph shows you the successful hit rate for this memory slab.', }, datasrc => [ - { name => 'get_hits', label => 'Get Requests', type => 'DERIVE', min => '0' }, - { name => 'cmd_set', label => 'Set Requests', type => 'DERIVE', min => '0' }, - { name => 'delete_hits', label => 'Delete Requests', type => 'DERIVE', min => '0' }, - { name => 'incr_hits', label => 'Increment Requests', type => 'DERIVE', min => '0' }, - { name => 'decr_hits', label => 'Decrement Requests', type => 'DERIVE', min => '0' }, + { + name => 'get_hits', + label => 'Get Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'cmd_set', + label => 'Set Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'delete_hits', + label => 'Delete Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'incr_hits', + label => 'Increment Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'decr_hits', + label => 'Decrement Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'cas_hits', + label => 'Sucessful CAS Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'cas_badval', + label => 'UnSucessful CAS Requests', + type => 'DERIVE', + min => '0' + }, + { + name => 'touch_hits', + label => 'Touch Requests', + type => 'DERIVE', + min => '0' + }, ], }; +# subgraph for breaking evictions down by slab ( subgraph of evictions ) $graphs{slabevics} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Evictions per Slab per ${graph_period}', - category => 'memcached', - title => 'Evictions for Slab: ', + args => '--base 1000 --lower-limit 0', + vlabel => 'Evictions per Slab per ${graph_period}', + category => 'memcached slab evictions', + title => 'Evictions for Slab: ', info => 'This graph shows you the eviction rate for this memory slab.', }, datasrc => [ - { name => 'evicted', label => 'Total Evictions', type => 'DERIVE', min => '0' }, - { name => 'evicted_nonzero', label => 'Evictions from LRU Prior to Expire', type => 'DERIVE', min => '0' }, - { name => 'reclaimed', label => 'Reclaimed Expired Items', info => 'This is number of times items were stored in expired entry memory space', type => 'DERIVE', min => '0' }, + { + name => 'evicted', + label => 'Total Evictions', + type => 'DERIVE', + min => '0', + info => 'Items evicted from memory slab' + }, + { + name => 'evicted_nonzero', + type => 'DERIVE', + label => 'Evictions from LRU Prior to Expire', + info => 'Items evicted from memory slab before ttl expiration', + min => '0' + }, + { + name => 'reclaimed', + type => 'DERIVE', + label => 'Reclaimed Expired Items', + info => + 'Number of times an item was stored in expired memory slab space', + min => '0' + }, ], }; +# subgraph for showing the time between an item was last evicted and requested ( subgraph of evictions ) $graphs{slabevictime} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => ' since Request for LEI', - category => 'memcached', - title => 'Eviction Request Time for Slab: ', - info => 'This graph shows you the time since we requested the last evicted item', + args => '--base 1000 --lower-limit 0', + vlabel => ' since Request for LEI', + category => 'memcached slab eviction time', + title => 'Eviction Request Time for Slab: ', + info => +'This graph shows you the time since we requested the last evicted item', }, datasrc => [ - { name => 'evicted_time', label => 'Eviction Time (LEI)', info => 'Time Since Request for Last Evicted Item', min => '0' }, + { + name => 'evicted_time', + label => 'Eviction Time (LEI)', + info => 'Time Since Request for Last Evicted Item', + min => '0' + }, ], }; +# subgraph for breaking items down by slab ( subgraph of items ) $graphs{slabitems} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => 'Items per Slab', - category => 'memcached', - title => 'Items in Slab: ', - info => 'This graph shows you the number of items and reclaimed items per slab.', + args => '--base 1000 --lower-limit 0', + vlabel => 'Items per Slab', + category => 'memcached slab item count', + title => 'Items in Slab: ', + info => +'This graph shows you the number of items and reclaimed items per slab.', }, datasrc => [ - { name => 'number', label => 'Items', info => 'This is the amount of items stored in this slab', min => '0' }, + { + name => 'number', + label => 'Items', + draw => 'AREA', + info => 'This is the amount of items stored in this slab', + min => '0' + }, ], }; +# subgraph for showing the age of the eldest item stored in a slab ( subgraph of items ) $graphs{slabitemtime} = { config => { - args => '--base 1000 --lower-limit 0', - vlabel => ' since item was stored', - category => 'memcached', - title => 'Age of Eldest Item in Slab: ', + args => '--base 1000 --lower-limit 0', + vlabel => ' since item was stored', + category => 'memcached slab item age', + title => 'Age of Eldest Item in Slab: ', info => 'This graph shows you the time of the eldest item in this slab', }, + datasrc => + [ { name => 'age', label => 'Eldest Item\'s Age', min => '0' }, ], +}; + +# main graph for memcached eviction rates +$graphs{slabunfetched} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Unfetched Items per ${graph_period}', + category => 'memcached slab unfetched', + title => 'Unfetched Items in Slab: ', + info => +'Number of items that were never touched get/incr/append/etc before X occured', + }, datasrc => [ - { name => 'age', label => 'Eldest Item\'s Age', min => '0' }, + { + name => 'expired_unfetched', + type => 'DERIVE', + label => 'Expired Unfetched', + min => '0', + info => +'Number of items that expired and never had get/incr/append/etc performed' + }, + { + name => 'evicted_unfetched', + type => 'DERIVE', + label => 'Evictioned Unfetched', + min => '0', + info => +'Number of items that evicted and never had get/incr/append/etc performed' + }, ], }; -## -#### Config Check #### -## +=head1 Munin Checks + + These checks look for config / autoconf / suggest params -if (defined $ARGV[0] && $ARGV[0] eq 'config') { +=head2 Config Check - $0 =~ /memcached_multi_(.+)*/; - my $plugin = $1; + This block of code looks at the argument that is possibly supplied, + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config + subroutine for the plugin specified, otherwise it dies complaining + about an unknown plugin. - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; +=cut + +if ( defined $ARGV[0] && $ARGV[0] eq 'config' ) { + +# Lets get our plugin from the symlink being called up, we'll also verify its a valid +# plugin that we have graph information for + $0 =~ /(?:([^\/]+)_)?memcached_multi_(.+)$/; + my $prefix = $1 ? $1 : ''; + my $plugin = $2; + die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' ) + unless $graphs{$plugin}; - # We need to fetch the stats before we do any config, cause its needed for multigraph +# We need to fetch the stats before we do any config, cause its needed for multigraph +# subgraphs which use slab information for title / info per slab fetch_stats(); + $globalmap = buildglobalmap(); # Now lets go ahead and print out our config. - do_config($plugin); - exit 0; + do_config( $prefix, $plugin ); + exit 0; } -## -#### Autoconf Check #### -## +=head2 Autoconf Check -if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { + This block of code looks at the argument that is possibly supplied, + should it be autoconf, we will attempt to connect to the memcached + service specified on the host:port, upon successful connection it + prints yes, otherwise it prints no. + +=cut - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); +if ( defined $ARGV[0] && $ARGV[0] eq 'autoconf' ) { - if (defined($s)) { + # Lets attempt to connect to memcached + my $s = get_conn(); + + # Lets verify that we did connect to memcached + if ( defined($s) ) { print "yes\n"; exit 0; - } else { - print "no (unable to connect to $host\[:$port\])\n"; + } + else { + print "no (unable to connect to $connection)\n"; exit 0; } } -## -#### Suggest Check #### -## +=head2 Suggest Check + + This block of code looks at the argument that is possibly supplied, + should it be suggest, we are going to print the possible plugins + which can be specified. Note we only specify the root graphs for the + multigraphs, since the rest of the subgraphs will appear "behind" the + root graphs. It also attempts to connect to the memcached service to + verify it is infact running. + +=cut -if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { +if ( defined $ARGV[0] && $ARGV[0] eq 'suggest' ) { - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); + # Lets attempt to connect to memcached + my $s = get_conn(); - if (defined($s)) { - my @rootplugins = ('bytes','conns','commands','evictions','items','memory'); + # Lets check that we did connect to memcached + if ( defined($s) ) { + fetch_stats(); + my @rootplugins = + ( 'bytes', 'conns', 'commands', 'evictions', 'items', 'memory' ); + if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) { + push( @rootplugins, 'unfetched' ); + } foreach my $plugin (@rootplugins) { print "$plugin\n"; } exit 0; - } else { - print "no (unable to connect to $host\[:$port\])\n"; + } + else { + print "no (unable to connect to $connection)\n"; exit 0; } } -## -#### Well We aren't running (auto)config/suggest so lets print some stats #### -## +=head1 Output Subroutines -fetch_output(); + Output Subroutine calls to output data values -## -#### Subroutines for printing info gathered from memcached #### -## +=head2 fetch_output -## -#### This subroutine performs the bulk processing for printing statistics. -## + This subroutine is the main call for printing data for the plugin. + No parameters are taken as this is the default call if no arguments + are supplied from the command line. -sub fetch_output { +=cut - $0 =~ /memcached_multi_(.+)*/; - my $plugin = $1; +# Well, no arguments were supplied that we know about, so lets print some data +$0 =~ /(?:([^\/]+)_)?memcached_multi_(.+)$/; +my $prefix = $1 ? $1 : ''; +my $plugin = $2; +die 'Unknown Plugin Specified: ' . ( $plugin ? $plugin : '' ) + unless $graphs{$plugin}; +fetch_stats(); +$globalmap = buildglobalmap(); +fetch_output( $prefix, $plugin ); - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; +sub fetch_output { + my ( $prefix, $plugin ) = (@_); - # Well we need to actually fetch the stats before we do anything to them. - fetch_stats(); - # Now lets go ahead and print out our output. my @subgraphs; - if ($plugin eq 'memory') { + if ( $plugin eq 'memory' ) { @subgraphs = ('slabchnks'); - foreach my $slabid(sort{$a <=> $b} keys %chnks) { - print_submulti_output($slabid,$plugin,@subgraphs); + foreach my $slabid ( sort { $a <=> $b } keys %chnks ) { + print_submulti_output( $prefix, $slabid, $plugin, @subgraphs ); } - print_rootmulti_output($plugin); - } elsif ($plugin eq 'commands') { + print_subrootmulti_output( $prefix, $plugin ); + print_rootmulti_output( $prefix, $plugin ); + } + elsif ( $plugin eq 'commands' ) { @subgraphs = ('slabhits'); - foreach my $slabid(sort{$a <=> $b} keys %chnks) { - print_submulti_output($slabid,$plugin,@subgraphs); - } - print_rootmulti_output($plugin); - } elsif ($plugin eq 'evictions') { - @subgraphs = ('slabevics','slabevictime'); - foreach my $slabid (sort{$a <=> $b} keys %items) { - print_submulti_output($slabid,$plugin,@subgraphs); - } - print_rootmulti_output($plugin); - } elsif ($plugin eq 'items') { - @subgraphs = ('slabitems','slabitemtime'); - foreach my $slabid (sort{$a <=> $b} keys %items) { - print_submulti_output($slabid,$plugin,@subgraphs); - } - print_rootmulti_output($plugin); - } else { + foreach my $slabid ( sort { $a <=> $b } keys %chnks ) { + print_submulti_output( $prefix, $slabid, $plugin, @subgraphs ); + } + print_subrootmulti_output( $prefix, $plugin ); + print_rootmulti_output( $prefix, $plugin ); + } + elsif ( $plugin eq 'evictions' ) { + @subgraphs = ('slabevics'); + if ( $leitime == 1 ) { push( @subgraphs, 'slabevictime' ); } + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_output( $prefix, $slabid, $plugin, @subgraphs ); + } + print_subrootmulti_output( $prefix, $plugin ); + print_rootmulti_output( $prefix, $plugin ); + } + elsif ( $plugin eq 'items' ) { + @subgraphs = ( 'slabitems', 'slabitemtime' ); + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_output( $prefix, $slabid, $plugin, @subgraphs ); + } + print_subrootmulti_output( $prefix, $plugin ); + print_rootmulti_output( $prefix, $plugin ); + } + elsif ( $plugin eq 'unfetched' ) { + @subgraphs = ('slabunfetched'); + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_output( $prefix, $slabid, $plugin, @subgraphs ); + } + print_subrootmulti_output( $prefix, $plugin ); + print_rootmulti_output( $prefix, $plugin ); + } + else { print_root_output($plugin); } return; } -## -#### This subroutine is for the root non-multigraph graphs which render on the main node page #### -## +=head2 print_root_output + + This subroutine prints out the return values for our non-multigraph root graphs. + It takes one parameter $plugin and returns when completed. + + $plugin; graph we are calling up to print data values for + + Example: print_root_output($plugin); + +=cut sub print_root_output { - my ($plugin) = (@_); + # Lets get our plugin, set our graph reference and print out info for Munin + my ($plugin) = (@_); my $graph = $graphs{$plugin}; - print "graph memcached_$plugin\n"; - - if ($plugin ne 'conns') { - foreach my $dsrc (@{$graph->{datasrc}}) { + # The conns plugin has some specific needs, looking for plugin type + if ( $plugin ne 'conns' ) { + foreach my $dsrc ( @{ $graph->{datasrc} } ) { my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key ne 'name'); + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key ne 'name' ); my $output = $stats{$value}; print "$dsrc->{name}.value $output\n"; } } - } else { + } + else { my $output; - foreach my $dsrc (@{$graph->{datasrc}}) { + foreach my $dsrc ( @{ $graph->{datasrc} } ) { my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - if ($value eq 'max_conns') { + while ( my ( $key, $value ) = each(%datasrc) ) { + if ( $value eq 'max_conns' ) { $output = $stats{maxconns}; - } elsif ($value eq 'curr_conns') { + } + elsif ( $value eq 'curr_conns' ) { $output = $stats{curr_connections}; - } elsif ($value eq 'avg_conns') { - $output = sprintf("%02d", $stats{total_connections} / $stats{uptime}); - } else { + } + elsif ( $value eq 'avg_conns' ) { + $output = sprintf( "%02d", + $stats{total_connections} / $stats{uptime} ); + } + else { next; } print "$dsrc->{name}.value $output\n"; } } } - return; } -## -#### This subroutine is for the root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_output + + This subroutine prints out the return values for our multigraph root graphs. + It takes one parameter $plugin and returns when completed. + + $plugin; root graph we are calling up to print data values for + + Example: print_rootmulti_output($plugin); + +=cut sub print_rootmulti_output { - my ($plugin) = (@_); + # Lets get our plugin, set our graph reference and print out info for Munin + my ( $prefix, $plugin ) = (@_); my $graph = $graphs{$plugin}; + if ($prefix) { + print "multigraph $prefix\_memcached_multi_$plugin\n"; + } + else { + print "multigraph memcached_multi_$plugin\n"; + } - print "multigraph memcached_$plugin\n"; - - foreach my $dsrc (@{$graph->{datasrc}}) { - my $output = 0; + # Lets print our data values with their appropriate name + foreach my $dsrc ( @{ $graph->{datasrc} } ) { + my $output = 0; my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key ne 'name'); - if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) { - foreach my $slabid (sort{$a <=> $b} keys %items) { + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key ne 'name' ); + next + if ( ( $plugin eq 'evictions' ) + && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) ); + if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) ) + { + foreach my $slabid ( sort { $a <=> $b } keys %items ) { $output += $items{$slabid}->{evicted_nonzero}; } - } else { + } + else { $output = $stats{$value}; } print "$dsrc->{name}.value $output\n"; } } + return; +} + +=head2 print_subrootmulti_output + + This subroutine prints out the return values for our multigraph root graphs, only this set of + data will display on the subpage made by the multigraph capabilities of munin and the plugin. + It takes one parameter $plugin and returns when completed. + + $plugin; root graph we are calling up to print data values for + + Example: print_rootmulti_output($plugin); + +=cut + +sub print_subrootmulti_output { + + # Lets get our plugin, set our graph reference and print out info for Munin + my ( $prefix, $plugin ) = (@_); + my $graph = $graphs{$plugin}; + if ($prefix) { + if ( $plugin eq 'evictions' ) { + print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n"; + } + else { + print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n"; + } + } + else { + if ( $plugin eq 'evictions' ) { + print "multigraph memcached_multi_$plugin.global$plugin\n"; + } + else { + print "multigraph memcached_multi_$plugin.$plugin\n"; + } + } + # Lets print our data values with their appropriate name + foreach my $dsrc ( @{ $graph->{datasrc} } ) { + my $output = 0; + my %datasrc = %$dsrc; + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key ne 'name' ); + next + if ( ( $plugin eq 'evictions' ) + && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) ); + if ( ( $plugin eq 'evictions' ) && ( $value eq 'evicted_nonzero' ) ) + { + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + $output += $items{$slabid}->{evicted_nonzero}; + } + } + else { + $output = $stats{$value}; + } + print "$dsrc->{name}.value $output\n"; + } + } return; } -## -#### This subroutine is for the sub multigraph graphs created via the multigraph plugin #### -## +=head2 print_submulti_output + + This subroutine prints out the return values for our multigraph subgraphs. It takes + three parameters $slabid, $plugin, @subgraphs and then rReturns when completed. + + $slabid; slab id that we will use to grab info from and print out + $plugin; root graph being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_output($slabid,$plugin,@subgraphs); + +=cut sub print_submulti_output { - my ($slabid,$plugin,@subgraphs) = (@_); + + # Lets get our slabid, plugin, and subgraphs + my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_); my $currslab = undef; + # Time to loop over our subgraphs array foreach my $sgraph (@subgraphs) { + # Lets set our graph reference for quick calling, and print some info for munin my $graph = $graphs{$sgraph}; + if ($prefix) { + print + "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n"; + } + else { + print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n"; + } - print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; - - if ($plugin eq 'evictions') { + # Lets figure out what slab info we are trying to call up + if ( ( $plugin eq 'evictions' ) + || ( $plugin eq 'items' ) + || ( $plugin eq 'unfetched' ) ) + { $currslab = $items{$slabid}; - } elsif ($plugin eq 'memory') { - $currslab = $chnks{$slabid}; - } elsif ($plugin eq 'commands') { + } + elsif ( ( $plugin eq 'memory' ) || ( $plugin eq 'commands' ) ) { $currslab = $chnks{$slabid}; - } elsif ($plugin eq 'items') { - $currslab = $items{$slabid}; - } else { + } + else { return; } - foreach my $dsrc (@{$graph->{datasrc}}) { + # Lets print our data values with their appropriate name + foreach my $dsrc ( @{ $graph->{datasrc} } ) { my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key ne 'name'); + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key ne 'name' ); + next + if ( ( $sgraph eq 'slabevics' ) + && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) ) + ); my $output = $currslab->{$value}; - if (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime')) { - $output = time_scale('data',$output); ; + if ( ( $sgraph eq 'slabevictime' ) + || ( $sgraph eq 'slabitemtime' ) ) + { + $output = time_scale( 'data', $output ); } print "$dsrc->{name}.value $output\n"; } } } - return; } -## -#### Subroutines for printing out config information for graphs #### -## +=head1 Config Subroutines + + These subroutines handle the config portion of munin calls. + +=head2 do_config + + This is the main call issued assuming we call up config and plugin specified exists + The subroutine takes one parameter $plugin, and returns when completed. -## -#### This subroutine does the bulk printing the config info per graph #### -## + $plugin; root graph being called + + Example: do_config($prefix, $plugin); + +=cut sub do_config { - my ($plugin) = (@_); + my ( $prefix, $plugin ) = (@_); my @subgraphs; - if ($plugin eq 'memory') { + if ( $plugin eq 'memory' ) { @subgraphs = ('slabchnks'); - foreach my $slabid (sort{$a <=> $b} keys %chnks) { - print_submulti_config($slabid,$plugin,@subgraphs); + foreach my $slabid ( sort { $a <=> $b } keys %chnks ) { + print_submulti_config( $prefix, $slabid, $plugin, @subgraphs ); } - print_rootmulti_config($plugin); - } elsif ($plugin eq 'commands') { + print_subrootmulti_config( $prefix, $plugin ); + print_rootmulti_config( $prefix, $plugin ); + } + elsif ( $plugin eq 'commands' ) { @subgraphs = ('slabhits'); - foreach my $slabid (sort{$a <=> $b} keys %chnks) { - print_submulti_config($slabid,$plugin,@subgraphs); + foreach my $slabid ( sort { $a <=> $b } keys %chnks ) { + print_submulti_config( $prefix, $slabid, $plugin, @subgraphs ); } - print_rootmulti_config($plugin); - } elsif ($plugin eq 'evictions') { - @subgraphs = ('slabevics','slabevictime'); - foreach my $slabid (sort{$a <=> $b} keys %items) { - print_submulti_config($slabid,$plugin,@subgraphs); + print_subrootmulti_config( $prefix, $plugin ); + print_rootmulti_config( $prefix, $plugin ); + } + elsif ( $plugin eq 'evictions' ) { + @subgraphs = ('slabevics'); + if ( $leitime == 1 ) { push( @subgraphs, 'slabevictime' ); } + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_config( $prefix, $slabid, $plugin, @subgraphs ); + } + print_subrootmulti_config( $prefix, $plugin ); + print_rootmulti_config( $prefix, $plugin ); + } + elsif ( $plugin eq 'items' ) { + @subgraphs = ( 'slabitems', 'slabitemtime' ); + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_config( $prefix, $slabid, $plugin, @subgraphs ); } - print_rootmulti_config($plugin); - } elsif ($plugin eq 'items') { - @subgraphs = ('slabitems','slabitemtime'); - foreach my $slabid (sort{$a <=> $b} keys %items) { - print_submulti_config($slabid,$plugin,@subgraphs); + print_subrootmulti_config( $prefix, $plugin ); + print_rootmulti_config( $prefix, $plugin ); + } + elsif ( $plugin eq 'unfetched' ) { + @subgraphs = ('slabunfetched'); + foreach my $slabid ( sort { $a <=> $b } keys %items ) { + print_submulti_config( $prefix, $slabid, $plugin, @subgraphs ); } - print_rootmulti_config($plugin); - } else { - print_root_config($plugin); + print_subrootmulti_config( $prefix, $plugin ); + print_rootmulti_config( $prefix, $plugin ); + } + else { + print_root_config( $prefix, $plugin ); } return; } -## -#### This subroutine is for the config info for sub multigraph graphs created via the multigraph plugin #### -## +=head2 print_root_config -sub print_submulti_config { - my ($slabid,$plugin,@subgraphs) = (@_); - my ($slabitems,$slabchnks) = undef; + This subroutine prints out the config information for all of the non-multigraph root graphs. + It takes one parameter, $plugin, returns when completed. - foreach my $sgraph (@subgraphs) { + $prefix; possible prefix used to allow multiple plugins per machine + $plugin; root graph used for multigraph call - my $graph = $graphs{$sgraph}; + Example: print_root_config($prefix,$plugin); - my %graphconf = %{$graph->{config}}; - - print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; +=cut - while ( my ($key, $value) = each(%graphconf)) { - if ($key eq 'title') { - print "graph_$key $value" . "$slabid" . " ($chnks{$slabid}->{chunk_size} Bytes)\n"; - } elsif (($key eq 'vlabel') && (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime'))) { - $value = time_scale('config',$value); - print "graph_$key $value\n"; - } else { - print "graph_$key $value\n"; - } - } +sub print_root_config { - foreach my $dsrc (@{$graph->{datasrc}}) { - my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key eq 'name'); - print "$dsrc->{name}.$key $value\n"; + # Lets get our plugin, set our graph reference and our graph config info + my ( $prefix, $plugin ) = (@_); + my $graph = $graphs{$plugin}; + my %graphconf = %{ $graph->{config} }; + + # Lets tell munin about the graph we are referencing and print the main config + while ( my ( $key, $value ) = each(%graphconf) ) { + if ( $key eq 'title' ) { + if ($prefix) { + print "graph_$key " . ucfirst($prefix) . " $value\n"; + } + else { + print "graph_$key $value\n"; } } + else { + print "graph_$key $value\n"; + } } + # Lets tell munin about our data values and how to treat them + foreach my $dsrc ( @{ $graph->{datasrc} } ) { + my %datasrc = %$dsrc; + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key eq 'name' ); + print "$dsrc->{name}.$key $value\n"; + } + } return; } -## -#### This subroutine is for the config info for root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_config -sub print_rootmulti_config { - my ($plugin) = (@_); + This subroutine prints out the config information for all of the multigraph root graphs. + It takes one parameter, $plugin, returns when completed. - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; + $prefix; possible prefix used to allow multiple plugins per machine + $plugin; root graph used for multigraph call - my $graph = $graphs{$plugin}; + Example: print_rootmulti_config($prefix,$plugin); - my %graphconf = %{$graph->{config}}; +=cut - print "multigraph memcached_$plugin\n"; +sub print_rootmulti_config { + + # Lets get out plugin, set our graph reference and our graph config info + my ( $prefix, $plugin ) = (@_); + my $graph = $graphs{$plugin}; + my %graphconf = %{ $graph->{config} }; - while ( my ($key, $value) = each(%graphconf)) { - print "graph_$key $value\n"; + # Lets tell munin about the graph we are referencing and print the main config + if ($prefix) { + print "multigraph $prefix\_memcached_multi_$plugin\n"; + } + else { + print "multigraph memcached_multi_$plugin\n"; + } + while ( my ( $key, $value ) = each(%graphconf) ) { + if ( $key eq 'category' ) { + print "graph_$key memcached\n"; + } + elsif ( $key eq 'title' ) { + if ($prefix) { + print "graph_$key " . ucfirst($prefix) . " $value\n"; + } + else { + print "graph_$key $value\n"; + } + } + else { + print "graph_$key $value\n"; + } } - foreach my $dsrc (@{$graph->{datasrc}}) { + # Lets tell munin about our data values and how to treat them + foreach my $dsrc ( @{ $graph->{datasrc} } ) { my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key eq 'name'); + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key eq 'name' ); + next + if ( ( $plugin eq 'evictions' ) + && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) ); print "$dsrc->{name}.$key $value\n"; } } - return; } -## -#### This subroutine is for the config info for non multigraph graphs which render on the main node page #### -## +=head2 print_subrootmulti_config -sub print_root_config { - my ($plugin) = (@_); + This subroutine prints out the config information for all of the multigraph root graph, only this + graph of the data will display on the subpage made by the multigraph capabilities of munin and + the plugin. It takes one parameter, $plugin, returns when completed. - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; + $prefix; possible prefix used to allow multiple plugins per machine + $plugin; root graph used for multigraph call - my $graph = $graphs{$plugin}; + Example: print_rootmulti_config($prefix,$plugin); + +=cut - my %graphconf = %{$graph->{config}}; +sub print_subrootmulti_config { - print "graph memcached_$plugin\n"; + # Lets get out plugin, set our graph reference and our graph config info + my ( $prefix, $plugin ) = (@_); + my $graph = $graphs{$plugin}; + my %graphconf = %{ $graph->{config} }; + if ($prefix) { + if ( $plugin eq 'evictions' ) { + print "multigraph $prefix\_memcached_multi_$plugin.global$plugin\n"; + } + else { + print "multigraph $prefix\_memcached_multi_$plugin.$plugin\n"; + } + } + else { + if ( $plugin eq 'evictions' ) { + print "multigraph memcached_multi_$plugin.global$plugin\n"; + } + else { + print "multigraph memcached_multi_$plugin.$plugin\n"; + } + } - while ( my ($key, $value) = each(%graphconf)) { - print "graph_$key $value\n"; + while ( my ( $key, $value ) = each(%graphconf) ) { + if ( $key eq 'title' ) { + if ($prefix) { + print "graph_$key " . ucfirst($prefix) . " $value\n"; + } + else { + print "graph_$key $value\n"; + } + } + else { + print "graph_$key $value\n"; + } } - foreach my $dsrc (@{$graph->{datasrc}}) { + # Lets tell munin about our data values and how to treat them + foreach my $dsrc ( @{ $graph->{datasrc} } ) { my %datasrc = %$dsrc; - while ( my ($key, $value) = each(%datasrc)) { - next if ($key eq 'name'); + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key eq 'name' ); + next + if ( ( $plugin eq 'evictions' ) + && ( !exists( $globalmap->{globalevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{globalcmds}->{ $dsrc->{name} } ) ) ); print "$dsrc->{name}.$key $value\n"; } } + return; +} + +=head2 print_submulti_config + + This subroutine prints out the config information for all of the multigraph subgraphs. + It takes three parameters, $slabid, $plugin and @subgraphs, returns when completed. + + $prefix; possible prefix used to allow multiple plugins per machine + $slabid; slab id that we will use to grab info from and print out + $plugin; root graph being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_config($prefix,$slabid,$plugin,@subgraphs); + +=cut + +sub print_submulti_config { + + # Lets get our slabid, plugin, and subgraphs + my ( $prefix, $slabid, $plugin, @subgraphs ) = (@_); + my ( $slabitems, $slabchnks ) = undef; + + # Time to loop over our subgraphs array + foreach my $sgraph (@subgraphs) { + # Lets set our graph reference, and main graph config for easy handling + my $graph = $graphs{$sgraph}; + my %graphconf = %{ $graph->{config} }; + +# Lets tell munin which graph we are graphing, and what our main graph config info is + if ($prefix) { + print + "multigraph $prefix\_memcached_multi_$plugin.$sgraph\_$slabid\n"; + } + else { + print "multigraph memcached_multi_$plugin.$sgraph\_$slabid\n"; + } + while ( my ( $key, $value ) = each(%graphconf) ) { + if ( $key eq 'title' ) { + if ($prefix) { + print "graph_$key " + . ucfirst($prefix) + . " $value" + . "$slabid" + . " ($chnks{$slabid}->{chunk_size} Bytes)\n"; + } + else { + print "graph_$key $value" + . "$slabid" + . " ($chnks{$slabid}->{chunk_size} Bytes)\n"; + } + } + elsif ( + ( $key eq 'vlabel' ) + && ( ( $sgraph eq 'slabevictime' ) + || ( $sgraph eq 'slabitemtime' ) ) + ) + { + $value = time_scale( 'config', $value ); + print "graph_$key $value\n"; + } + else { + print "graph_$key $value\n"; + } + } + + # Lets tell munin about our data values and how to treat them + foreach my $dsrc ( @{ $graph->{datasrc} } ) { + my %datasrc = %$dsrc; + while ( my ( $key, $value ) = each(%datasrc) ) { + next if ( $key eq 'name' ); + next + if ( ( $sgraph eq 'slabevics' ) + && ( !exists( $globalmap->{slabevics}->{ $dsrc->{name} } ) ) + ); + next + if ( ( $plugin eq 'commands' ) + && ( !exists( $globalmap->{slabcmds}->{ $dsrc->{name} } ) ) + ); + print "$dsrc->{name}.$key $value\n"; + } + } + } return; } -## -#### This subroutine actually performs the data fetch for us #### -#### These commands do not lock up Memcache at all #### -## +=head1 Misc Subroutines + + These subroutines are misc ones, and are referenced inside of the code. They + should never be called up by Munin. + +=head2 get_conn + + This subroutine returns a socket connection + +=cut + +sub get_conn { + my $s = undef; + + # check if we want to use sockets instead of tcp + my ($sock) = ( $host =~ /unix:\/\/(.+)*$/ ); + + if ($sock) { + $connection = "unix:\/\/$sock"; + $s = IO::Socket::UNIX->new( Peer => $sock ); + } + else { + $connection = "$host:$port"; + $s = IO::Socket::INET->new( + Proto => "tcp", + PeerAddr => $host, + PeerPort => $port, + Timeout => 10, + ); + } + return $s; +} + +=head2 fetch_stats + + This subroutine fetches the information from memcached and stores it into our + hashes for later referencing throughout the graph. Returns when completed + +=cut sub fetch_stats { - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - die "Error: Unable to Connect to $host\[:$port\]\n" unless $s; + # Lets try and connect to memcached + my $s = get_conn(); - print $s "stats\r\n"; + # Die if we can't establish a connection to memcached + die "Error: Unable to Connect to $connection\n" unless $s; - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { - my ($skey,$svalue) = ($1,$2); + # Lets print the stats command and store the info from the output + print $s "stats\r\n"; + while ( my $line = <$s> ) { + if ( $line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/ ) { + my ( $skey, $svalue ) = ( $1, $2 ); $stats{$skey} = $svalue; } last if $line =~ /^END/; } + # Lets print the stats settings command and store the info from the output print $s "stats settings\r\n"; - - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { - my ($skey,$svalue) = ($1,$2); + while ( my $line = <$s> ) { + if ( $line =~ /STAT\s(.+?)\s((\w|\d|\S)+)/ ) { + my ( $skey, $svalue ) = ( $1, $2 ); + if ( $skey eq 'evictions' ) { + $skey = 'evictions_active'; + } $stats{$skey} = $svalue; } last if $line =~ /^END/; } + # Lets print the stats slabs command and store the info from the output print $s "stats slabs\r\n"; - - while (my $line = <$s>) { - if ($line =~ /STAT\s(\d+):(.+)\s(\d+)/) { - my ($slabid,$slabkey,$slabvalue) = ($1,$2,$3); + while ( my $line = <$s> ) { + if ( $line =~ /STAT\s(\d+):(.+)\s(\d+)/ ) { + my ( $slabid, $slabkey, $slabvalue ) = ( $1, $2, $3 ); $chnks{$slabid}->{$slabkey} = $slabvalue; } last if $line =~ /^END/; } + # Lets print the stats items command and store the info from the output print $s "stats items\r\n"; - - while (my $line = <$s>) { - if ($line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/) { - my ($itemid,$itemkey,$itemvalue) = ($1,$2,$3); + while ( my $line = <$s> ) { + if ( $line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/ ) { + my ( $itemid, $itemkey, $itemvalue ) = ( $1, $2, $3 ); $items{$itemid}->{$itemkey} = $itemvalue; } last if $line =~ /^END/; } } -## -#### This subroutine is to help manage the time_scale settings for the graph -## +=head2 time_scale + + This subroutine is here for me to adjust the timescale of the time graphs + for last evicted item and age of eldest item in cache. + + Please note, after long usage I have noticed these counters may not + be accurate, I believe the developers are aware and have submitted + a patch upstream. + +=cut sub time_scale { - my ($configopt,$origvalue) = (@_); + + # Lets get our config option and value to adjust + my ( $configopt, $origvalue ) = (@_); my $value; - if ($configopt eq 'config') { - if ($timescale == 1) { + # If config is defined, it returns the config info for time scale + # If data is defined, it returns the original value after its been adjusted + if ( $configopt eq 'config' ) { + if ( $timescale == 1 ) { $value = "Seconds" . $origvalue; - } elsif ($timescale == 2) { + } + elsif ( $timescale == 2 ) { $value = "Minutes" . $origvalue; - } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) { + } + elsif (( $timescale == 3 ) + || ( $timescale > 4 ) + || ( !defined($timescale) ) ) + { $value = "Hours" . $origvalue; - } elsif ($timescale == 4) { + } + elsif ( $timescale == 4 ) { $value = "Days" . $origvalue; } - } elsif ($configopt eq 'data') { - if ($timescale == 1) { - $value = sprintf("%02.2f", $origvalue / 1); - } elsif ($timescale == 2) { - $value = sprintf("%02.2f", $origvalue / 60); - } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) { - $value = sprintf("%02.2f", $origvalue / 3600); - } elsif ($timescale == 4) { - $value = sprintf("%02.2f", $origvalue / 86400); - } - } else { + } + elsif ( $configopt eq 'data' ) { + if ( $timescale == 1 ) { + $value = sprintf( "%02.2f", $origvalue / 1 ); + } + elsif ( $timescale == 2 ) { + $value = sprintf( "%02.2f", $origvalue / 60 ); + } + elsif (( $timescale == 3 ) + || ( $timescale > 4 ) + || ( !defined($timescale) ) ) + { + $value = sprintf( "%02.2f", $origvalue / 3600 ); + } + elsif ( $timescale == 4 ) { + $value = sprintf( "%02.2f", $origvalue / 86400 ); + } + } + else { die "Unknown time_scale option given: either [config/data]\n"; } return $value; } + +=head2 buildglobalmap + + This subroutine looks at the specified commands inputted, and generates + a hashref containing two arrays, one for global command keys and one for + slab command keys. + +=cut + +sub buildglobalmap { + my $results; + my @cmds = split( ' ', $commands ); + foreach my $cmd (@cmds) { + if ( $cmd eq "get" ) { + $results->{globalcmds}->{cmd_get} = 1; + $results->{globalcmds}->{get_hits} = 1; + $results->{globalcmds}->{get_misses} = 1; + $results->{slabcmds}->{get_hits} = 1; + } + elsif ( $cmd eq "set" ) { + $results->{globalcmds}->{cmd_set} = 1; + $results->{slabcmds}->{cmd_set} = 1; + } + elsif ( $cmd eq "delete" ) { + $results->{globalcmds}->{delete_hits} = 1; + $results->{globalcmds}->{delete_misses} = 1; + $results->{slabcmds}->{delete_hits} = 1; + } + elsif ( $cmd eq "incr" ) { + $results->{globalcmds}->{incr_hits} = 1; + $results->{globalcmds}->{incr_misses} = 1; + $results->{slabcmds}->{incr_hits} = 1; + } + elsif ( $cmd eq "decr" ) { + $results->{globalcmds}->{decr_hits} = 1; + $results->{globalcmds}->{decr_misses} = 1; + $results->{slabcmds}->{decr_hits} = 1; + } + elsif ( $cmd eq "cas" ) { + $results->{globalcmds}->{cas_hits} = 1; + $results->{globalcmds}->{cas_misses} = 1; + $results->{globalcmds}->{cas_badval} = 1; + $results->{slabcmds}->{cas_hits} = 1; + $results->{slabcmds}->{cas_badval} = 1; + } + elsif ( $cmd eq "touch" ) { + if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) { + $results->{globalcmds}->{cmd_touch} = 1; + $results->{globalcmds}->{touch_hits} = 1; + $results->{globalcmds}->{touch_misses} = 1; + $results->{slabcmds}->{touch_hits} = 1; + } + } + elsif ( $cmd eq "flush" ) { + if ( $stats{version} !~ /^1\.4\.[0-7]$/ ) { + $results->{globalcmds}->{cmd_flush} = 1; + } + } + else { + + # Do absolutely nothing... + } + } + $results->{globalevics}->{evictions} = 1; + $results->{globalevics}->{evicted_nonzero} = 1; + $results->{slabevics}->{evicted} = 1; + $results->{slabevics}->{evicted_nonzero} = 1; + if ( $stats{version} !~ /^1\.4\.[0-2]$/ ) { + $results->{globalevics}->{reclaimed} = 1; + $results->{slabevics}->{reclaimed} = 1; + } + return $results; +}