+#!/usr/bin/perl
+#
+=head1 NAME
+
+Memcached - A Plugin to monitor Memcached Servers (Multigraph)
+
+=head1 MUNIN CONFIGURATION
+
+[memcached_*]
+ env.host 127.0.0.1 *default*
+ env.port 11211 *default*
+ env.timescale 3 *default*
+
+=head2 MUNIN ENVIRONMENT CONFIGURATION EXPLANATION
+
+ host = host we are going to monitor
+ port = port we are connecting to, in order to gather stats
+ timescale = what time frame do we want to format our graphs too
+
+=head1 NODE CONFIGURATION
+
+Please make sure you can telnet to your memcache servers and issue the
+ following commands: stats, stats settings, stats items and stats slabs.
+
+Available Graphs contained in this Plugin
+
+bytes => This graphs the current network traffic in and out
+
+commands => I<MULTIGRAPH> This graphs the current commands being issued to the memcache machine. B<Multigraph breaks this down to per slab.>
+
+conns => This graphs the current, max connections as well as avg conns per sec avg conns per sec is derived from total_conns / uptime.
+
+evictions => I<MULTIGRAPH> This graphs the current evictions on the node. B<Multigraph breaks this down to per slab.>
+
+items => I<MULTIGRAPH> This graphs the current items and total items in the memcached node. B<Multigraph breaks this down to per slab.>
+
+memory => I<MULTIGRAPH> This graphs the current and max memory allocation B<Multigraph breaks this down to per slab.>
+
+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
+
+=head1 ADDITIONAL INFORMATION
+
+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<Last Evicted Item>.
+
+The B<Timescale> variable formats certain graphs based on the following guidelines.
+ 1 => Seconds
+ 2 => Minutes
+ 3 => Hours B<*Default*>
+ 4 => Days
+
+=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 ;)
+
+=head1 AUTHOR
+
+Matt West < https://code.google.com/p/memcached-munin-plugin/ >
+
+=head1 LICENSE
+
+GPLv2
+
+=head1 MAGIC MARKERS
+
+ #%# family=auto
+ #%# capabilities=autoconf suggest
+
+=cut
+
+use strict;
+use IO::Socket;
+use Munin::Plugin;
+
+need_multigraph();
+
+my $host = $ENV{host} || "127.0.0.1";
+my $port = $ENV{port} || 11211;
+
+my %stats;
+# This hash contains the information contained in two memcache commands
+# stats and stats settings.
+
+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 %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
+# so we can re-tune memcached to allocate more pages for the specified chunk size
+
+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
+
+# 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;
+
+$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',
+ },
+ datasrc => [
+ { name => 'curr_items', label => 'Current Items', min => '0' },
+ { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' },
+ ],
+};
+
+$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',
+ },
+ datasrc => [
+ { name => 'limit_maxbytes', draw => 'AREA', label => 'Maximum Bytes Allocated', min => '0' },
+ { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' },
+ ],
+};
+
+$graphs{bytes} = {
+ config => {
+ 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',
+ },
+ 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' },
+ ],
+};
+
+$graphs{conns} = {
+ config => {
+ 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',
+ },
+ 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' },
+ ],
+};
+
+$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',
+ },
+ 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' },
+ ],
+};
+
+$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',
+ },
+ 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' },
+ ],
+};
+
+$graphs{slabchnks} = {
+ config => {
+ args => '--base 1000 --lower-limit 0',
+ vlabel => 'Available Chunks for this Slab',
+ category => 'memcached',
+ 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 => 'used_chunks', label => 'Total Chunks in Use', min => '0' },
+ { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' },
+ ],
+};
+
+$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.',
+ },
+ 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' },
+ ],
+};
+
+$graphs{slabevics} = {
+ config => {
+ args => '--base 1000 --lower-limit 0',
+ vlabel => 'Evictions per Slab per ${graph_period}',
+ category => 'memcached',
+ 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' },
+ ],
+};
+
+$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',
+ },
+ datasrc => [
+ { name => 'evicted_time', label => 'Eviction Time (LEI)', info => 'Time Since Request for Last Evicted Item', min => '0' },
+ ],
+};
+
+$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.',
+ },
+ datasrc => [
+ { name => 'number', label => 'Items', info => 'This is the amount of items stored in this slab', min => '0' },
+ ],
+};
+
+$graphs{slabitemtime} = {
+ config => {
+ args => '--base 1000 --lower-limit 0',
+ vlabel => ' since item was stored',
+ category => 'memcached',
+ 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' },
+ ],
+};
+
+##
+#### Config Check ####
+##
+
+if (defined $ARGV[0] && $ARGV[0] eq 'config') {
+
+ $0 =~ /memcached_multi_(.+)*/;
+ my $plugin = $1;
+
+ 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
+ fetch_stats();
+
+ # Now lets go ahead and print out our config.
+ do_config($plugin);
+ exit 0;
+}
+
+##
+#### Autoconf Check ####
+##
+
+if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') {
+
+ my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $host,
+ PeerPort => $port,
+ );
+
+ if (defined($s)) {
+ print "yes\n";
+ exit 0;
+ } else {
+ print "no (unable to connect to $host\[:$port\])\n";
+ exit 0;
+ }
+}
+
+##
+#### Suggest Check ####
+##
+
+if (defined $ARGV[0] && $ARGV[0] eq 'suggest') {
+
+ my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $host,
+ PeerPort => $port,
+ );
+
+ if (defined($s)) {
+ my @rootplugins = ('bytes','conns','commands','evictions','items','memory');
+ foreach my $plugin (@rootplugins) {
+ print "$plugin\n";
+ }
+ exit 0;
+ } else {
+ print "no (unable to connect to $host\[:$port\])\n";
+ exit 0;
+ }
+}
+
+##
+#### Well We aren't running (auto)config/suggest so lets print some stats ####
+##
+
+fetch_output();
+
+##
+#### Subroutines for printing info gathered from memcached ####
+##
+
+##
+#### This subroutine performs the bulk processing for printing statistics.
+##
+
+sub fetch_output {
+
+ $0 =~ /memcached_multi_(.+)*/;
+ my $plugin = $1;
+
+ die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$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') {
+ @subgraphs = ('slabchnks');
+ foreach my $slabid(sort{$a <=> $b} keys %chnks) {
+ print_submulti_output($slabid,$plugin,@subgraphs);
+ }
+ print_rootmulti_output($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 {
+ print_root_output($plugin);
+ }
+
+ return;
+}
+
+##
+#### This subroutine is for the root non-multigraph graphs which render on the main node page ####
+##
+
+sub print_root_output {
+ my ($plugin) = (@_);
+
+ my $graph = $graphs{$plugin};
+
+ print "graph memcached_$plugin\n";
+
+ if ($plugin ne 'conns') {
+ foreach my $dsrc (@{$graph->{datasrc}}) {
+ my %datasrc = %$dsrc;
+ while ( my ($key, $value) = each(%datasrc)) {
+ next if ($key ne 'name');
+ my $output = $stats{$value};
+ print "$dsrc->{name}.value $output\n";
+ }
+ }
+ } else {
+ my $output;
+ foreach my $dsrc (@{$graph->{datasrc}}) {
+ my %datasrc = %$dsrc;
+ while ( my ($key, $value) = each(%datasrc)) {
+ if ($value eq 'max_conns') {
+ $output = $stats{maxconns};
+ } elsif ($value eq 'curr_conns') {
+ $output = $stats{curr_connections};
+ } 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 ####
+##
+
+sub print_rootmulti_output {
+ my ($plugin) = (@_);
+
+ my $graph = $graphs{$plugin};
+
+ print "multigraph memcached_$plugin\n";
+
+ 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) {
+ $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 ####
+##
+
+sub print_submulti_output {
+ my ($slabid,$plugin,@subgraphs) = (@_);
+ my $currslab = undef;
+
+ foreach my $sgraph (@subgraphs) {
+
+ my $graph = $graphs{$sgraph};
+
+ print "multigraph memcached_$plugin.$sgraph\_$slabid\n";
+
+ if ($plugin eq 'evictions') {
+ $currslab = $items{$slabid};
+ } elsif ($plugin eq 'memory') {
+ $currslab = $chnks{$slabid};
+ } elsif ($plugin eq 'commands') {
+ $currslab = $chnks{$slabid};
+ } elsif ($plugin eq 'items') {
+ $currslab = $items{$slabid};
+ } else {
+ return;
+ }
+
+ foreach my $dsrc (@{$graph->{datasrc}}) {
+ my %datasrc = %$dsrc;
+ while ( my ($key, $value) = each(%datasrc)) {
+ next if ($key ne 'name');
+ my $output = $currslab->{$value};
+ 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 ####
+##
+
+##
+#### This subroutine does the bulk printing the config info per graph ####
+##
+
+sub do_config {
+ my ($plugin) = (@_);
+ my @subgraphs;
+ if ($plugin eq 'memory') {
+ @subgraphs = ('slabchnks');
+ foreach my $slabid (sort{$a <=> $b} keys %chnks) {
+ print_submulti_config($slabid,$plugin,@subgraphs);
+ }
+ print_rootmulti_config($plugin);
+ } elsif ($plugin eq 'commands') {
+ @subgraphs = ('slabhits');
+ foreach my $slabid (sort{$a <=> $b} keys %chnks) {
+ print_submulti_config($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_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_rootmulti_config($plugin);
+ } else {
+ print_root_config($plugin);
+ }
+
+ return;
+}
+
+##
+#### This subroutine is for the config info for sub multigraph graphs created via the multigraph plugin ####
+##
+
+sub print_submulti_config {
+ my ($slabid,$plugin,@subgraphs) = (@_);
+ my ($slabitems,$slabchnks) = undef;
+
+ foreach my $sgraph (@subgraphs) {
+
+ my $graph = $graphs{$sgraph};
+
+ my %graphconf = %{$graph->{config}};
+
+ print "multigraph memcached_$plugin.$sgraph\_$slabid\n";
+
+ 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";
+ }
+ }
+
+ 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 ####
+##
+
+sub print_rootmulti_config {
+ my ($plugin) = (@_);
+
+ die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
+
+ my $graph = $graphs{$plugin};
+
+ my %graphconf = %{$graph->{config}};
+
+ print "multigraph memcached_$plugin\n";
+
+ while ( my ($key, $value) = each(%graphconf)) {
+ print "graph_$key $value\n";
+ }
+
+ 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 non multigraph graphs which render on the main node page ####
+##
+
+sub print_root_config {
+ my ($plugin) = (@_);
+
+ die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin};
+
+ my $graph = $graphs{$plugin};
+
+ my %graphconf = %{$graph->{config}};
+
+ print "graph memcached_$plugin\n";
+
+ while ( my ($key, $value) = each(%graphconf)) {
+ print "graph_$key $value\n";
+ }
+
+ 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 actually performs the data fetch for us ####
+#### These commands do not lock up Memcache at all ####
+##
+
+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;
+
+ print $s "stats\r\n";
+
+ while (my $line = <$s>) {
+ if ($line =~ /STAT\s(.+?)\s(\d+)/) {
+ my ($skey,$svalue) = ($1,$2);
+ $stats{$skey} = $svalue;
+ }
+ last if $line =~ /^END/;
+ }
+
+ print $s "stats settings\r\n";
+
+ while (my $line = <$s>) {
+ if ($line =~ /STAT\s(.+?)\s(\d+)/) {
+ my ($skey,$svalue) = ($1,$2);
+ $stats{$skey} = $svalue;
+ }
+ last if $line =~ /^END/;
+ }
+
+ print $s "stats slabs\r\n";
+
+ 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/;
+ }
+
+ print $s "stats items\r\n";
+
+ 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
+##
+
+sub time_scale {
+ my ($configopt,$origvalue) = (@_);
+ my $value;
+
+ if ($configopt eq 'config') {
+ if ($timescale == 1) {
+ $value = "Seconds" . $origvalue;
+ } elsif ($timescale == 2) {
+ $value = "Minutes" . $origvalue;
+ } elsif (($timescale == 3) || ($timescale > 4) || (!defined($timescale))) {
+ $value = "Hours" . $origvalue;
+ } 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 {
+ die "Unknown time_scale option given: either [config/data]\n";
+ }
+ return $value;
+}