Current Path : /usr/sbin/ |
Current File : //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. # use strict; use warnings; use Getopt::Long; 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 @all_families = qw/auto manual contrib/; my @default_families = qw/auto/; sub main { parse_args(); my $plugins = $config->{plugins}; $plugins->load(); # Gather information if ($config->{snmp}) { $plugins->{library}->prepare_plugin_environment($plugins->names); fetch_plugin_snmpconf($plugins, $_) foreach ($plugins->list); $config->{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", @errors; 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 = 0; my $pidebug = 0; my ($suggest, $shell, $removes, $newer); my @families; my (@snmp_hosts, $snmpver, $snmpcomm, $snmpport, $snmpdomain); my ($snmp3username, $snmp3authpass, $snmp3authproto, $snmp3privpass, $snmp3privproto); print_usage_and_exit() unless GetOptions( 'help' => \&print_usage_and_exit, 'version' => \&print_version_and_exit, 'suggest!' => \$suggest, 'newer=s' => \$newer, 'shell!' => \$shell, 'remove-also!' => \$removes, 'debug!' => \$debug, 'pidebug!' => \$pidebug, # paths 'config=s' => \$conffile, 'servicedir=s' => \$servicedir, 'sconfdir=s' => \$sconfdir, 'libdir=s' => \$libdir, 'families=s' => \@families, # SNMP 'snmp=s' => \@snmp_hosts, 'snmpversion=s' => \$snmpver, 'snmpport=i' => \$snmpport, 'snmpdomain=s' => \$snmpdomain, # SNMPv1/2c 'snmpcommunity=s' => \$snmpcomm, # SNMPv3 'snmpusername=s' => \$snmp3username, 'snmpauthpass=s' => \$snmp3authpass, 'snmpauthproto=s' => \$snmp3authproto, 'snmpprivpass=s' => \$snmp3privpass, 'snmpprivproto=s' => \$snmp3privproto, ); $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; my $snmp = init_snmp( hosts => \@snmp_hosts, version => $snmpver, port => $snmpport, domain => $snmpdomain, community => $snmpcomm, username => $snmp3username, authpassword => $snmp3authpass, authprotocol => $snmp3authproto, privpassword => $snmp3privpass, privprotocol => $snmp3privproto, ) if @snmp_hosts; my $plugins = Munin::Node::Configure::PluginList->new( libdir => $libdir, servicedir => $servicedir, pidebug => $pidebug, families => \@families, newer => $newer, ); $config->reinitialize({ %$config, suggest => $suggest, shell => $shell, remove_also => $removes, snmp => $snmp, plugins => $plugins, conffile => $conffile, servicedir => $servicedir, # needed for creating/removing links sconfdir => $sconfdir, DEBUG => $debug, }); return; } sub print_usage_and_exit { require Pod::Usage; Pod::Usage::pod2usage(-verbose => 1); } sub print_version_and_exit { require Pod::Usage; Pod::Usage::pod2usage( -verbose => 99, -sections => 'VERSION|COPYRIGHT', ); } 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 $plugins->{library}->prepare_plugin_environment($plugins->names); foreach my $plugin ($plugins->list) { fetch_plugin_autoconf($plugins, $plugin); fetch_plugin_suggestions($plugins, $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 ############################################################### sub init_snmp { unless (eval { require Munin::Node::SNMPConfig; }) { die "# ERROR: Cannot perform SNMP probing as Munin::Node::SNMPConfig module is not available.\n", $@; } return Munin::Node::SNMPConfig->new(@_); } ### 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 ($plugins, $plugin, $mode) = @_; my $name = $plugin->{name}; DEBUG("Running '$mode' on $name" ); my $res = $plugins->{library}->fork_service($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)"); return; } } # No if there is anything on stderr that's not debug if (my @junk = grep !/^#/, @{ $res->{stderr} }) { $plugin->log_error("Junk printed to stderr"); DEBUG("Junk printed to stderr: @junk"); return; } # Ignore debug output my @response = grep !/^#/, @{ $res->{stdout} }; # Error out on empty response except in suggest mode $plugin->log_error('Nothing printed to stdout') unless (scalar @response || $mode eq 'suggest'); 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 ($plugins, $plugin) = @_; return unless ($plugin->{capabilities}{autoconf}); my @response = run_plugin($plugins, $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 ($plugins, $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($plugins, $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 ($plugins, $plugin) = @_; return unless ($plugin->{capabilities}{snmpconf}); my @response = run_plugin($plugins, $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}:\n"; push @error_list, map { "# \t$_\n" } @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 DESCRIPTION B<munin-node-configure> reports which plugins are enabled on the current node, and suggest changes to this list. By default this program shows which plugins are activated on the system. If you specify C<--suggest>, it will present a table of plugins that will probably work (according to the plugins' autoconf command). If you specify C<--snmp>, followed by a list of hosts, it will present a table of SNMP plugins that they support. If you additionally specify C<--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. =head1 OPTIONS =over 4 =item B<< --help >> Show this help page. =item B<< --version >> Show version information. =item B<< --debug >> Print debug information on the operations of C<munin-node-configure>. This can be very verbose. All debugging output is printed to STDOUT, and each line is prefixed with '#'. Only errors are printed to STDERR. =item B<< --pidebug >> Plugin debug. Sets the environment variable MUNIN_DEBUG to 1 so that plugins may enable debugging. =item B<< --config <file> >> Override configuration file [/etc/munin/munin-node.conf] =item B<< --servicedir <dir> >> Override plugin directory [/etc/munin/plugins/] =item B<< --sconfdir <dir> >> Override plugin configuration directory [/etc/munin/plugin-conf.d/] =item B<< --libdir <dir> >> Override plugin library [/usr/share/munin/plugins/] =item B<< --suggest >> Suggest plugins that might be added or removed, instead of those that are currently enabled. =back =head2 OUTPUT OPTIONS By default, C<munin-node-configure> will print out a table summarising the results. =over 4 =item B<< --shell >> Instead of a table, print shell commands to install the new plugin suggestions. This implies C<--suggest>, unless C<--snmp> was also enabled. By default, it will not attempt to remove any plugins. =item B<< --remove-also >> When C<--shell> is enabled, also provide commands to remove plugins that are no longer applicable from the service directory. =back =head2 PLUGIN SELECTION OPTIONS =over 4 =item B<< --families <family,...> >> Override the list of families that will be used (auto, manual, contrib, snmpauto). Multiple families can be specified as a comma-separated list, by repeating the C<--families> option, or as a combination of the two. When listing installed plugins, the default families are 'auto', 'manual' and 'contrib'. Only 'auto' plugins are checked for suggestions. SNMP probing is only performed on 'snmpauto' plugins. =item B<< --newer <version> >> Only consider plugins added to the Munin core since <version>. This option is useful when upgrading, since it can prevent plugins that have been manually removed from being reinstalled. This only applies to plugins in the 'auto' family. =back =head2 SNMP Options =over 4 =item B<< --snmp <host|cidr,...> >> Probe the SNMP agents on the host or CIDR network (e.g. "192.168.1.0/24"), to see what plugins they support. This may take some time, especially if the many hosts are specified. This option can be specified multiple times, or as a comma-separated list, to include more than one host/CIDR. =item B<< --snmpversion <ver> >> The SNMP version (1, 2c or 3) to use. ['2c'] =item B<< --snmpport <port> >> The SNMP port to use [161] =item B<< --snmpdomain <domain> >> The Transport Domain to use for exchanging SNMP messages. The default is UDP/IPv4. Possible values: 'udp', 'udp4', 'udp/ipv4'; 'udp6', 'udp/ipv6'; 'tcp', 'tcp4', 'tcp/ipv4'; 'tcp6', 'tcp/ipv6'. =item B<SNMP 1/2c authentication> SNMP versions 1 and 2c use a "community string" for authentication. This is a shared password, sent in plaintext over the network. =item B<< --snmpcommunity <string> >> The community string for version 1 and 2c agents. ['public'] (If this works your device is probably very insecure and needs a security checkup). =item B<SNMP 3 authentication> SNMP v3 has three security levels. Lowest is C<noAuthNoPriv>, which provides neither authentication nor encryption. If a username and C<authpassword> are given it goes up to C<authNoPriv>, and the connection is authenticated. If C<privpassword> is also given the security level becomes C<authPriv>, and the connection is authenticated and encrypted. B<Note>: Encryption can slow down slow or heavily loaded network devices. For most uses C<authNoPriv> will be secure enough -- the password is sent over the network encrypted in any case. ContextEngineIDs are not (yet) supported. For further reading on SNMP v3 security models please consult RFC3414 and the documentation for L<Net::SNMP>. =item B<< --snmpusername <name> >> Username. There is no default. =item B<< --snmpauthpassword <password> >> Authentication password. Optional when encryption is also enabled, in which case defaults to the privacy password (C<--snmpprivpassword>). =item B<< --snmpauthprotocol <protocol> >> Authentication protocol. One of 'md5' or 'sha' (HMAC-MD5-96, RFC1321 and SHA-1/HMAC-SHA-96, NIST FIPS PIB 180, RFC2264). ['md5'] =item B<< --snmpprivpassword <password> >> Privacy password to enable encryption. There is no default. An empty ('') password is considered as no password and will not enable encryption. Privacy requires a privprotocol as well as an authprotocol and a authpassword, but all of these are defaulted (to 'des', 'md5', and the privpassword value, respectively) and may therefore be left unspecified. =item B<< --snmpprivprotocol <protocol> >> If the privpassword is set this setting controls what kind of encryption is used to achieve privacy in the session. Only the very weak 'des' encryption method is supported officially. ['des'] munin-node-configure also supports '3des' (CBC-3DES-EDE, aka Triple-DES, NIST FIPS 46-3) as specified in IETF draft-reeder-snmpv3-usm-3desede. Whether or not this works with any particular device, we do not know. =back =head1 FILES /etc/munin/munin-node.conf /etc/munin/plugin-conf.d/* /etc/munin/plugins/* /usr/share/munin/plugins/plugins.history /usr/share/munin/plugins/* =head1 VERSION This is munin-node-configure (munin-node) v2.0.66. =head1 AUTHORS Jimmy Olsen, Nicolai Langfeldt, Matthew Boyle =head1 BUGS Please see L<http://munin-monitoring.org/report/1>. =head1 COPYRIGHT Copyright (C) 2003-2006 Jimmy Olsen, Nicolai Langfeldt. Copyright (C) 2009-2010 Matthew Boyle 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