WishList: muninAggregator.2.pl

File muninAggregator.2.pl, 7.5 kB (added by speculatrix, 3 years ago)

web interface aggregating the same graph across many nodes v1.01

Line 
1 #!/usr/bin/perl -w
2 # muninAggregator - a cgi-bin which presents the same graph for multiple nodes
3 # and thus allows seeing the state of a whole farm of machines
4 #
5 # 20091020 - v1.00 - PaulM
6 # - it's all working and fairly tidy too
7 # 20091021 - v1.01 - PaulM
8 # - expands graph types by looking at munin's rrd files
9 #     which I'd already envisaged but Philipp Niemann requested it
10 #     so I took up the challenge
11 #
12 # released under GPL to the munin community with the authority of my CTO @taptu.com
13
14 use strict;
15 use CGI qw(:standard);
16 use POSIX ":sys_wait_h";
17 use FileHandle;
18 use Data::Dumper;
19
20 ###############################################################################
21 # constants
22
23 # you'll need to tweak these to suit your installation
24 my $muninBaseUrl = ($ENV{'HTTPS'} eq 'on' ? 'https://' : 'http://') . $ENV{'SERVER_NAME'} . '/munin';
25 #my $muninBaseUrl = 'https://foo.example.com/munin';
26
27 my $muninDataFiles = '/var/lib/munin/db';
28
29 # most distros have it here
30 my $muninConf = '/etc/munin/munin.conf';
31 # but user self-builds might have it here:
32 #my $muninConf = '/usr/local/etc/munin/munin.conf';
33
34
35 ## you shouldn't need to touch anything below this line! ##
36
37 # make the munin RRD names more human-friendly
38 # at some point it'd be cool to scan the munin directories and add any missing items
39 my %graphTypes = (
40                     'apache_processes'          => 'Apache Processes'                   
41                   , 'cpu'                       => 'CPU'                                       
42                   , 'df'                        => 'Disk Usage'                         
43                   , 'forks'                     => 'Fork rate'                         
44                   , 'irqstats'                  => 'IRQ Statistics'                     
45                   , 'interrupts'                => 'Interrupts & Context Switches'     
46                   , 'iostat'                    => 'IOStat'                             
47                   , 'log_sizes'                 => 'Log File Sizes'                             
48                   , 'memory'                    => 'Memory Usage'                       
49                   , 'omreport_fan_speed'        => 'OMSA Fan Speed'                     
50                   , 'omreport_pwrmon_current'   => 'OMSA Power'                         
51                   , 'omreport_temp'             => 'OMSA Temperature'                   
52                   , 'processes'                 => 'Process count'                     
53                   , 'vmstat'                    => 'VMStat process states'             
54                  );
55
56 # make the munin time ranges more human-friendly
57 my %timeScales =
58                 (
59                   'day'                         => '24 Hours'
60                 , 'week'                        => '7 days'
61                 , 'month'                       => '31 days'
62                 , 'year'                        => 'year'
63                 );
64
65 my $refreshRate = 10;   # default refresh rate
66
67 ###############################################################################
68 # user-supplied url params
69 my $dbgLevel    = (defined(param('debugLevel')))        ? param('debugLevel')   : '0';
70 my $graphType   = (defined(param('graphType')))         ? param('graphType')    : '';
71 my $timeScale   = (defined(param('timeScale')))         ? param('timeScale')    : '';
72 my $nodeGroup   = (defined(param('nodeGroup')))         ? param('nodeGroup')    : '';
73 my $refresh     = (defined(param('refresh')))           ? param('refresh')      : 0;
74
75 ###############################################################################
76 # writeSelectArray generates a select field of specified name with named options
77 # from a hash and pre-selects a specific value (if defined).
78 sub     writeSelectHash
79 {
80         my ($name, $value, %options) = @_;
81
82         print "<select name=\"$name\">\n";
83         for my $option (sort keys %options)
84         {
85                 print "\t<option value=\"" . $option . "\"";
86                 print " selected " if ((defined $option) && ($option eq $value));
87                 print ">" . $options{$option} . "</option>\n";
88         }
89         print "</select>"
90 }
91 ###############################################################################
92 # writeSelectArray generates a select field of specified name with options
93 # from an array and pre-selects a specific value (if defined).
94 sub     writeSelectArray
95 {
96         my ($name, $value, @options) = @_;
97
98         print "<select name=\"$name\">\n";
99         for my $option (sort @options)
100         {
101                 print "\t<option value=\"" . $option . "\"";
102                 print " selected " if ((defined $option) && ($option eq $value));
103                 print ">" . $option . "</option>\n";
104         }
105         print "</select>"
106 }
107 ###############################################################################
108 # generates a debug message in html-friendly form if the specified level is
109 # equal or higher than current global debug level
110 sub debugPrint
111 {
112         my ($level, $dbgText) = @_;
113         print "<i>Debug: $dbgText</i>\n<br />\n" if ($level >= $dbgLevel);
114 }
115
116 ###############################################################################
117
118 # minimise delays writing to the browser
119 $| = 1;
120 STDERR->autoflush;          # already unbuffered in stdio
121
122
123
124 print "Content-type: text/html\n\n"
125         . "<html>\n<head>\n";
126
127 print "\t<meta http-equiv=\"refresh\" content=\"$refreshRate\">\n" if (defined($refresh) && ($refresh));
128
129 print "\t<title>muninAggregator</title>\n"
130         . "</head>\n"
131         . "<body>\n";
132
133
134 if (!open(FH, "<$muninConf"))
135 {
136         print "Sorry, an error occurred reading file $muninConf.\n<br />\nPlease check this script for errors\n";
137 }
138 else
139 {
140         ##########
141         # read the munin configuration file to extract groups and hosts and dbdir
142         my %nodeGroups;
143         my %nodesInGroups;
144         while(<FH>)
145         {
146                 chomp;
147                 if ($_ =~ /^\[(.*);(.*)\]$/)
148                 {
149                         push @{$nodesInGroups{$1}}, $2;
150                         $nodeGroups{$1} = 1;
151                 }
152                 elsif ($_ =~ /^\[(.*)\]$/)
153                 {
154                         my $host = $1;
155                         $host =~ /^([a-zA-Z0-9\-]*)\.(.*)$/;
156                         push @{$nodesInGroups{$2}}, $host;
157                         $nodeGroups{$1} = 1;
158                 }
159                 elsif ($_ =~ /^dbdir\s+(.*)$/)
160                 {
161                         $muninDataFiles = $1;
162                         #debugPrint(1, "munin data files are stored in $muninDataFiles");
163                 }
164         }
165         close(FH);
166
167         ##########
168         # expand the graphTypes table with any extra ones by looking in the
169         # munin data directories
170         foreach my $group ( sort keys %nodeGroups )
171         {
172                 if (-d "$muninDataFiles/$group")
173                 {
174                         #debugPrint(1, "examining rrd files in $muninDataFiles/$group to populate the graphType hash");
175                         chdir "$muninDataFiles/$group";
176                         foreach my $rrdFile (<*>)
177                         {
178                                 #debugPrint(1, "found $1  $2   $3");
179                                 if ($rrdFile =~ /^(.*)-(.*)-(.*)-(.*)$/ )
180                                 {
181                                         if (!defined($graphTypes{$2}))
182                                         {
183                                                 #debugPrint(0, "adding auto-derived graph type $2");
184                                                 $graphTypes{$2} = $2;
185                                         }
186                                 }
187                         }
188                 }
189         }
190
191
192         # present the graphing choices to the user as HTML form
193         print "<form method=\"get\" action=\"\">\n";
194
195         print "Check to refresh every 10 minutes: <input type=\"checkbox\" name=\"refresh\" value=\"1\" " . (defined($refresh) && ($refresh) ? "checked" : "") . "/><br />\n";
196
197         print "Choose node group:&nbsp;";
198         writeSelectArray("nodeGroup", $nodeGroup, keys %nodesInGroups);
199         print "\n<br />\n";
200
201         print "Choose graph type:&nbsp;";
202         writeSelectHash("graphType", $graphType, %graphTypes);
203         print "\n<br />\n";
204
205         print "Choose time scale:&nbsp;";
206         writeSelectHash("timeScale", $timeScale, %timeScales);
207         print "\n<br />\n";
208
209         print "\t<input type=\"submit\" name=\"go\" value=\"go\" />\n</form>\n\n";
210
211
212         # can we render the table of graph images?
213         # i.e. has user selected any graphs to show?
214         if ($graphType ne '')
215         {
216
217                 # get a list of nodes which have rrd files of selected type
218                 chdir("$muninDataFiles/$nodeGroup");
219                 my %rrdFilesByHost;
220                 for my $rrdFile (<*>)
221                 {
222                         $rrdFilesByHost{$1} = 1 if ($rrdFile =~ /^(.*)-$graphType-(.*)-(.*)$/ );
223                 }
224
225                 # lets get rendering!
226                 print "<table>\n";
227                 my $graphCount = 0;
228                 foreach my $nodeName (sort @{$nodesInGroups{$nodeGroup}})
229                 {
230                         $nodeName =~ /^([a-z0-9]*)\.(.*)$/;
231                         print "\t<tr>\n" if (!($graphCount %2));
232                         print "\t\t<td><b>$nodeName</b> <a href=\"$muninBaseUrl/$nodeGroup/$nodeName-$graphType.html\" target=munin_" . $nodeName . '_' .  $graphType . ">details</a><br />\n";
233
234                         # check there's an rrd file for this host/group/type
235                         if (!defined($rrdFilesByHost{$nodeName}))
236                         {
237                                 print "<i>no graph of this type for this node</i>";
238                         }
239                         else
240                         {
241                                 print "<img src=\"$muninBaseUrl/$nodeGroup/$nodeName-$graphType-$timeScale.png\" />\n\t\t</td>\n";
242                         }
243                         print "\t</tr>\n" if ($graphCount %1);
244                         ++$graphCount;
245                 }
246         }
247         else
248         {
249                 print "<i>No graph type chosen or program error</i><br />\n";
250         }
251 }
252
253 print "</body>\n</html>\n";
254
255 # end of muninAggregator