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

Last change on this file since 378 was 378, checked in by g7moreau, 6 years ago
  • Update consolelog
  • Property svn:keywords set to Id
File size: 21.9 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# 2018/11/03 Gabriel Moreau - ajax
7#
8# velvice.cgi
9# Copyright (C) 2014-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
10#
11# Need NagiosStatus http://exchange.nagios.org/directory/Addons/APIs/Perl/NagiosStatus-2Epm/details
12# Possible command http://old.nagios.org/developerinfo/externalcommands/commandlist.php
13#
14# apt-get install perl-modules libnagios-object-perl libhtml-parser-perl liburi-encode-perl libcolor-calc-perl libyaml-syck-perl
15# apt-get install libdatetime-event-recurrence-perl libdatetime-set-perl
16
17use strict;
18use warnings;
19use version; our $VERSION = version->declare('0.10.6');
20
21use CGI;
22use HTML::Entities ();
23use Nagios::StatusLog;
24use URI::Encode qw(uri_encode uri_decode);
25use Color::Calc ();
26use YAML::Syck;
27
28my $query           = CGI->new();
29my $cgi_check       = uri_decode($query->param('check'));
30my $cgi_script_name = $query->script_name();
31my $cgi_path        = $cgi_script_name =~ s{/[^/]+\.cgi$}{}r;
32my $cgi_only;
33$cgi_only++ if uri_decode($query->param('only')) eq 'body';
34undef $query;
35
36my %STATUS_DB = (
37   CRITICAL => {id => 3, color => '#F88888'},
38   WARNING  => {id => 2, color => '#FFFF00'},
39   PENDING  => {id => 1, color => '#E0E0E0'},
40   );
41
42my $config = {};
43$config = YAML::Syck::LoadFile('/etc/nagios3/velvice.yml') if -e '/etc/nagios3/velvice.yml';
44$config->{'nagios-server'}                ||= {};
45$config->{'nagios-server'}{'status-file'} ||= '/var/cache/nagios3/status.dat';
46$config->{'nagios-server'}{'nagios-cmd'}  ||= '/var/lib/nagios3/rw/nagios.cmd';
47$config->{'nagios-server'}{'portal-url'}  ||= $cgi_path =~ s{/cgi-bin/}{/}r . '/';
48$config->{'nagios-server'}{'status-cgi'}  ||= "$cgi_path/status.cgi";
49$config->{'nagios-server'}{'stylesheets'} ||= $config->{'nagios-server'}{'portal-url'} =~ s{/?$}{/stylesheets}r;
50$config->{'nagios-server'}{'image'}       ||= $config->{'nagios-server'}{'portal-url'} =~ s{/?$}{/images}r;
51$config->{'host-mapping'}                 ||= {};
52$config->{'color-downtime'}               ||= {};
53$config->{'color-downtime'}{'day-min'}    ||=  3;
54$config->{'color-downtime'}{'day-max'}    ||= 50;
55$config->{'color-downtime'}{'factor'}     ||=  0.7;
56$config->{'remote-action'}                ||= {};
57$config->{'refresh'}                      ||=  0;
58
59sub hostmapping {
60   my $host = shift;
61
62   return exists $config->{'host-mapping'}{$host} ? $config->{'host-mapping'}{$host} : $host;
63   }
64
65sub downtime {
66   my ($time_change) = @_;
67
68   my $now = time;
69   return sprintf '%.1f', ($now - $time_change) / (60 * 3600);
70   }
71
72sub alertcolor {
73   my ($status, $downtime) = @_;
74
75   my $color = '#0000FF';
76   $color = $STATUS_DB{$status}->{'color'} if exists $STATUS_DB{$status};
77
78   $downtime = $downtime - $config->{'color-downtime'}{'day-min'}; # same color first days
79   $downtime = $config->{'color-downtime'}{'day-max'} if $downtime > $config->{'color-downtime'}{'day-max'}; # max 50 days for color
80   $downtime =  0 if $downtime <  0;
81
82   my $factor = ($downtime * $config->{'color-downtime'}{'factor'}) / $config->{'color-downtime'}{'day-max'};
83   return Color::Calc::color_light_html($color, $factor);
84   }
85
86sub nosbreak {
87   my ($str) = @_;
88   
89   return $str =~ s/\s/\&nbsp;/gr;
90   }
91
92my $log = Nagios::StatusLog->new(
93   Filename => $config->{'nagios-server'}{'status-file'},
94   Version  => 3.0
95   );
96
97# refresh configuration
98if (exists $config->{'refreshments'}) {
99   require DateTime::Event::Recurrence;
100   require DateTime::SpanSet;
101
102   my @refreshments;
103   SET:
104   for my $set (@{$config->{'refreshments'}}) {
105      my $start   = DateTime::Event::Recurrence->weekly(days => $set->{'days'}, hours => $set->{'start'});
106      my $end     = DateTime::Event::Recurrence->weekly(days => $set->{'days'}, hours => $set->{'end'});
107      my $spanset = DateTime::SpanSet->from_sets(start_set => $start, end_set => $end);
108      push @refreshments, {refresh => $set->{'refresh'}, spanset => $spanset};
109      }
110
111   my $now = DateTime->now(time_zone => 'local');
112   SET:
113   for my $set (@refreshments) {
114      next SET if not $set->{'spanset'}->contains($now);
115 
116      $config->{'refresh'} = $set->{'refresh'};
117      last SET;
118      }
119   }
120
121my %hostdown;
122my @serviceproblems;
123my %hostcount;
124my @futurecheck;
125HOST:
126for my $host (sort $log->list_hosts()) {
127   my $host_stat = $log->host($host);
128
129   if ($host_stat->status eq 'DOWN') {TESTIF:{
130      for my $srv ($log->list_services_on_host($host)) {
131         last TESTIF if $log->service($host, $srv)->status eq 'OK' or $log->service($host, $srv)->status eq 'PENDING';
132         }
133
134      $hostdown{$host} = $host_stat;
135      next HOST;
136      }}
137
138   SRV:
139   for my $srv ($log->list_services_on_host($host)) {
140      my $status = $log->service($host, $srv)->status;
141
142      next SRV if $status eq 'OK';
143
144      push @serviceproblems, $log->service($host, $srv);
145   
146      my $downtime = downtime($log->service($host, $srv)->last_state_change);
147      my $color    = alertcolor($status, $downtime);
148
149      my $status_id = 0;
150      $status_id = $STATUS_DB{$status}->{'id'} if exists $STATUS_DB{$status};
151
152      #$hostcount{$host}++;
153      $hostcount{$host} ||= {count => 0, color => $color, status_id => $status_id, downtime => $downtime};
154      $hostcount{$host}->{'count'}++;
155      if (($status_id >= $hostcount{$host}->{'status_id'}) and ($downtime < $hostcount{$host}->{'downtime'})) {
156         $hostcount{$host}->{'downtime'}  = $downtime;
157         $hostcount{$host}->{'status_id'} = $status_id;
158         $hostcount{$host}->{'color'}     = $color;
159         }
160      }
161   }
162
163my $now = time;
164my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $now;
165$year += 1900;
166$mon++;
167# my $date_sec = sprintf '%04i-%02i-%02i %02i:%02i:%02i', $year, $mon, $mday, $hour, $min, $sec;
168my $date_min = nosbreak(sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min);
169
170my $htmlpage;
171
172$htmlpage .= <<"ENDH" if not $cgi_only;
173Content-Type: text/html
174
175<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
176<html lang="en">
177<head>
178 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
179ENDH
180
181$htmlpage .= <<"ENDH" if $cgi_only;
182Content-Type: text/xml
183
184<?xml version="1.0" encoding="utf-8"?>
185<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
186ENDH
187
188#$htmlpage .= " <meta http-equiv=\"Refresh\" content=\"$config->{'refresh'}\">" if $config->{'refresh'} > 59; # minimum 1 min
189$htmlpage .= <<"ENDH" if not $cgi_only;
190 <title>Nagios  Velvice</title>
191 <link rel="stylesheet"    type="text/css"  href="$config->{'nagios-server'}{'stylesheets'}/velvice.css">
192 <link rel="shortcut icon" type="image/ico" href="$config->{'nagios-server'}{'image'}/favicon.ico">
193 <script type="text/javascript">
194   var refresh_sec = 900; // 15 min = 900 s
195   var refresh_interval;
196   function refresh() {
197      var req = new XMLHttpRequest();
198      var datenow = new Date();
199      console.log("Grabbing Value");
200      req.open("GET", '$cgi_script_name?only=body', true); // Grabs whatever you've written in this file
201      req.onload = function () {
202         if (req.status == 200) {
203            document.getElementById('master-body').innerHTML = req.responseXML.getElementById('master-body').innerHTML;
204            console.log("Update Page at", datenow.toLocaleTimeString());
205
206            refresh_next = req.responseXML.getElementById('master-body').getAttribute("refresh");
207            if (refresh_next < 60 && refresh_next != 0) {
208               refresh_next = 60;
209               }
210            if (refresh_next != refresh_sec) {
211               refresh_sec = refresh_next;
212               clearInterval(refresh_interval);
213               refresh_interval = setInterval(refresh, refresh_sec * 1000);
214               console.log("Update at", datenow.toLocaleTimeString(), "Refresh Interval:", refresh_sec);
215               document.getElementById('master-body').setAttribute("refresh", refresh_sec);
216               }
217            }
218         }
219      req.send(null);
220      }
221
222   function init() { // This is the function the browser first runs when it's loaded.
223      refresh_sec = document.getElementById('master-body').getAttribute("refresh");
224      if (refresh_sec < 60 && refresh_sec != 0) {
225         refresh_sec = 60;
226         }
227      // Set the refresh() function to run every 900 seconds. 1 second would be 1000
228      refresh_interval = setInterval(refresh, refresh_sec * 1000);
229      console.log("Start Refresh Interval:", refresh_sec);
230      }
231</script>
232</head>
233ENDH
234
235$htmlpage .= <<"ENDH";
236<body id="master-body" onload="init()" refresh="$config->{'refresh'}">
237<div class="header">
238 <h1>
239  <ul>
240    <li>Nagios Velvice Alert Panel : <a href="$config->{'nagios-server'}{'portal-url'}">Core Server</a></li>
241    <li><small><a id="refresh" href="$cgi_script_name">$date_min</a></small></li>
242  </ul>
243 </h1>
244</div>
245ENDH
246
247my %service_name   = ();
248my %service_status = ();
249for my $srv (@serviceproblems) {
250   $service_name{$srv->service_description}++;
251   $service_status{$srv->status}++;
252   }
253
254if (scalar @serviceproblems == 0) {
255   $htmlpage .= "<p>No alert to recheck.</p>\n";
256   }
257else {
258
259   $htmlpage .= "<p>Alert to recheck - Level:\n";
260   $htmlpage .= join ",\n",
261      " <span class='button'><a href='$cgi_script_name?check=all'>ALL</a><small>" . scalar(@serviceproblems) . '</small></span>',
262      map(" <span class='button'><a href='$cgi_script_name?check=" . lc(uri_encode($_)) . "'>$_</a><small>$service_status{$_}</small></span>",
263         sort keys %service_status);
264   $htmlpage .= ".\n";
265   $htmlpage .= " <br />\n";
266   $htmlpage .= " Service:\n";
267   $htmlpage .= join ",\n",
268      map(" <span class='button'><a href='$cgi_script_name?check=" . lc(uri_encode($_)) . "'>" . nosbreak($_) . "</a><small>$service_name{$_}</small></span>",
269         sort keys %service_name);
270   $htmlpage .= ".\n";
271   $htmlpage .= "</p>\n";
272
273   my $nagios_cmd;
274   open $nagios_cmd, '>>', $config->{'nagios-server'}{'nagios-cmd'} or die "Can't open file filename: $!";
275
276   my %remote_sshdown = ();
277   my %remote_db      = ();
278   my $remote_flag;
279
280   my $current_host  = '';
281   $htmlpage .= "<table border=\"1\">\n";
282   SERVICE_PROBLEMS:
283   for my $srv (@serviceproblems) {
284      my $hostname = $srv->host_name;
285      my $service  = $srv->service_description;
286      my $status   = $srv->status;
287      my $downtime = downtime($srv->last_state_change);
288      my $output   = HTML::Entities::encode($srv->plugin_output) =~ s/^[A-Z_\s]+?[:-]//r;
289
290      my $color = alertcolor($status, $downtime);
291      my $stylecolor = "style='background:$color;'";
292      $htmlpage .= " <tr>\n";
293      if ($hostname ne $current_host) {
294         $current_host  = $hostname;
295         my $rowspan    = $hostcount{$hostname}->{'count'};
296         my $rowcolor   = "style='background:" . $hostcount{$hostname}->{'color'} . ";'";
297         $htmlpage .= "  <td $rowcolor rowspan='$rowspan'>"
298            . "<a href=\"$cgi_script_name?check=" . uri_encode($hostname) . '">&#8623;</a></td>' . "\n";
299         $htmlpage .= "  <td $rowcolor class='hoop' rowspan='$rowspan'>"
300            . "<a href=\"$config->{'nagios-server'}{'status-cgi'}?host=" . uri_encode($hostname) . "\">$hostname</a></td>\n";
301         }
302
303      my $bold;
304      ACTION_STYLE:
305      for my $act_name (keys %{$config->{'remote-action'}}) {
306         my $act_regex = $config->{'remote-action'}{$act_name}{'regex'};
307         $bold++ if $service =~ m/$act_regex/ and $config->{'remote-action'}{$act_name}{'style'} eq 'bold';
308         }
309      $htmlpage .= $bold ? "  <td $stylecolor class='hoop bold'>" : "  <td $stylecolor class='hoop'>";
310      $htmlpage .= "$service</td>\n";
311
312      $htmlpage .= "  <td $stylecolor class='hoop'>$status</td>\n";
313      $htmlpage .= "  <td $stylecolor class='comment'>$output</td>\n";
314      $htmlpage .= "  <td $stylecolor class='days'>$downtime days</td>\n";
315
316      if (($cgi_check =~ m/all/i)
317            or ($cgi_check =~ m/^$service$/i)
318            or ($cgi_check =~ m/critical/i and $status eq 'CRITICAL')
319            or ($cgi_check =~ m/warning/i  and $status eq 'WARNING')
320            or ($cgi_check =~ m/pending/i  and $status eq 'PENDING')
321            or ($cgi_check eq $hostname    and $status =~ m/^(CRITICAL|WARNING|PENDING)$/)
322            ) {
323         $now++;
324         my $interval = $srv->next_check() - $srv->last_check() || 300; # 5 * 60 = 300
325         $interval =  240 if $interval <  240;
326         $interval = 3000 if $interval > 3000;
327         my $future = $now + 20 + int(rand($interval - 20));
328
329         $htmlpage .= "  <td class='checking'>" . ($future - $now) . "</td>\n";
330         #$htmlpage .= " -- <b>CHECK</b> [$now/" . ($future - $now) . "]";
331         printf $nagios_cmd "[%lu] SCHEDULE_FORCED_SVC_CHECK;%s;%s;%lu\n", $now, $hostname, $service, $now;
332         # delay future command
333         push @futurecheck, sprintf "[%lu] SCHEDULE_FORCED_SVC_CHECK;%s;%s;%lu", $future, $hostname, $service, $future;
334         }
335
336      ACTION_PUSH_AND_DEPEND:
337      for my $act_name (keys %{$config->{'remote-action'}}) {
338         my $act_regex  = $config->{'remote-action'}{$act_name}{'regex'};
339         my $act_status = $config->{'remote-action'}{$act_name}{'status'} || 'ALL';
340         my $act_depend = $config->{'remote-action'}{$act_name}{'depend'} || 'SSH';
341
342         if ($service =~ m/$act_regex/ and ($act_status eq 'ALL' or $status =~ m/$act_status/)) {
343            $remote_db{$act_name} ||= [];
344            push @{$remote_db{$act_name}}, $hostname;
345            $remote_flag++;
346            }
347
348         # check depend service otherwise
349         $remote_sshdown{$act_depend} ||= {};
350         $remote_sshdown{$act_depend}->{$hostname}++ if $service =~ m/$act_depend/;
351         }
352
353      $htmlpage .= " </tr>\n";
354      }
355
356   $htmlpage .= "</table>\n";
357   close $nagios_cmd;
358
359   # host down
360   if (%hostdown) {
361      $htmlpage .= "<br />\n";
362      $htmlpage .= "<table border='1'>\n";
363      HOST_DOWN:
364      for my $host (sort keys %hostdown) {
365         my $host_stat = $hostdown{$host};
366         my $hostname = $host_stat->host_name;
367         my $downtime = downtime($host_stat->last_state_change);
368         my $color = alertcolor('CRITICAL', $downtime);
369         $htmlpage .= " <tr style='background:$color'>\n";
370         $htmlpage .= "  <td class='hoop'><a href=\"$config->{'nagios-server'}{'status-cgi'}?host=" . uri_encode($hostname) . "\">$hostname</a></td>\n";
371         my @host_service;
372         for my $srv ($log->list_services_on_host($host)) {
373            push @host_service, $log->service($host, $srv)->service_description;
374            }
375         $htmlpage .= "  <td><small>" . join(', ', @host_service) . "</small></td>\n";
376         $htmlpage .= "  <td style='text-align:right;'>$downtime days</td>\n";
377         $htmlpage .= " </tr>\n";
378         }
379      $htmlpage .= "</table>\n";
380      }
381
382   # remote action
383   if ($remote_flag) {
384      require Nagios::Object::Config;
385      my $parser = Nagios::Object::Config->new();
386      $parser->parse("/var/cache/nagios3/objects.cache");
387
388      $htmlpage .= "<div class='action'>\n";
389      REMOTE_ACTION:
390      for my $act_name (keys %remote_db) {
391         my $act_depend = $config->{'remote-action'}{$act_name}{'depend'} || 'SSH';
392
393         my @action = grep !exists $remote_sshdown{$act_depend}->{$_}, @{$remote_db{$act_name}};
394         if (@action) {
395            my $srv_title = $config->{'remote-action'}{$act_name}{'title'} || "Action: $act_name";
396            $htmlpage .= "<h2>$srv_title</h2>\n";
397            $htmlpage .= "<pre>\n";
398            my $remote_action = $config->{'remote-action'}{$act_name}{'command'};
399            $remote_action = $config->{'remote-action'}{$act_name}{'command-one'}
400               if @action == 1 and exists $config->{'remote-action'}{$act_name}{'command-one'};
401            my @hosts;
402            for my $host (@action) {
403               my $object = $parser->find_object("$host", "Nagios::Host");
404               push @hosts, hostmapping($object->address =~ s/\..*$//r);
405               }
406            my $hosts_list = join ' ', @hosts;
407            $htmlpage .= ' ' . $remote_action =~ s{\%m}{$hosts_list}r;
408            $htmlpage .= "</pre>\n";
409            }
410         }
411      $htmlpage .= "</div>\n";
412      }
413   }
414
415$htmlpage .= <<"ENDH";
416<hr clear="all" />
417<div class="footer">
418 <b><a href="http://servforge.legi.grenoble-inp.fr/projects/soft-trokata/wiki/SoftWare/NagiosVelvice">Velvice</a>
419   - version: $VERSION</b>
420   (<a href="http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/nagios-velvice/velvice.html">online manual</a>)
421   - Written by Gabriel Moreau
422 <ul>
423  <li>Licence GNU GPL version 2 or later and Perl equivalent</li>
424  <li>Copyright (C) 2014-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France</li>
425 </ul>
426</div>
427</body>
428ENDH
429
430$htmlpage .= <<"ENDH" if not $cgi_only;
431</html>
432ENDH
433
434print $htmlpage;
435
436# delayed future check
437if (@futurecheck) {
438   sleep 2;
439   my $nagios_cmd;
440   open $nagios_cmd, '>>', $config->{'nagios-server'}{'nagios-cmd'} or die "Can't open file filename: $!";
441   print $nagios_cmd "$_\n" for @futurecheck;
442   close $nagios_cmd;
443   }
444
445__END__
446
447
448=head1 NAME
449
450velvice.cgi - nagios velvice alert panel
451
452=head1 USAGE
453
454 velvice.cgi
455 velvice.cgi?check=XXX
456
457
458=head1 DESCRIPTION
459
460=begin html
461
462<img width="700" alt="Nagios Velvice Alert Panel" title="Nagios Velvice Alert Panel" style="float:right" src="velvice.png" />
463
464=end html
465
466Nagios VELVICE is an acronym for "Nagios leVEL serVICE status".
467
468The Nagios web page is sometimes very graphically charged
469and does not necessarily contain the information you need at a glance.
470For example, it is quite complicated to restart controls on multiple hosts in one click.
471
472For example, a server that is down should take only one line and not one per service...
473Similarly, a service that has been down for 5 minutes or since yesterday
474has more weight than a service that has fallen for 15 days.
475
476With Velvice Panel, a broken down server takes only one line.
477Services that have been falling for a long time gradually lose their color and become pastel colors.
478
479With Velvice Panel, it is possible through a single click
480to redo a check of all services that are in the CRITICAL state.
481Similarly, it is possible to restart a check on all SSH services in breakdowns ...
482In order not to clog the Nagios server, checks are shifted by 2 seconds in time.
483
484There is also a link to the web page of the main Nagios server.
485For each computer, you have a direct link to its dedicated web page on this server.
486
487
488=head1 CONFIGURATION FILE SPECIFICATION
489
490The configuration file must be F</etc/nagios3/velvice.yml>.
491This is not a required file.
492The file is in YAML format because this is a human-readable text file style.
493Other formats could have been Plain XML, RDF, JSON... but they are much less readable.
494
495You can find in the software nagios-velvice an example of configuration:
496L<velvice.sample.yml|http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/nagios-velvice/velvice.sample.yml>.
497This one is in fact the master reference specification!
498
499The main keys C<nagios-server> and C<color-downtime> have good default values.
500No secondary key is required...
501The Velvice script try hard to replace ~ by the good value automatically.
502
503 nagios-server:
504   status-file: /var/cache/nagios3/status.dat
505   nagios-cmd:  /var/lib/nagios3/rw/nagios.cmd
506   portal-url:  ~/nagios3/
507   status-cgi:  ~/cgi-bin/nagios3/status.cgi
508   stylesheets: ~/nagios3/stylesheets
509
510The background color of the faulty service line display remains stable with a bright color for at least 3 days.
511Then, it decreases and becomes pastel after 53 days with an intensity of 70% (100% is white and 0% is black).
512
513 color-downtime:
514   day-min:  3
515   day-max: 50
516   factor:   0.7
517
518With key C<host-mapping>,
519it's good to map C<localhost> to the real name of the computer (hostname).
520
521 host-mapping:
522   localhost:  srv-nagios
523   toto:       titi
524
525The only important key is C<remote-action>.
526You can affiliate as many subkeys as you want.
527Let's take an example:
528
529 remote-action:
530   oom-killer:
531     regex: ^OOM Killer
532     title:  OOM Killer
533     command:     tssh -c 'sudo rm /var/lib/nagios3/nagios_oom_killer.log' %m
534     command-one: ssh %m 'sudo rm /var/lib/nagios3/nagios_oom_killer.log'
535     depend: ^SSH
536     status: ALL
537     style: bold
538
539C<oom-killer> is just a key for your remote action.
540The regex is used to find which service has a problem...
541The title is use in the result web page (not mandatory - otherwise, it will be C<Action: oom-killer>).
542The C<command> is just written on this web page.
543You have the responsibility to copy / cut it on a terminal.
544For security reasons, the nagios server does not have the right to launch the command on the remote host.
545The wildcard C<%m> is replaced by the list of the host (separated by the space).
546Sometime, the command could be different if there is only one computer (just SSH and no parallel SSH).
547If your command is based on SSH,
548you can have an SSH action only if the remote SSH is running.
549So you can make the remote action depend on the SSH service through a regular expression of your choice.
550
551The last two keys.
552The C<status> key is for CRITICAL or WARNING (or ALL).
553The key C<style> is there to mark in bold the service in error on the web page.
554
555=head1 SEE ALSO
556
557yamllint(1), ysh(1), YAML, Nagios::StatusLog, Color::Calc
558
559In Debian GNU/Linux distribution, packages for C<yamllint> and C<ysh> are:
560
561=over
562
563=item * C<yamllint> - Linter for YAML files (Python)
564
565=item * C<libyaml-shell-perl> - YAML test shell (Perl)
566
567=back
568
569
570Own project ressources:
571
572=over
573
574=item * L<Web Site|http://servforge.legi.grenoble-inp.fr/projects/soft-trokata/wiki/SoftWare/NagiosVelvice>
575
576=item * L<Online Manual|http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/nagios-velvice/velvice.html>
577
578=item * L<SVN Repository|http://servforge.legi.grenoble-inp.fr/svn/soft-trokata/trunk/nagios-velvice>
579
580=item * L<Debian Package|http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/nagios-velvice/download/>
581
582=back
583
584
585=head1 VERSION
586
587$Id: velvice.cgi 378 2018-11-04 19:08:17Z g7moreau $
588
589
590=head1 AUTHOR
591
592Written by Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>, LEGI UMR 5519, CNRS, Grenoble - France
593
594
595=head1 LICENSE AND COPYRIGHT
596
597Licence GNU GPL version 2 or later and Perl equivalent
598
599Copyright (C) 2014-2018, LEGI UMR 5519 / CNRS UGA G-INP, Grenoble, France
Note: See TracBrowser for help on using the repository browser.