package Munin; #!@@PERL@@ # # Copyright (C) 2003-2004 Jimmy Olsen, Audun Ytterdal # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # # $Id: Munin.pm.in 1142 2006-10-17 12:27:35Z tore $ # # $Log$ # Revision 1.44.2.4 2005/03/09 17:57:37 jimmyo # Escape regexps more properly (Deb#296575). # # Revision 1.44.2.3 2005/03/06 22:22:40 jimmyo # Make sure all rrd-tunes are correct after an upgrade (Deb#296454, Deb#296645). # # Revision 1.44.2.2 2005/03/06 19:32:45 jimmyo # Make sure all rrd-tunes are correct after an upgrade (Deb#296454, Deb#296645). # # Revision 1.44.2.1 2005/02/01 22:02:30 jimmyo # Added category_order, to complete the *_order options. # # Revision 1.44 2005/01/05 18:32:49 jimmyo # Main: Tables in service-view now containt the correct "Type" when using data aliases. # # Revision 1.43 2005/01/05 18:08:01 jimmyo # Main: Tables in service-view now sorted according to graph_order. # # Revision 1.42 2005/01/05 17:43:34 jimmyo # Main: Bugfix with graphs using both "graph_sums" and data aliases. # # Revision 1.41 2004/12/25 18:56:59 jimmyo # Allow dots in PNG paths (patch by Jacques Caruso). # # Revision 1.40 2004/12/22 21:08:30 jimmyo # Bugfix when using long labels and CGI graphing. # # Revision 1.39 2004/12/22 20:13:30 jimmyo # Added option cgiurl_graph. # # Revision 1.38 2004/12/22 20:01:14 jimmyo # Optimised munin-cgi-graph a bit. # # Revision 1.37 2004/12/09 22:12:56 jimmyo # Added "graph_period" option, to make "graph_sums" usable. # # Revision 1.36 2004/12/08 08:47:44 jimmyo # Fix bug where munin-limits didn't warn properly in all situations. # # Revision 1.35 2004/11/26 13:06:00 jimmyo # Bugfix in munin-html, when using groups/host names with more than one dot. # # Revision 1.34 2004/11/21 14:39:23 jimmyo # Renamed function munin_get_val to munin_get. # # Revision 1.33 2004/11/21 14:30:55 jimmyo # Added new field option "line", which draws HRULEs. # # Revision 1.32 2004/11/19 23:01:41 jimmyo # Fixed irqstats plugin so it doesn't use numbers as field names. Also expanded the label if possible, since munin-graph now handles long labels. # # Revision 1.31 2004/11/19 22:09:48 jimmyo # Write out contact info to datafile correctly. # # Revision 1.30 2004/11/19 21:32:38 jimmyo # Added a --force option to munin-limits, to force sending absolutely all messages. # # Revision 1.29 2004/11/19 20:51:24 jimmyo # New notification system finished (I think). # # Revision 1.28 2004/11/18 00:22:01 jimmyo # Midway implementation of new notification scheme. # # Revision 1.27 2004/11/16 20:00:43 jimmyo # License cleanups. # # Revision 1.26 2004/11/14 20:10:38 jimmyo # Made sure the latest feature additions also work with the CGI option. # # Revision 1.25 2004/11/12 23:18:52 jimmyo # Added new options notify_enable and notify_text, to allow more finely tuned notifications (both what to notify and what text to send.) # # Revision 1.24 2004/09/23 19:46:01 jimmyo # Added option "compare". # # Revision 1.23 2004/09/23 16:52:55 jimmyo # Don't show warnings/criticals in the HTML interface if the field/graph isn't plotted. # # Revision 1.22 2004/09/23 16:43:39 jimmyo # Don't show warnings/criticals in the HTML interface if the field/graph isn't plotted. # # Revision 1.21 2004/09/13 21:00:38 jimmyo # Added new config option "local_address", to specify which local address outgoing connections (from munin-update) should be used. # # Revision 1.20 2004/09/10 19:24:18 jimmyo # Added new option "graph_sums" which creates summarised graphs. # # Revision 1.19 2004/09/07 21:45:37 jimmyo # Changes of min and max values now causes corresponding changes in the RRD files. # # Revision 1.18 2004/09/04 21:33:13 jimmyo # Handle strange characters better. # # Revision 1.17 2004/09/01 19:00:22 jimmyo # Moved function get_picture_filename from munin-graph to Munin.pm. # # Revision 1.16 2004/09/01 18:30:21 jimmyo # Added new options "graph_height" and "graph_width", to beter control size. # # Revision 1.15 2004/08/23 10:01:07 jimmyo # Better error handling when fetching data from RRD files. # # Revision 1.14 2004/08/23 09:23:46 jimmyo # Better error handling when fetching data from rrd files. # # Revision 1.13 2004/07/02 14:19:06 jimmyo # * Munin-graph: Added 'graph_sources' option. This is (in effect) the same as # graph_order, but with a default of 'graph no'. # # Revision 1.12 2004/06/24 14:32:17 jimmyo # More work on output_graphs (renamed to graph_strategy, amongst others). # # Revision 1.11 2004/06/23 17:38:39 jimmyo # * Created munin-cgi-graph, which creates dynamic graphs. # * Added munin.conf option "output_graphs (cgi|cron)" which # defaults to cron. # # Revision 1.10 2004/06/08 15:30:31 jimmyo # The server programs now open the log file at an earlier point. # # Revision 1.9 2004/06/08 15:18:21 jimmyo # The server programs now open the log file at an earlier point. # # Revision 1.8 2004/05/20 22:30:08 jimmyo # * Munin-limits added to distro. # * Breached limis now show up in overview and node view. # # Revision 1.7 2004/05/20 20:47:19 jimmyo # The server programs now open the log file at an earlier point. # # Revision 1.6 2004/05/20 12:20:24 jimmyo # Added "graph_category" option, to categorise plugins. # # Revision 1.5 2004/05/16 11:27:39 jimmyo # Added warning and critical statuses to the info table in the service view. # # Revision 1.4 2004/05/16 00:33:07 jimmyo # Added a descriptionary table at the bottom of the service view page. Also added "graph_info" and ".info" which can be used by plugins to supply supplementary information for it. # # Revision 1.3 2004/01/29 18:19:58 jimmyo # Made Munin compatible with perl 5.005_03 (patch by Lupe Christoph) (SF#884622) # # Revision 1.2 2004/01/15 15:20:01 jimmyo # Making things workable after name change. Upping for test verwion. # # Revision 1.1 2004/01/02 18:50:01 jimmyo # Renamed occurrances of lrrd -> munin # # Revision 1.1.1.1 2004/01/02 15:18:07 jimmyo # Import of LRRD CVS tree after renaming to Munin # # Revision 1.29 2003/12/12 18:59:30 jimmyo # Change \1 to to make lrrd-update shut up. # # Revision 1.28 2003/12/10 11:59:39 jimmyo # Enable/disable notifications at any level # # Revision 1.27 2003/12/06 20:19:55 jimmyo # Typo fix # # Revision 1.26 2003/12/06 19:12:57 jimmyo # Added max_processes config variable. Also, removed zombie-generation code. :-P # # Revision 1.25 2003/12/06 17:13:56 jimmyo # Can now escape #. Can now use \ to join lines. # # Revision 1.24 2003/12/02 11:48:56 jimmyo # Forgot small change # # Revision 1.23 2003/12/02 10:14:43 jimmyo # Moved some functions to LRRD.pm, since other programs use them as well. # # Revision 1.22 2003/11/24 16:25:51 jimmyo # Make sure LRRD doesn't write any illegal lines to the datafile # # Revision 1.21 2003/11/24 14:22:10 jimmyo # 0.9.9 release 2. Fixes a couple of stupid (minor) bugs # # Revision 1.20 2003/11/24 12:58:01 jimmyo # minor bugfix - no noise about "extinfo" # # Revision 1.19 2003/11/16 11:33:16 jimmyo # Minor bugfixes # # Revision 1.18 2003/11/15 13:26:01 jimmyo # Added warn to legal options # # Revision 1.17 2003/11/15 11:10:29 jimmyo # Various fixes # # Revision 1.16 2003/11/10 16:09:00 jimmyo # Be nice to Nagios - don't DOS it. # # Revision 1.15 2003/11/07 23:57:05 jimmyo # Remove trailing whitespace from config file # # Revision 1.14 2003/11/07 22:58:09 jimmyo # Documentation of new features/changes # # Revision 1.13 2003/11/07 20:46:12 jimmyo # Only require Config::General if using old config format. # # Revision 1.12 2003/11/07 20:12:02 jimmyo # datafile now saved in new config format # # Revision 1.11 2003/11/07 17:43:16 jimmyo # Cleanups and log entries # # use Exporter; @ISA = ('Exporter'); @EXPORT = ('munin_trend', 'munin_fetch', 'munin_nscasend', 'munin_createlock', 'munin_removelock', 'munin_runlock', 'munin_getlock', 'munin_readconfig', 'munin_writeconfig', 'munin_delete', 'munin_overwrite', 'munin_config', 'munin_draw_field', 'munin_get_bool', 'munin_get_bool_val', 'munin_get', 'munin_field_status', 'munin_service_status', 'munin_node_status', 'munin_category_status', 'munin_get_picture_filename', 'munin_get_filename', 'munin_graph_column_headers', 'munin_get_max_label_length', 'munin_get_field_order', 'munin_get_rrd_filename' ); use strict; use RRDs; use Fcntl qw(:DEFAULT :flock); use IO::Handle; use Symbol 'gensym'; my $nsca = new IO::Handle; my $config = undef; my $VERSION = "@@VERSION@@"; my $DEBUG=0; my $configfile="@@CONFDIR@@/munin.conf"; my @legal = ("tmpldir", "ncsa", "ncsa_server", "ncsa_config", "rundir", "dbdir", "logdir", "htmldir", "include", "domain_order", "node_order", "graph_order", "graph_sources", "fork", "graph_title", "create_args", "graph_args", "graph_vlabel", "graph_vtitle", "graph_total", "graph_scale", "graph", "update", "host_name", "label", "cdef", "draw", "graph", "max", "min", "negative", "skipdraw", "type", "warning", "critical", "special_stack", "special_sum", "stack", "sum", "address", "htaccess", "warn", "use_default_name", "use_node_name", "port", "graph_noscale", "nsca", "nsca_server", "nsca_config", "extinfo", "fetch_data", "filename", "max_processes", "nagios", "info", "graph_info", "graph_category", "graph_strategy", "graph_width", "graph_height", "graph_sums", "local_address", "compare", "text", "command", "contact", "contacts", "max_messages", "always_send", "notify_alias", "line", "state", "graph_period", "cgiurl_graph", "cgiurl", "service_order", "category_order", "version", "colour" ); my %legal_expanded = map { $_ => 1 } @legal; # Fields to copy when "aliasing" a field my @copy_fields = ("label", "draw", "type", "rrdfile", "fieldname", "info"); sub munin_trend { my (@array) = @_; return ($array[$#array] - $array[0]); } sub munin_fetch { my ($file,$last,$type) = @_; my ($start,$step,$names,$data) = RRDs::fetch $file,$type || "AVERAGE"; unless (defined $data) { ::logger ("Could not fetch data from $file(".($type||"AVERAGE")."): ". RRDs::error); return undef; } my @array = map { @$_[0] } splice(@$data, $#$data - ($last || 1)); return $array[0] if (!$last); return @array; } sub munin_draw_field { my $node = shift; my $service = shift; my $field = shift; $field =~ s/=.*//; print "DEBUG: munin_draw_field: Checking $service -> $field: " . &munin_get_bool_val ($node->{client}->{$service}->{$field.".graph"}, 1) . ".\n" if $DEBUG;; return 0 if (exists $node->{client}->{$service}->{$field.".skipdraw"}); return (&munin_get_bool_val ($node->{client}->{$service}->{$field.".graph"}, 1)); } sub munin_nscasend { my ($name,$service,$label,$level,$comment) = @_; if (!$nsca->opened) { open ($nsca ,"|$config->{nsca} $config->{nsca_server} -c $config->{nsca_config} -to 60"); } if ($label) { print $nsca "$name\t$service: $label\t$level\t$comment\n"; print ("$name;$service: $label;$level;$comment\n") if $DEBUG; } else { print $nsca "$name\t$service\t$level\t$comment\n"; print ("$name;$service;$level;$comment\n") if $DEBUG; } } sub munin_createlock { my ($lockname) = @_; if (-e $lockname && (! -w $lockname || ! -f $lockname)) { die "Error writing to $lockname, wrong permissions"; } if (sysopen (LOCK,$lockname,O_WRONLY | O_CREAT | O_EXCL)) { print "Creating lock : $lockname succeded\n" if $DEBUG; print LOCK $$; # we want the pid inside for later use close LOCK; return 1; } else { print "Creating lock : $lockname failed, skipping\n" if $DEBUG; return 0; } } sub munin_removelock { my ($lockname) = @_; if (-e $lockname && ! -w $lockname) { die "Error deleting $lockname, wrong permissions"; } if (-e $lockname) { unlink $lockname; print "Deleting lock : $lockname\n" if $DEBUG; } else { print "Deleting lock : $lockname not found, skipping\n" if $DEBUG; } } sub munin_runlock { my ($lockname) = @_; unless (&munin_getlock($lockname)) { print "Lock already exists: $lockname. Dying.\n"; exit 0; } return 1; } sub munin_getlock { my ($lockname) = @_; unless (&munin_createlock($lockname)) { # Is the lockpid alive? open LOCK,$lockname; my $pid = ; if ($pid =~ /^\d+$/ and kill(0,$pid)) { return 0; } &munin_removelock ($lockname); &munin_createlock($lockname); } return 1; } sub munin_delete { my ($config,$data) = @_; for my $domain (keys %{$data->{domain}}) { unless ($config->{domain}->{$domain}) { ::logger("Removing domain: $domain"); delete ($data->{domain}->{$domain}); next; } for my $node (keys %{$data->{domain}->{$domain}->{node}}) { unless ($config->{domain}->{$domain}->{node}->{$node}) { ::logger("Removing node from $domain: $node"); delete ($data->{domain}->{$domain}->{node}->{$node}); } } } return ($data); } sub munin_overwrite { my ($configfile,$overwrite) = @_; for my $key (keys %$overwrite) { if (ref $overwrite->{$key}) { &munin_overwrite($overwrite->{$key},$configfile->{$key}); } $configfile->{$key} = $overwrite->{$key}; } return ($configfile); } sub munin_readconfig { my ($conf, $missingok, $corruptok) = @_; my $config = undef; my @contents = undef; $conf ||= $configfile; if (! -r $conf and ! $missingok) { ::logger ("munin_readconfig: cannot open '$conf'"); return undef; } if (open (CFG, $conf)) { @contents = ; close (CFG); } $config = &munin_parse_config (\@contents); # Some important defaults before we return... $config->{'rundir'} ||= "/tmp/"; $config->{'dbdir'} ||= "/var/lib/munin/"; $config->{'logdir'} ||= "/var/log/"; $config->{'tmpldir'}||= "/etc/munin/templates/"; $config->{'htmldir'}||= "@@HTMLDIR@@/"; return ($config); } sub munin_parse_config { my $lines = shift; my $hash = undef; my $prefix = ""; my $prevline = ""; foreach my $line (@{$lines}) { chomp $line; #$line =~ s/(^|[^\\])#.*/$1/g if $line =~ /#/; # Skip comments... if ($line =~ /#/) { next if ($line =~ /^#/); $line =~ s/(^|[^\\])#.*/$1/g; } next unless ($line =~ /\S/); # And empty lines... if (length $prevline) { $line = $prevline . $line; $prevline = ""; } if ($line =~ /\\\\$/) { $line =~ s/\\\\$/\\/; } elsif ($line =~ /\\$/) { ($prevline = $line) =~ s/\\$//; next; } $line =~ s/\s+$//g; # And trailing whitespace... $line =~ s/^\s+//g; # And heading whitespace... if ($line =~ /^\.(\S+)\s+(.+)\s*$/) { my ($var, $val) = ($1, $2); $hash = &munin_set_var_path ($hash, $var, $val); } elsif ($line =~ /^\s*\[([^\]]*)]\s*$/) { $prefix = $1; if ($prefix =~ /^([^:;]+);([^:;]+)$/) { $prefix .= ":"; } elsif ($prefix =~ /^([^:;]+);$/) { $prefix .= ""; } elsif ($prefix =~ /^([^:;]+);([^:;]+):(.*)$/) { $prefix .= "."; } elsif ($prefix =~ /^([^:;]+)$/) { (my $domain = $prefix) =~ s/^[^\.]+\.//; $prefix = "$domain;$prefix:"; } elsif ($prefix =~ /^([^:;]+):(.*)$/) { (my $domain = $prefix) =~ s/^[^\.]+\.//; $prefix = "$domain;$prefix."; } } elsif ($line =~ /^\s*(\S+)\s+(.+)\s*$/) { my ($var, $val) = ($1, $2); $hash = &munin_set_var_path ($hash, "$prefix$var", $val); } else { warn "Malformed configuration line \"$line\"."; } } return $hash; } sub munin_get_var_path { my $hash = shift; my $var = shift; my $val = shift; print "DEBUG: Getting var \"$var\" = \"$val\"\n" if $DEBUG; if ($var =~ /^\s*([^;:]+);([^;:]+):(\S+)\s*$/) { my ($dom, $host, $rest) = ($1, $2, $3); my @sp = split (/\./, $rest); if (@sp == 3) { return $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{"$sp[1].$sp[2]"}; } elsif (@sp == 2) { return $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{$sp[1]}; } elsif (@sp == 1) { return $hash->{domain}->{$dom}->{node}->{$host}->{$sp[0]}; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:]+);([^;:]+)\s*$/) { my ($dom, $rest) = ($1, $2); my @sp = split (/\./, $rest); if (@sp == 1) { return $hash->{domain}->{$dom}->{$sp[0]}; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:\.]+)\s*$/) { return $hash->{$1}; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } return undef; } sub munin_set_var_path { my $hash = shift; my $var = shift; my $val = shift; print "DEBUG: Setting var \"$var\" = \"$val\"\n" if $DEBUG; if ($var =~ /^\s*([^;:]+);([^:]+):(\S+)\s*$/) { my ($dom, $host, $rest) = ($1, $2, $3); my @sp = split (/\./, $rest); if (@sp == 3) { ::logger ("Warning: Unknown option \"$sp[2]\" in \"$dom;$host:$sp[0].$sp[1].$sp[2]\".") unless defined $legal_expanded{$sp[2]}; $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{"$sp[1].$sp[2]"} = $val; } elsif (@sp == 2) { ::logger ("Warning: Unknown option \"$sp[1]\" in \"$dom;$host:$sp[0].$sp[1]\".") unless defined $legal_expanded{$sp[1]}; $hash->{domain}->{$dom}->{node}->{$host}->{client}->{$sp[0]}->{$sp[1]} = $val; } elsif (@sp == 1) { ::logger ("Warning: Unknown option \"$sp[0]\" in \"$dom;$host:$sp[0]\".") unless defined $legal_expanded{$sp[0]}; $hash->{domain}->{$dom}->{node}->{$host}->{$sp[0]} = $val; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:]+);([^;:]+)\s*$/) { my ($dom, $rest) = ($1, $2); my @sp = split (/\./, $rest); if (@sp == 1) { ::logger ("Warning: Unknown option \"$sp[0]\" in \"$dom;$sp[0]\".") unless defined $legal_expanded{$sp[0]}; $hash->{domain}->{$dom}->{$sp[0]} = $val; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } } elsif ($var =~ /^\s*([^;:\.]+)\s*$/) { ::logger ("Warning: Unknown option \"$1\" in \"$1\".") unless defined $legal_expanded{$1}; $hash->{$1} = $val; } elsif ($var =~ /^\s*([^\.]+)\.([^\.]+)\.([^\.]+)$/) { ::logger ("Warning: Unknown option \"$1\" in \"$var\".") unless defined $legal_expanded{$1}; ::logger ("Warning: Unknown option \"$3\" in \"$var\".") unless defined $legal_expanded{$3}; $hash->{$1}->{$2}->{$3} = $val; } else { warn "munin_set_var: Malformatted variable path \"$var\"."; } return $hash; } sub munin_writeconfig_loop { my ($data,$fh,$pre) = @_; $pre |= ""; # Write datafile foreach my $a (keys %{$data}) { if (ref ($data->{$a}) eq "HASH") { if ($a eq "domain" or $a eq "node" or $a eq "client") { &munin_writeconfig_loop ($data->{$a}, $fh, "$pre"); } elsif ($a eq "contact" and $pre eq "") { &munin_writeconfig_loop ($data->{$a}, $fh, "contact."); } else { my $lpre = $pre; if ($lpre eq "") { $lpre = $a.";"; } elsif ($lpre =~ /;$/) { $lpre .= $a.":"; } else { $lpre .= $a."."; } &munin_writeconfig_loop ($data->{$a}, $fh, "$lpre"); } } elsif (defined $data->{$a} and length $data->{$a}) { next if "$pre$a" eq "version"; # Handled separately print "Writing: $pre$a $data->{$a}\n" if $DEBUG; if ($data->{$a} =~ /\\$/) { # Backslash as last char has special meaning. Avoid it. print $fh "$pre$a $data->{$a}\\\n"; } else { print $fh "$pre$a $data->{$a}\n"; } } } } sub munin_writeconfig { my ($datafilename,$data,$fh) = @_; # my $datafile = new Config::General(); # $datafile->save_file($datafilename,$data); if (!defined $fh) { $fh = gensym(); unless (open ($fh, ">$datafilename")) { die "Fatal error: Could not open \"$datafilename\" for writing: $!"; } } # Write version print $fh "version $VERSION\n"; # Write datafile &munin_writeconfig_loop ($data, $fh, ""); if (defined $fh) { print "DEBUG: Closing filehandle \"$datafilename\"...\n" if $DEBUG; close ($fh); } } sub munin_config { my $conffile = shift; $config = shift; $conffile ||= $configfile; $config = &munin_readconfig ($conffile); ::logger_open ($config->{logdir}); my $data = &munin_readconfig("$config->{dbdir}/datafile", 1, 1); $data = &munin_overwrite($data,$config); return ($data); } sub munin_get_picture_filename { my $config = shift; my $domain = shift; my $name = shift; my $service = shift; my $scale = shift; my $sum = shift; my $dir = $config->{'htmldir'}; # Sanitise $dir =~ s/[^\w_\/"'\[\]\(\)+=-]\./_/g; $domain =~ s/[^\w_\/"'\[\]\(\)+=\.-]/_/g; $name =~ s/[^\w_\/"'\[\]\(\)+=\.-]/_/g; $service =~ s/[^\w_\/"'\[\]\(\)+=-]/_/g; $scale =~ s/[^\w_\/"'\[\]\(\)+=-]/_/g; if (defined $sum and $sum) { return "$dir/$domain/$name-$service-$scale-sum.png"; } else { return "$dir/$domain/$name-$service-$scale.png"; } } sub munin_get_filename { my ($config,$domain,$node,$service,$field) = @_; return ($config->{'dbdir'} . "/$domain/$node-$service-$field-" . lc substr (($config->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{$field.".type"}||"GAUGE"), 0,1). ".rrd"); } sub munin_get_bool { my $conf = shift; my $field = shift; my $default = shift; my $domain = shift; my $node = shift; my $service = shift; my $plot = shift; return undef unless defined $field; my $ans = &munin_get ($conf, $field, $default, $domain, $node, $service, $plot); return undef if not defined $ans; if ($ans =~ /^yes$/i or $ans =~ /^true$/i or $ans =~ /^on$/i or $ans =~ /^enable$/i or $ans =~ /^enabled$/i ) { return 1; } elsif ($ans =~ /^no$/i or $ans =~ /^false$/i or $ans =~ /^off$/i or $ans =~ /^disable$/i or $ans =~ /^disabled$/i ) { return 0; } elsif ($ans !~ /\D/) { return $ans; } else { return undef; } } sub munin_get_bool_val { my $field = shift; my $default = shift; if (!defined $field) { if (!defined $default) { return 0; } else { return $default; } } if ($field =~ /^yes$/i or $field =~ /^true$/i or $field =~ /^on$/i or $field =~ /^enable$/i or $field =~ /^enabled$/i ) { return 1; } elsif ($field =~ /^no$/i or $field =~ /^false$/i or $field =~ /^off$/i or $field =~ /^disable$/i or $field =~ /^disabled$/i ) { return 0; } elsif ($field !~ /\D/) { return $field; } else { return undef; } } sub munin_get { my $conf = shift; my $field = shift; my $default = shift; my $domain = shift; my $node = shift; my $service = shift; my $plot = shift; if (defined $field) { return $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$plot.$field"} if (defined $domain and defined $node and defined $service and defined $plot and defined $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$plot.$field"}); return $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{$field} if (defined $domain and defined $node and defined $service and defined $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{$field}); return $conf->{domain}->{$domain}->{node}->{$node}->{$field} if (defined $domain and defined $node and defined $conf->{domain}->{$domain}->{node}->{$node}->{$field}); return $conf->{domain}->{$domain}->{$field} if (defined $domain and defined $conf->{domain}->{$domain}->{$field}); return $conf->{$field} if (defined $conf->{$field}); return $default; } else { return $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service} if (defined $domain and defined $node and defined $service and defined $conf->{domain}->{$domain}->{node}->{$node}->{client}->{$service}); return $conf->{domain}->{$domain}->{node}->{$node} if (defined $domain and defined $node and defined $conf->{domain}->{$domain}->{node}->{$node}); return $conf->{domain}->{$domain} if (defined $domain and defined $conf->{domain}->{$domain}); return $conf if (defined $conf); return $default; } } sub munin_node_status { my ($config, $limits, $domain, $node, $check_draw) = @_; my $state = "ok"; return undef unless defined $config->{domain}->{$domain}->{node}->{$node}; my $snode = $config->{domain}->{$domain}->{node}->{$node}; foreach my $service (keys %{$snode}) { my $fres = &munin_service_status ($config, $limits, $domain, $node, $service, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } } } return $state; } sub munin_category_status { my ($config, $limits, $domain, $node, $category, $check_draw) = @_; my $state = "ok"; return undef unless defined $config->{domain}->{$domain}->{node}->{$node}; my $snode = $config->{domain}->{$domain}->{node}->{$node}; foreach my $service (keys %{$snode->{client}}) { next if ((not defined $snode->{client}->{$service}->{graph_category}) and $category ne 'other'); next if ((defined $snode->{client}->{$service}->{graph_category}) and ($snode->{client}->{$service}->{graph_category} ne $category)); my $fres = &munin_service_status ($config, $limits, $domain, $node, $service, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } } } return $state; } sub munin_service_status { my ($config, $limits, $domain, $node, $service, $check_draw) = @_; my $state = "ok"; return undef unless defined $config->{domain}->{$domain}->{node}->{$node}->{client}->{$service}; foreach my $key (keys %{$config->{domain}->{$domain}->{node}->{$node}->{client}->{$service}}) { next unless $key =~ /^([^\.]+)\.label$/; my $field = $1; my $fres = &munin_field_status ($config, $limits, $domain, $node, $service, $field, $check_draw); if (defined $fres) { if ($fres eq "critical") { $state = $fres; last; } elsif ($fres eq "warning") { $state = $fres; } } } return $state; } sub munin_field_status { my ($config, $limits, $domain, $node, $service, $field, $check_draw) = @_; my $state = undef; # Return undef if nagios is turned off, or the field doesn't have any limits unless ((defined $config->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$field.warning"}) or (defined $config->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$field.critical"})) { return undef; } if (defined $limits->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$field.critical"} and (!defined $check_draw or !$check_draw or &munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $service, $field))) { return "critical"; } elsif (defined $limits->{domain}->{$domain}->{node}->{$node}->{client}->{$service}->{"$field.warning"} and (!defined $check_draw or !$check_draw or &munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $service, $field))) { return "warning"; } else { return "ok"; } } sub munin_graph_column_headers { my ($config, $domain, $node, $serv) = @_; my $ret = 0; my @fields = (); foreach my $field (keys %{$config->{domain}->{$domain}->{node}->{$node}->{client}->{$serv}}) { if ($field =~ /^([^\.]+)\.negative$/ and munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $serv, $1)) { return 1; } elsif ($field =~ /^([^\.]+)\.label$/ and munin_draw_field ($config->{domain}->{$domain}->{node}->{$node}, $serv, $1)) { push @fields, $1; } } return 1 if (munin_get_max_label_length ($config->{'domain'}->{$domain}->{'node'}->{$node}, $config, $domain, $node, $serv, \@fields) > 20); return $ret; } sub munin_get_max_label_length { my $node = shift; my $config = shift; my $domain = shift; my $host = shift; my $service = shift; my $order = shift; my $result = 0; for my $field (@$order) { my $path = undef; (my $f = $field) =~ s/=.+//; next if (exists $node->{client}->{$service}->{$f.".process"} and $node->{client}->{$service}->{$f.".process"} ne "yes"); next if (exists $node->{client}->{$service}->{$f.".skipdraw"}); next unless (!exists $node->{client}->{$service}->{$f.".graph"} or $node->{client}->{$service}->{$f.".graph"} eq "yes"); if ($result < length ($node->{client}->{$service}->{$f.".label"} || $f)) { $result = length ($node->{client}->{$service}->{$f.".label"} || $f); } if (exists $node->{client}->{$service}->{graph_total} and length $node->{client}->{$service}->{graph_total} > $result) { $result = length $node->{client}->{$service}->{graph_total}; } } return $result; } sub munin_get_field_order { my $node = shift; my $config = shift; my $domain = shift; my $host = shift; my $service = shift; my $result = []; if ($node->{client}->{$service}->{graph_sources}) { foreach my $gs (split /\s+/, $node->{client}->{$service}->{'graph_sources'}) { push (@$result, "-".$gs); } } if ($node->{client}->{$service}->{graph_order}) { push (@$result, split /\s+/, $node->{client}->{$service}->{'graph_order'}); } for my $key (keys %{$node->{client}->{$service}}) { my ($client,$type)=""; ($client,$type) = split /\./,$key; if (defined $type and $type eq "label") { push @$result,$client if !grep /^\Q$client\E(?:=|$)/, @$result;; } } return $result; } sub munin_get_rrd_filename { my $node = shift; my $config = shift; my $domain = shift; my $name = shift; my $service = shift; my $field = shift; my $path = shift; my $result = "unknown"; if ($node->{client}->{$service}->{$field.".filename"}) { $result = $node->{client}->{$service}->{$field.".filename"}; } elsif ($path) { if (!defined ($node->{client}->{$service}->{$field.".label"})) { print "DEBUG: Setting label: $field\n" if $DEBUG; $node->{client}->{$service}->{$field.".label"} = $field; } if ($path =~ /^\s*([^:;]+)[:;]([^:]+):([^:\.]+)[:\.]([^:\.]+)\s*$/) { $result = munin_get_filename ($config, $1, $2, $3, $4); print "\nDEBUG1: Expanding $path...\n" if $DEBUG; if (! defined $node->{client}->{$service}->{$field."label"}) { for my $f (@copy_fields) { if (not exists $node->{client}->{$service}->{"$field.$f"} and exists $config->{'domain'}->{$1}->{'node'}->{$2}->{'client'}->{$3}->{"$4.$f"}) { $node->{client}->{$service}->{"$field.$f"} = $config->{'domain'}->{$1}->{'node'}->{$2}->{'client'}->{$3}->{"$4.$f"}; } } } } elsif ($path =~ /^\s*([^:]+):([^:\.]+)[:\.]([^:\.]+)\s*$/) { print "\nDEBUG2: Expanding $path...\n" if $DEBUG; $result = munin_get_filename ($config, $domain, $1, $2, $3); for my $f (@copy_fields) { if (not exists $node->{client}->{$service}->{"$field.$f"} and exists $config->{'domain'}->{$domain}->{'node'}->{$1}->{'client'}->{$2}->{"$3.$f"}) { print "DEBUG: Copying $f...\n" if $DEBUG; $node->{client}->{$service}->{"$field.$f"} = $config->{'domain'}->{$domain}->{'node'}->{$1}->{'client'}->{$2}->{"$3.$f"}; } } } elsif ($path =~ /^\s*([^:\.]+)[:\.]([^:\.]+)\s*$/) { print "\nDEBUG3: Expanding $path...\n" if $DEBUG; $result = munin_get_filename ($config, $domain, $name, $1, $2); for my $f (@copy_fields) { if (not exists $node->{client}->{$service}->{"$field.$f"} and exists $node->{client}->{$1}->{"$2.$f"}) { $node->{client}->{$service}->{"$field.$f"} = $node->{client}->{$1}->{"$2.$f"}; } } } elsif ($path =~ /^\s*([^:\.]+)\s*$/) { print "\nDEBUG4: Expanding $path...\n" if $DEBUG; $result = munin_get_filename ($config, $domain, $name, $service, $1); for my $f (@copy_fields) { if (not exists $node->{client}->{$service}->{"$field.$f"} and exists $node->{client}->{$service}->{"$1.$f"}) { $node->{client}->{$service}->{"$field.$f"} = $node->{client}->{$service}->{"$1.$f"}; } } } } else { print "\nDEBUG5: Doing path...\n" if $DEBUG; $result = munin_get_filename($config, $domain,$name,$service,$field); } return $result; } 1; # vim: syntax=perl ts=8