WishList: muninAggregator.2.pl

File muninAggregator.2.pl, 7.5 KB (added by speculatrix, at 2009-10-21T13:29:28+02:00)

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
14use strict;
15use CGI qw(:standard);
16use POSIX ":sys_wait_h";
17use FileHandle;
18use Data::Dumper;
19
20###############################################################################
21# constants
22
23# you'll need to tweak these to suit your installation
24my $muninBaseUrl = ($ENV{'HTTPS'} eq 'on' ? 'https://' : 'http://') . $ENV{'SERVER_NAME'} . '/munin';
25#my $muninBaseUrl = 'https://foo.example.com/munin';
26
27my $muninDataFiles = '/var/lib/munin/db';
28
29# most distros have it here
30my $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
39my %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
57my %timeScales =
58                (
59                  'day'                         => '24 Hours'
60                , 'week'                        => '7 days'
61                , 'month'                       => '31 days'
62                , 'year'                        => 'year'
63                );
64
65my $refreshRate = 10;   # default refresh rate
66
67###############################################################################
68# user-supplied url params
69my $dbgLevel    = (defined(param('debugLevel')))        ? param('debugLevel')   : '0';
70my $graphType   = (defined(param('graphType')))         ? param('graphType')    : '';
71my $timeScale   = (defined(param('timeScale')))         ? param('timeScale')    : '';
72my $nodeGroup   = (defined(param('nodeGroup')))         ? param('nodeGroup')    : '';
73my $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).
78sub     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).
94sub     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
110sub 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;
120STDERR->autoflush;          # already unbuffered in stdio
121
122
123
124print "Content-type: text/html\n\n"
125        . "<html>\n<head>\n";
126
127print "\t<meta http-equiv=\"refresh\" content=\"$refreshRate\">\n" if (defined($refresh) && ($refresh));
128
129print "\t<title>muninAggregator</title>\n"
130        . "</head>\n"
131        . "<body>\n";
132
133
134if (!open(FH, "<$muninConf"))
135{
136        print "Sorry, an error occurred reading file $muninConf.\n<br />\nPlease check this script for errors\n";
137}
138else
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
253print "</body>\n</html>\n";
254
255# end of muninAggregator