source: trunk/nagios-velvice/velvice.cgi @ 258

Last change on this file since 258 was 258, checked in by g7moreau, 6 years ago
  • Small error on cgi path
File size: 11.4 KB
Line 
1#!/usr/bin/env perl
2#
3# 2014/05/15 Gabriel Moreau <Gabriel.Moreau@univ-grenoble-alpes.fr>
4# 2017/06/22 Gabriel Moreau - big update
5# 2018/06/25 Gabriel Moreau - make velvice generic
6#
7# velvice.cgi
8# Copyright (C) 2014-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
9#
10# Need NagiosStatus http://exchange.nagios.org/directory/Addons/APIs/Perl/NagiosStatus-2Epm/details
11# Possible command http://old.nagios.org/developerinfo/externalcommands/commandlist.php
12#
13# apt-get install perl-modules libnagios-object-perl libhtml-parser-perl liburi-encode-perl libcolor-calc-perl libyaml-syck-perl
14
15use strict;
16use warnings;
17use version; our $VERSION = version->declare('0.6.3');
18
19use CGI;
20use HTML::Entities ();
21use Nagios::StatusLog;
22use URI::Encode qw(uri_encode uri_decode);
23use Color::Calc ();
24use YAML::Syck;
25
26my $query           = CGI->new();
27my $cgi_check       = uri_decode($query->param('check'));
28my $cgi_script_name = $query->script_name();
29my $cgi_path        = $cgi_script_name =~ s{/$cgi_script_name$}{}r;
30
31my $config = {};
32$config = YAML::Syck::LoadFile('/etc/nagios3/velvice.yml') if -e '/etc/nagios3/velvice.yml';
33$config->{'nagios-server'}                ||= {};
34$config->{'nagios-server'}{'status-file'} ||= '/var/cache/nagios3/status.dat';
35$config->{'nagios-server'}{'nagios-cmd'}  ||= '/var/lib/nagios3/rw/nagios.cmd';
36$config->{'nagios-server'}{'portal-url'}  ||= $cgi_path =~ s{/cgi-bin/}{/}r;
37$config->{'nagios-server'}{'status-cgi'}  ||= "$cgi_path/status.cgi";
38$config->{'host-mapping'}                 ||= {};
39$config->{'color-downtime'}               ||= {};
40$config->{'color-downtime'}{'min'}        ||= 3;
41$config->{'color-downtime'}{'max'}        ||= 50;
42$config->{'color-downtime'}{'factor'}     ||= 0.7;
43$config->{'remote-action'}                ||= {};
44
45my $log = Nagios::StatusLog->new(
46   Filename => $config->{'nagios-server'}{'status-file'},
47   Version  => 3.0
48   );
49
50sub hostmapping {
51   my $host = shift;
52
53   return exists $config->{'host-mapping'}{$host} ? $config->{'host-mapping'}{$host} : $host;
54   }
55
56sub downtime {
57   my ($time_change) = @_;
58
59   my $now = time;
60   return sprintf '%.1f', ($now - $time_change) / (60 * 3600);
61   }
62
63sub alertcolor {
64   my ($color, $downtime) = @_;
65
66   $downtime = $downtime - $config->{'color-downtime'}{'min'}; # same color first days
67   $downtime = $config->{'color-downtime'}{'max'} if $downtime > $config->{'color-downtime'}{'max'}; # max 50 days for color
68   $downtime =  0 if $downtime <  0;
69
70   my $factor = ($downtime * $config->{'color-downtime'}{'factor'}) / $config->{'color-downtime'}{'max'};
71   return Color::Calc::color_light_html($color, $factor);
72   }
73
74my %hostdown;
75my @serviceproblems;
76my %hostcount;
77my @futurecheck;
78HOST:
79for my $host (sort $log->list_hosts()) {
80   my $host_stat = $log->host($host);
81
82   if ($host_stat->status eq 'DOWN') {TESTIF:{
83      for my $srv ($log->list_services_on_host($host)) {
84         last TESTIF if $log->service($host, $srv)->status eq 'OK' or $log->service($host, $srv)->status eq 'PENDING';
85         }
86
87      $hostdown{$host} = $host_stat;
88      next HOST;
89      }}
90
91   for my $srv ($log->list_services_on_host($host)) {
92      if ($log->service($host, $srv)->status ne 'OK') {
93         push @serviceproblems, $log->service($host, $srv);
94         $hostcount{$host}++;
95         }
96      }
97   }
98
99my $now = time;
100my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $now;
101$year += 1900;
102$mon++;
103my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
104
105my $htmlpage = <<"ENDH";
106Content-Type: text/html
107
108<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
109<html lang="en">
110<head>
111 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
112 <title>Nagios  Velvice</title>
113</head>
114<style type="text/css">
115/* https://stackoverflow.com/questions/14920401/how-to-simulate-hfill-with-html-and-css */
116h1 ul {
117   display: flex;
118   justify-content: space-between;
119   }
120h1 li {
121   display: inline;
122   }
123td.bold {
124   font-weight: bold;
125   }
126</style>
127<body>
128<h1>
129 <ul>
130   <li>Nagios Velvice Alert Panel : <a href="$config->{'nagios-server'}{'portal-url'}">Core Server</a></li>
131   <li><small>(<a href="$cgi_script_name">UPDATE</a> - $date)</small></li>
132 </ul>
133</h1>
134ENDH
135
136my %service_name   = ();
137my %service_status = ();
138for my $srv (@serviceproblems) {
139   $service_name{$srv->service_description}++;
140   $service_status{$srv->status}++;
141   }
142
143if (scalar @serviceproblems == 0) {
144   $htmlpage .= "<p>No alert to recheck.</p>\n";
145   }
146else {
147
148   $htmlpage .= "<p>Alert to recheck - Level:\n";
149   $htmlpage .= join ",\n",
150      " <a href='$cgi_script_name?check=all'>ALL</a><small>(" . scalar(@serviceproblems) . ')</small>',
151      map(" <a href='$cgi_script_name?check=" . lc(uri_encode($_)) . "'>$_</a>($service_status{$_})", sort keys %service_status);
152   $htmlpage .= ".\n";
153   $htmlpage .= " <br />\n";
154   $htmlpage .= " Service:\n";
155   $htmlpage .= join ",\n", map(" <a href='$cgi_script_name?check=" . lc(uri_encode($_)) . "'>$_</a><small>($service_name{$_})</small>", sort keys %service_name);
156   $htmlpage .= ".\n";
157   $htmlpage .= "</p>\n";
158
159   my $nagios_cmd;
160   open $nagios_cmd, '>>', $config->{'nagios-server'}{'nagios-cmd'} or die "Can't open file filename: $!";
161
162   my %remote_sshdown = ();
163   my %remote_db      = ();
164   my $remote_flag;
165
166   my $current_host  = '';
167   $htmlpage .= "<table border=\"1\">\n";
168   SERVICE_PROBLEMS:
169   for my $srv (@serviceproblems) {
170      my $hostname = $srv->host_name;
171      my $service  = $srv->service_description;
172      my $status   = $srv->status;
173      my $downtime = downtime($srv->last_state_change);
174      my $output   = HTML::Entities::encode($srv->plugin_output) =~ s/^[A-Z_\s]+?[:-]//r;
175
176      my $color = $status eq 'CRITICAL' ? '#F88888' : '#FFFF00';
177      $color = alertcolor($color, $downtime);
178      $htmlpage .= " <tr style='background:$color;'>\n";
179      if ($hostname ne $current_host) {
180         $current_host = $hostname;
181         $htmlpage .= "  <td rowspan='$hostcount{$hostname}' style='vertical-align:middle;'><a href=\"$config->{'nagios-server'}{'status-cgi'}?host=$hostname\">$hostname</a></td>\n";
182         }
183
184      my $bold;
185      ACTION_STYLE:
186      for my $act_name (keys %{$config->{'remote-action'}}) {
187         my $act_regex = $config->{'remote-action'}{$act_name}{'regex'};
188         $bold++ if $service =~ m/$act_regex/ and $config->{'remote-action'}{$act_name}{'style'} eq 'bold';
189         }
190      $htmlpage .= $bold ? '  <td class="bold">' : '  <td>';
191      $htmlpage .= "$service</td>\n";
192
193      $htmlpage .= "  <td>$status</td>\n";
194      $htmlpage .= "  <td style='max-width:60%;'><small>$output";
195
196      if (($cgi_check =~ m/all/i)
197            or ($cgi_check =~ m/^$service$/i)
198            or ($cgi_check =~ m/critical/i and $status eq 'CRITICAL')
199            or ($cgi_check =~ m/warning/i  and $status eq 'WARNING')
200            or ($cgi_check =~ m/pending/i  and $status eq 'PENDING')
201            ) {
202         $now++;
203         my $interval = $srv->next_check() - $srv->last_check() || 300;
204         $interval =  240 if $interval <  240;
205         $interval = 3000 if $interval > 3000;
206         my $future = $now + 20 + int(rand($interval - 20)); # 5 * 60 = 300
207
208         $htmlpage .= " -- <b>CHECK</b> [$now/" . ($future - $now) . "]";
209         printf $nagios_cmd "[%lu] SCHEDULE_FORCED_SVC_CHECK;%s;%s;%lu\n", $now, $hostname, $service, $now;
210         # delay future command
211         push @futurecheck, sprintf "[%lu] SCHEDULE_FORCED_SVC_CHECK;%s;%s;%lu", $future, $hostname, $service, $future;
212         }
213
214      ACTION_PUSH_AND_DEPEND:
215      for my $act_name (keys %{$config->{'remote-action'}}) {
216         my $act_regex  = $config->{'remote-action'}{$act_name}{'regex'};
217         my $act_status = $config->{'remote-action'}{$act_name}{'status'} || 'ALL';
218         my $act_depend = $config->{'remote-action'}{$act_name}{'depend'} || 'SSH';
219
220         if ($service =~ m/$act_regex/ and ($act_status eq 'ALL' or $status =~ m/$act_status/)) {
221            $remote_db{$act_name} ||= [];
222            push @{$remote_db{$act_name}}, $hostname;
223            $remote_flag++;
224            }
225
226         # check depend service otherwise
227         $remote_sshdown{$act_depend} ||= {};
228         $remote_sshdown{$act_depend}->{$hostname}++ if $service =~ m/$act_depend/;
229         }
230
231      $htmlpage .= "</small></td>\n";
232      $htmlpage .= "  <td style='text-align:right;'>$downtime days</td>\n";
233      $htmlpage .= " </tr>\n";
234      }
235
236   $htmlpage .= "</table>\n";
237   close $nagios_cmd;
238
239   # host down
240   if (%hostdown) {
241      $htmlpage .= "<br />\n";
242      $htmlpage .= "<table border='1'>\n";
243      HOST_DOWN:
244      for my $host (sort keys %hostdown) {
245         my $host_stat = $hostdown{$host};
246         my $hostname = $host_stat->host_name;
247         my $downtime = downtime($host_stat->last_state_change);
248         my $color = alertcolor('#F88888', $downtime);
249         $htmlpage .= " <tr style='background:$color'>\n";
250         $htmlpage .= "  <td><a href=\"$config->{'nagios-server'}{'status-cgi'}?host=$hostname\">$hostname</a></td>\n";
251         my @host_service;
252         for my $srv ($log->list_services_on_host($host)) {
253            push @host_service, $log->service($host, $srv)->service_description;
254            }
255         $htmlpage .= "  <td><small>" . join(', ', @host_service) . "</small></td>\n";
256         $htmlpage .= "  <td style='text-align:right;'>$downtime days</td>\n";
257         $htmlpage .= " </tr>\n";
258         }
259      $htmlpage .= "</table>\n";
260      }
261
262   # remote action
263   if ($remote_flag) {
264      require Nagios::Object::Config;
265      my $parser = Nagios::Object::Config->new();
266      $parser->parse("/var/cache/nagios3/objects.cache");
267
268      REMOTE_ACTION:
269      for my $act_name (keys %remote_db) {
270         my $act_depend = $config->{'remote-action'}{$act_name}{'depend'} || 'SSH';
271
272         my @action = grep !exists $remote_sshdown{$act_depend}->{$_}, @{$remote_db{$act_name}};
273         if (@action) {
274            my $srv_title = $config->{'remote-action'}{$act_name}{'title'} || "Action: $act_name";
275            $htmlpage .= "<h2>$srv_title</h2>\n";
276            $htmlpage .= "<pre>\n";
277            my $remote_action = $config->{'remote-action'}{$act_name}{'command'};
278            $remote_action = $config->{'remote-action'}{$act_name}{'command-one'}
279               if @action == 1 and exists $config->{'remote-action'}{$act_name}{'command-one'};
280            my @hosts;
281            for my $host (@action) {
282               my $object = $parser->find_object("$host", "Nagios::Host");
283               push @hosts, hostmapping($object->address =~ s/\..*$//r);
284               }
285            my $hosts_list = join ' ', @hosts;
286            $htmlpage .= ' ' . $remote_action =~ s{\%m}{$hosts_list}r;
287            $htmlpage .= "</pre>\n";
288            }
289         }
290      }
291   }
292
293$htmlpage .= <<'ENDH';
294</body>
295</html>
296ENDH
297
298print $htmlpage;
299
300# delayed future check
301if (@futurecheck) {
302   sleep 2;
303   my $nagios_cmd;
304   open $nagios_cmd, '>>', $config->{'nagios-server'}{'nagios-cmd'} or die "Can't open file filename: $!";
305   print $nagios_cmd "$_\n" for @futurecheck;
306   close $nagios_cmd;
307   }
308
309__END__
310
311
312=head1 NAME
313
314velvice.cgi - nagios velvice alert panel
315
316
317=head1 DESCRIPTION
318
319Nagios VELVICE is an acronym for "Nagios leVEL serVICE status".
320Homepage: http://servforge.legi.grenoble-inp.fr/projects/soft-trokata/wiki/SoftWare/NagiosVelvice
321
322=head1 AUTHORS
323
324Written by Gabriel Moreau - Grenoble - France
325
326
327=head1 LICENSE AND COPYRIGHT
328
329Licence GNU GPL version 2 or later and Perl equivalent
330
331Copyright (C) 2014-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.