Your IP : 3.145.17.18
Current Path : /sbin/ |
|
Current File : //sbin/munin-asyncd |
#! /usr/bin/perl
# -*- cperl -*-
#
# Copyright (C) 2010 Steve Schnepp
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
use strict;
use warnings;
use IO::Socket;
use IO::File;
use File::Path qw(mkpath);
use Getopt::Long;
use Pod::Usage;
use List::Util qw(min max);
use Munin::Common::Daemon;
use Munin::Node::SpoolWriter;
my $host = "localhost:4949";
my $metahostname;
my $SPOOLDIR = "/var/lib/munin/spool";
my $intervalsize = 86400;
my $timeout = 3600;
my $minrate = 300;
my $retaincount = 7;
# Asyncd collects data from plugins periodically. We want to avoid skipping the next value due to
# slight deviations of this period. Thus we tolerate a slightly shorter time distance between two
# successive requests for values from a plugin.
my $min_update_rate_threshold = 0.75;
my $nocleanup;
my $do_fork;
my $verbose;
my $debug;
my $help;
GetOptions(
"host=s" => \$host,
"spooldir|s=s" => \$SPOOLDIR,
"interval|i=i" => \$intervalsize,
"timeout=i" => \$timeout,
"minrate=i" => \$minrate,
"retain|r=i" => \$retaincount,
"fork" => \$do_fork,
"help|h" => \$help,
"verbose|v" => \$verbose,
"nocleanup|n" => \$nocleanup,
"debug" => \$debug,
) or pod2usage(1);
if ($help) {
pod2usage(1);
}
# Debug implies Verbose
$verbose = 1 if $debug;
unless (-d $SPOOLDIR) {
mkpath($SPOOLDIR, { verbose => $verbose, } )
or die ("Cannot create '$SPOOLDIR': $!");
}
my $process_name = "main";
my $sock = new IO::Socket::INET(
PeerAddr => "$host",
Proto => 'tcp'
);
if (!$sock) {
print STDERR "[$$][$process_name] Failed to connect to munin-node - trying again in a few seconds ...\n" if $verbose;
sleep 20;
$sock = new IO::Socket::INET(
PeerAddr => "$host",
Proto => 'tcp'
) || die "Error connecting to munin node ($host): $!";
}
my $nodeheader = <$sock>;
print $sock "quit\n";
close ($sock);
( $metahostname ) = ( $nodeheader =~ /munin node at (\S+)\n/);
$metahostname = "unknown" unless $metahostname;
my $spoolwriter = Munin::Node::SpoolWriter->new(
spooldir => $SPOOLDIR,
interval_size => $intervalsize,
interval_keep => $retaincount,
hostname => $metahostname,
);
$0 = "munin-asyncd [$metahostname] [idle]";
my @plugins;
{
print STDERR "[$$][$process_name] Reading config from $host\n" if $verbose;
my $sock = new IO::Socket::INET(
PeerAddr => "$host",
Proto => 'tcp'
) || die "Error creating socket: $!";
local $0 = "munin-asyncd [$metahostname] [list]";
print STDERR "[sock][>] cap multigraph\n" if $debug;
print $sock "cap multigraph\n";
print STDERR "[sock][>] list\n" if $debug;
print $sock "list\n";
<$sock>; # Read the first header comment line
<$sock>; # Read the multigraph response line
my $plugins_line = <$sock>;
chomp($plugins_line);
{
my $fh_list = IO::File->new(
"$SPOOLDIR/munin-daemon.list",
"w",
);
my $sanitised_plugins_line = $plugins_line;
$sanitised_plugins_line =~ s/[^_A-Za-z0-9 ]/_/g;
print $fh_list $sanitised_plugins_line;
print $fh_list "\n";
}
@plugins = split(/ /, $plugins_line);
}
my $keepgoing = 1;
sub termhandler() {
$keepgoing = 0;
}
# Q&D child collection
$SIG{CHLD} = 'IGNORE';
$SIG{HUP} = 'IGNORE';
$SIG{INT} = 'termhandler';
$SIG{TERM} = 'termhandler';
# now, update regularly...
# ... but each plugin in its own process to avoid delay-leaking
my %last_updated;
my $last_cleanup=0;
# all preparations are finished: we can notify our caller, that we are ready
Munin::Common::Daemon::emit_sd_notify_message();
MAIN: while($keepgoing) {
my $when = time;
# start the next run close to the end of a munin-node update operation
# (i.e. try to avoid overlapping activities)
my $when_next = int((int($when / $minrate) + 0.75) * $minrate);
while ($when_next <= $when) {
$when_next = $when_next + $minrate;
}
my $sock;
PLUGIN: foreach my $plugin (@plugins) {
# See if this plugin should be updated
my $plugin_rate = $spoolwriter->get_metadata("plugin_rates/$plugin") || 300;
if ($when < ($last_updated{$plugin} || 0) + ($plugin_rate * $min_update_rate_threshold)) {
# not yet, next plugin
next;
}
# Should update it
$last_updated{$plugin} = $when;
if ($do_fork && fork()) {
# parent, return directly
next PLUGIN;
}
unless ($sock) {
$sock = new IO::Socket::INET(
PeerAddr => "$host",
Proto => 'tcp'
);
unless ($sock) {
if ($do_fork) {
die "Error creating socket: $!";
} else {
warn "Error creating socket: $!, moving to next plugin to try again";
next;
}
}
<$sock>; # skip header
}
# Setting the command name for a useful top information
$process_name = "plugin:$plugin";
local $0 = "munin-asyncd [$metahostname] [$process_name]";
fetch_data($plugin, $when, $sock);
# We end here if we forked
last MAIN if $do_fork;
}
print STDERR "[$$][$process_name][>] quit\n" if $verbose;
print $sock "quit\n" if $sock;
print STDERR "[$$][$process_name] closing sock\n" if $verbose;
$sock = undef;
$spoolwriter->set_metadata("lastruntime", $when);
# Clean spool dir
if (!$nocleanup && $last_cleanup<(time - 600)) {
$last_cleanup = time;
$spoolwriter->cleanup();
}
# Sleep until next plugin exec.
my $sleep_sec = $when_next - time;
# "sleep" expects an unsigned integer - thus we may not let a wrapped number splip through.
if ($sleep_sec > 0) {
print STDERR "[$$][$process_name] Sleeping $sleep_sec sec\n" if $verbose;
sleep $sleep_sec;
} else {
print STDERR "[$$][$process_name] Already late : should sleep $sleep_sec sec\n" if $verbose;
}
}
print STDERR "[$$][$process_name] Exiting\n" if $verbose;
sub fetch_data
{
my $plugin = shift;
my $when = shift;
my $sock = shift;
print STDERR "[$$][$process_name][>][$plugin] asking for config\n" if $verbose;
print STDERR "[sock][>][$plugin] config $plugin\n" if $debug;
print $sock "config $plugin\n";
my $output_rows = [];
while(my $line = <$sock>) {
chomp($line);
print STDERR "[sock][<][$plugin] $line\n" if $debug;
if ($line =~ m/^\./) {
# Starting with . => end
last;
}
push @$output_rows, $line;
if ($line =~ m/^update_rate (\d+)/) {
# The plugin has a special update_rate: overriding it
# XXX - Doesn't take into account a per field update_rate
# This has to be sent back to the master
$spoolwriter->set_metadata("plugin_rates/$plugin", $1);
}
}
print STDERR "[$$][$process_name][>][$plugin] asking for data\n" if $verbose;
print STDERR "[sock][>][$plugin] fetch $plugin\n" if $debug;
print $sock "fetch $plugin\n";
while(my $line = <$sock>) {
chomp($line);
print STDERR "[sock][<][$plugin] $line\n" if $debug;
if ($line =~ m/^\./) {
# Starting with . => end
last;
}
# Save the line
push @$output_rows, $line;
}
# Write the whole load into the spool
$spoolwriter->write($when, $plugin, $output_rows);
}
__END__
=head1 NAME
munin-asyncd - A program to spool munin-node calls
=head1 SYNOPSIS
munin-asyncd [options]
Options:
--host <hostname:port> Connect to this munin-node [localhost:4949]
-s --spool <spooldir> Store the spooled data in this dir [/var/lib/munin-async]
-i --interval <seconds> Override default interval size of one day [86400]
--timeout <seconds> Wake up at least this number of seconds. [3600]
--minrate <seconds> This is the minimal rate you want to poll a node [300]
-r --retain <count> Specify number of interval files to retain [7]
-n --nocleanup Disable automated spool dir cleanup
--fork Do fork
-v --verbose Be verbose
-h --help View this message