File: //proc/self/root/proc/self/root/usr/sbin/munin-node-configure
#!/usr/bin/perl -w
# -*- cperl -*-
#
# Copyright (C) 2003-2006 Jimmy Olsen, Nicolai Langfeldt.
#
# 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.
#
# Program to suggest what plugins to use and not
use strict;
use warnings;
use Getopt::Long;
use POSIX ();
use Data::Dumper;
use Carp;
use Munin::Common::Defaults;
use Munin::Node::Configure::PluginList;
use Munin::Node::Configure::Debug;
use Munin::Node::Config;
my $config = Munin::Node::Config->instance();
my $timeout = 10;
my @all_families = qw/auto manual contrib/;
my @default_families = qw/auto/;
sub main
{
parse_args();
my $plugins = Munin::Node::Configure::PluginList->new(
libdir => $config->{libdir},
servicedir => $config->{servicedir},
);
$plugins->load(@{$config->{families}});
# Gather information
if (@{ $config->{snmp} }) {
my $snmp = init_snmp($plugins);
$snmp->run_probes($plugins);
}
elsif ($config->{suggest}) {
gather_suggestions($plugins);
}
else {
print_status_list($plugins);
exit 0;
}
# Display results.
if ($config->{shell}) {
manage_links($plugins);
}
else {
show_suggestions($plugins);
}
# Report errors.
# FIXME: surely exit 0 unless m-n-c itself has gone funny?
if (my @errors = list_errors($plugins->list)) {
print STDERR "# The following plugins caused errors:\n";
foreach my $err (@errors) {
print STDERR "# $err\n";
}
exit 1;
}
exit 0;
}
sub parse_args
{
my $conffile = "$Munin::Common::Defaults::MUNIN_CONFDIR/munin-node.conf";
my $servicedir = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugins";
my $sconfdir = "$Munin::Common::Defaults::MUNIN_CONFDIR/plugin-conf.d";
my $libdir = "$Munin::Common::Defaults::MUNIN_LIBDIR/plugins";
my $debug;
my ($suggest, $shell, $removes, $newer);
my $exit_not_error = 1;
my @families;
my (@snmp_hosts, $snmpver, $snmpcomm, $snmpport);
print_usage_and_exit() unless GetOptions(
"help" => \&print_usage_and_exit,
"shell!" => \$shell,
"exitnoterror!" => \$exit_not_error,
"debug!" => \$debug,
"suggest!" => \$suggest,
"config=s" => \$conffile,
"servicedir=s" => \$servicedir,
"sconfdir=s" => \$sconfdir,
"remove-also!" => \$removes,
"libdir=s" => \$libdir,
"families=s" => \@families,
"version!" => \&print_version_and_exit,
"snmp=s" => \@snmp_hosts,
"snmpversion=s" => \$snmpver,
"snmpcommunity=s" => \$snmpcomm,
"snmpport=i" => \$snmpport,
"newer=s" => \$newer
);
$config->parse_config_from_file($conffile);
# --shell implies --suggest unless --snmp was also used
$suggest = 1 if ($shell and not @snmp_hosts);
@families = (@families) ? map { split /,/ } @families :
(@snmp_hosts) ? ('snmpauto') :
($suggest) ? @default_families :
@all_families ;
# Allow the user to mix multiple invocations of --snmp with the
# comma-delimited form
@snmp_hosts = map { split /,/ } @snmp_hosts;
$config->reinitialize({
timeout => $timeout,
%$config,
newer => $newer,
shell => $shell,
exit_not_error => $exit_not_error,
suggest => $suggest,
remove_also => $removes,
families => \@families,
conffile => $conffile,
servicedir => $servicedir,
sconfdir => $sconfdir,
libdir => $libdir,
snmp => \@snmp_hosts,
snmp_version => $snmpver,
snmp_community => $snmpcomm,
snmp_port => $snmpport,
DEBUG => $debug,
});
return;
}
sub print_usage_and_exit
{
my $all_families = join(', ', @all_families);
my $default_families = join(', ', @default_families);
print qq{Usage: $0 [options]
Options:
--help View this help page
--version Show version information
--debug View debug information (very verbose). All
debugging output is printed on STDOUT but each
line is prefixed with '#'. Only errors are
printed on STDERR.
--config <file> Override configuration file
[$Munin::Common::Defaults::MUNIN_CONFDIR/munin.conf]
--servicedir <dir> Override plugin directory
[$Munin::Common::Defaults::MUNIN_CONFDIR/plugins/]
--sconfdir <dir> Override plugin configuration directory
[$Munin::Common::Defaults::MUNIN_CONFDIR/plugin-conf.d]
--libdir <dir> Override plugin lib
[$Munin::Common::Defaults::MUNIN_LIBDIR/plugins/]
--families <family,...> Override families ($all_families) [$default_families]
--suggest Show suggestions instead of status
--shell Show shell commands (implies --suggest)
--exitnoterror Do not consider non-zero exit-value as error
--remove-also Also show rm-commands when doing --shell
--newer <version> Only show suggestions related to plugins
included more recently than version <version>.
Only supported along with --shell.
--snmp <host|cidr> Do SNMP probing on the host or CIDR network
(e.g. "192.168.1.0/24"). This may take some
time, especially if the probe includes many
hosts. This option can be specified multiple
times, or once with a comma-separated list, to
include more than one host/CIDR.
--snmpversion <ver> Set SNMP version (1, 2c or 3) [2c]
--snmpcommunity <comm> Set SNMP community string [public]
--snmpport <port> Set SNMP port [161]
By default this program shows which plugins are activated on the system.
If you specify --suggest, it will present a table of plugins that will
probably work (according to the plugins' autoconf command).
If you specify --shell, shell commands to install those same plugins
will be printed. These can be reviewed or piped directly into a shell to
install the plugins.
};
exit 0;
}
sub print_version_and_exit
{
print qq{munin-node-configure (munin-node) version $Munin::Common::Defaults::MUNIN_VERSION.
Written by Jimmy Olsen
Copyright (C) 2003-2006 Jimmy Olsen
This is free software released under the GNU General Public License. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. For details, please refer to the file COPYING that is included
with this software or refer to
http://www.fsf.org/licensing/licenses/gpl.txt
};
exit 0;
}
sub print_table_entry
{
printf "%-26s | %-4s | %-39s\n", @_;
return;
}
### Reporting current status ###################################################
# For each available plugin, prints a line detailing whether or not it's
# installed, and (if it's a wildcard plugin) what identities are currently
# in use
sub print_status_list
{
my ($plugins) = @_;
print_table_entry("Plugin", "Used", "Extra information");
print_table_entry("------", "----", "-----------------");
foreach my $plugin ($plugins->list) {
print_table_entry(
$plugin->{name},
$plugin->is_installed,
$plugin->installed_services_string
);
}
return;
}
### Reporting and managing suggestions #########################################
# Asks each available autoconf plugin whether or not it should be installed,
# and (if it's a wildcard plugin) its suggested profiles.
sub gather_suggestions
{
my ($plugins) = @_;
# We're going to be running plugins
Munin::Node::Service->prepare_plugin_environment($plugins->names);
foreach my $plugin ($plugins->list) {
fetch_plugin_autoconf($plugin);
fetch_plugin_suggestions($plugin);
}
return;
}
# Prints out the tabular representation of the suggestion
sub show_suggestions
{
my ($plugins) = @_;
print_table_entry("Plugin", "Used", "Suggestions");
print_table_entry("------", "----", "-----------");
foreach my $plugin ($plugins->list) {
print_table_entry(
$plugin->{name},
$plugin->is_installed,
$plugin->suggestion_string
);
}
return;
}
# prints shell commands to get the system into the recommended
# state by adding or removing symlinks
sub manage_links
{
my ($plugins) = @_;
foreach my $plugin ($plugins->list) {
link_add($plugin->{path}, $_) foreach $plugin->services_to_add;
if ($config->{remove_also}) {
link_remove($_) foreach $plugin->services_to_remove;
}
}
return;
}
# Prints a shell-command to remove a given symlink from the servicedir
sub link_remove
{
my ($service) = @_;
return unless (-l "$config->{servicedir}/$service"); # Strange...
print "rm -f '$config->{servicedir}/$service'\n";
return;
}
# Prints a shell-command to add a symlink called $service pointing to
# the plugin.
sub link_add
{
my ($plugin, $service) = @_;
print "ln -s '$plugin' '$config->{servicedir}/$service'\n";
return;
}
### SNMP probing ###############################################################
# Prepares for SNMP probing
sub init_snmp
{
my ($plugins) = @_;
unless (eval { require Munin::Node::SNMPConfig; }) {
die "# ERROR: Cannot perform SNMP probing since Munin::Node::SNMPConfig module is not available.\n",
$@;
}
Munin::Node::Service->prepare_plugin_environment($plugins->names);
fetch_plugin_snmpconf($_) foreach ($plugins->list);
return Munin::Node::SNMPConfig->new(
hosts => $config->{snmp},
community => $config->{snmp_community},
version => $config->{snmp_version},
port => $config->{snmp_port},
);
}
### Running plugins and analysing responses ####################################
# Runs the plugin with argument $mode (eg. 'suggest', 'autoconf') and runs
# tests on the results. Assuming no errors were detected, returns a list
# of the lines printed to STDOUT, with any debug output removed.
sub run_plugin
{
my ($plugin, $mode) = @_;
my $name = $plugin->{name};
DEBUG("Running '$mode' on $name" );
my $res = Munin::Node::Service->fork_service($config->{libdir},
$name, $mode);
# No if it timed out
if ($res->{timed_out}) {
$plugin->log_error("Timed out during $mode");
return;
}
elsif ($res->{retval}) {
# Non-zero exit is an immediate fail
my $plugin_exit = $res->{retval} >> 8;
my $plugin_signal = $res->{retval} & 127;
# Definitely a bad sign
if ($plugin_signal) {
$plugin->log_error("Died with signal $plugin_signal during $mode");
return;
}
elsif ($plugin_exit) {
$plugin->log_error("Non-zero exit during $mode ($plugin_exit)");
# Verboten according to the specification, but lots of plugins
# still exit(1) it during autoconf. So making it a non-fatal error
# for the time being
return unless ($mode eq 'autoconf'
and $plugin_exit == 1
and $config->{exit_not_error});
}
}
# No if there is anything on stderr that's not debug
if (grep !/^#/, @{ $res->{stderr} }) {
$plugin->log_error("Junk printed to stderr");
# FIXME: log the other output
return;
}
# Ignore debug output
my @response = grep !/^#/, @{ $res->{stdout} };
$plugin->log_error('Nothing printed to stdout') unless (scalar @response);
return @response;
}
# Runs the given plugin, and records whether it thinks it should be installed.
# Sets the 'default' and 'defaultreason' fields
sub fetch_plugin_autoconf
{
my ($plugin) = @_;
return unless ($plugin->{capabilities}{autoconf});
my @response = run_plugin($plugin, 'autoconf') or return;
return $plugin->parse_autoconf_response(@response);
}
# Runs the given wildcard plugin and saves a list of suggested profiles
# in the 'suggestions' field
sub fetch_plugin_suggestions
{
my ($plugin) = @_;
# Only run if the autoconf gave the go-ahead
return unless ($plugin->{default} eq "yes");
return unless ($plugin->{capabilities}{suggest});
my @suggested = run_plugin($plugin, 'suggest');
return $plugin->parse_suggest_response(@suggested);
}
# Runs a given snmpconf-capable plugin, and notes the parameters it returns
sub fetch_plugin_snmpconf
{
my ($plugin) = @_;
return unless ($plugin->{capabilities}{snmpconf});
my @response = run_plugin($plugin, 'snmpconf');
return $plugin->parse_snmpconf_response(@response);
}
### Debugging and error reporting ##############################################
sub list_errors
{
my @error_list;
foreach my $plugin (@_) {
if (my @errors = @{$plugin->{errors}}) {
push @error_list, "$plugin->{name}:";
push @error_list, map { "\t$_" } @errors;
}
}
return @error_list;
}
exit main() unless caller;
1;
__END__
=head1 NAME
munin-node-configure - View and modify which plugins are enabled.
=head1 SYNOPSIS
munin-node-configure [options]
=head1 OPTIONS
=over 5
=item B<< --help >>
View this help page
=item B<< --version >>
Show version information
=item B<< --debug >>
Print debug information (very verbose). All debugging output is
printed on STDOUT but each line is prefixed with '#'. Only errors are
printed on STDERR.
=item B<< --config <file> >>
Override configuration file [@@CONFDIR@@/munin-node.conf]
=item B<< --servicedir <dir> >>
Override plugin dir [@@CONFDIR@@/plugins/]
=item B<< --sconfdir <dir> >>
Override plugin configuration directory [@@CONFDIR@@/plugin-conf.d/]
=item B<< --libdir <dir> >>
Override plugin lib [@@LIBDIR@@/plugins/]
=item B<< --families <family,...> >>
Override families (auto, manual, contrib) [auto]
=item B<< --suggest >>
Show suggestions instead of status
=item B<< --shell >>
Show shell commands instead of a table (implies --suggest unless --snmp was
also provided)
=item B<< --exitnoterror >>
Do not consider non-zero exit-value as error
=item B<< --remove-also >>
Also show rm-commands when doing --shell
=item B<< --newer <version> >>
Only show suggestions related to plugins included more recently than
version <version>.
=item B<< --snmp <host|cidr,...> >>
Do SNMP probing on the host or CIDR network (e.g. "192.168.1.0/24"). This may
take some time, especially if the probe includes many hosts. This option can
be specified multiple times, or as a comma-separated list, to include more
than one host/CIDR.
=item B<< --snmpversion <ver> >>
Set the SNMP version (1, 2c or 3) to use when probing. Default is "2c".
=item B<< --snmpcommunity <comm> >>
Set SNMP community string to use when probing. Default is "public".
=item B<< --snmpport <port> >>
Set SNMP port. Default is "161".
=back
=head1 DESCRIPTION
munin-node-configure is a script to show which plugins are currently enabled
on the current node. It can also suggest changes to this list.
=head1 FILES
@@CONFDIR@@/munin-node.conf
@@CONFDIR@@/plugin-conf.d/*
@@CONFDIR@@/plugins/*
@@LIBDIR@@/plugins/plugins.history
@@LIBDIR@@/plugins/*
=head1 VERSION
This is munin-node-configure v@@VERSION@@.
=head1 AUTHORS
Jimmy Olsen, Nicolai Langfeldt
=head1 BUGS
Please see L<http://munin.projects.linpro.no/report/1>.
=head1 COPYRIGHT
Copyright (C) 2003-2006 Jimmy Olsen, Nicolai Langfeldt.
This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
This program is released under the GNU General Public License
=cut
# vim: sw=4 : ts=4 : expandtab