nagios-fifo.pl 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. #!/usr/bin/perl
  2. # What this script does :
  3. # -----------------------
  4. # 1/ check nagios.log
  5. # 2/ setup an array of alerts
  6. # 3/ ability to remove-change alerts on new notification for same host/[service]
  7. # 4/ dialog with livestatus to replay full alerts on load/setup : irssi command /nagrefresh
  8. # 5/ define !recheck to nagios
  9. # 6/ ability to define chan/server on irssi configuration instead of bot config.
  10. #
  11. # What it should do :
  12. # -------------------
  13. # TODO
  14. # define !ack irc command
  15. # use %ACK_INDEX by alert type HOST / SERVICE
  16. # use %ACK_INDEX_LEVEL by alert type CRIT/UP/DOWN/WARN/UNK…
  17. use strict;
  18. use vars qw($VERSION %IRSSI);
  19. use Irssi;
  20. use POSIX "sys_wait_h";
  21. use POSIX qw(strftime);
  22. use Term::ANSIColor qw/ :constants /;
  23. use Fcntl; # provides `O_NONBLOCK' and `O_RDONLY' constants
  24. ##########################################################################
  25. # Part A : script configuration / Prototypes
  26. # Put signals to irssi ###################################################
  27. our ( $FIFO, # fifo absolute filename (expanded from Irssi config)
  28. $FIFO_HANDLE, # fifo filehandle for `open' et al.
  29. $FIFO_TAG ); # fifo signal tag for `input_add'
  30. $VERSION = "0.4.2";
  31. %IRSSI = (
  32. authors => 'asr',
  33. contact => 'root@lautre.net',
  34. name => 'nagios-ack',
  35. description => 'ack nagios alerts in irc / follow nagios log',
  36. license => 'GPLv2',
  37. url => 'http://www.lautre.net/',
  38. changed => '20141006',
  39. modules => ''
  40. );
  41. my $PRINTF = "/usr/bin/printf";
  42. # Put signals to irssi ###################################################
  43. Irssi::settings_add_str($IRSSI{name}, # default fifo_remote_file
  44. 'fifo_remote_file', '/home/tc-14/var/nagios-fifo'); #
  45. Irssi::settings_add_str("nagios_ack", "nagios_ack_channel", "#main_channel"); # see also etc/config.template
  46. Irssi::settings_add_str("nagios_ack", "nagios_ack_nick", ""); # any user (none by default)
  47. Irssi::settings_add_str("nagios_ack", "nagios_live", "/var/lib/nagios3/rw/live");
  48. Irssi::settings_add_str("nagios_ack", "nagios_command", "/var/lib/nagios3/rw/nagios.cmd");
  49. #Irssi::command_bind( 'ack', \&nagios_ack );
  50. #Irssi::command_bind( 'nagstat', \&nagios_status );
  51. Irssi::command_bind( 'nagrefresh', \&nagios_query_status );
  52. # CONFIG: point this where your Nagios configuration files live
  53. #my $nagioslog = "/var/log/nagios3/nagios.log";
  54. # To be used to check immediately / hosts-stats
  55. my $nagioscmd = Irssi::settings_get_str("nagios_command");
  56. my $nagioslive = Irssi::settings_get_str("nagios_live");
  57. my $nagios_ack_channel= Irssi::settings_get_str("nagios_ack_channel");
  58. my $nagios_ack_nick= Irssi::settings_get_str("nagios_ack_nick");
  59. my %renot; # { "host" or "host:service" => time_last_notification }
  60. my @cmdqueue = ();
  61. my %ignore = ();
  62. my @ACKS; # [ hostname, service, state, plugin_output, last_state_change, ACKnowledged ] );
  63. my %ACK_Ind;
  64. my %C = (
  65. K => "\x0301", # 00. White
  66. B => "\x0302", # 01. Black
  67. G => "\x0303", # 02. Blue (Navy)
  68. R => "\x0304", # 03. Green
  69. # 04. Red
  70. V => "\x0306", # 05. Brown (Maroon)
  71. O => "\x0307", # 06. Purple
  72. Y => "\x0308", # 07. Orange
  73. Gg => "\x0309", # 08. Yellow
  74. # 09. Light Green (Lime)
  75. C => "\x0311", # 10. Teal (Green/Blue Cyan)
  76. Bb => "\x0312", # 11. Light Cyan (Cyan) (Aqua)
  77. Ma => "\x0313", # 12. Light Blue (Royal)
  78. Gr => "\x0314", # 13. Pink (Light Purple) (Fuchsia)
  79. W => "\x0315", # 14. Grey
  80. # 15. Light Grey (Silver)
  81. Z => "\x03",
  82. );
  83. # Numeric to string conversion of nagios status
  84. # 0..4 : hosts
  85. # 10..14 : services
  86. my @NagStates = qw/UP DOWN WARNING UNKNOWN .. .. .. .. .. ..
  87. OK WARNING CRITICAL UNKNOWN/;
  88. # Bold: U+0002 ("0x02") — Example: ^Bold Text^ whereas ^ represents the control character.
  89. # Italics: U+001D ("0x1D") — Example: ^Italicized Text^ whereas ^ represents the control character.
  90. # Underline: U+001F ("0x1F") — Example: ^Underlined Text^ whereas ^ represents the control character.
  91. # The control character used for color is U+0003 ("0x03").
  92. my $state_to_color = {
  93. OK => $C{G},
  94. UP => $C{G},
  95. WARNING => $C{Y},
  96. CRITICAL => $C{R},
  97. DOWN => $C{V},
  98. UNKNOWN => $C{Gr},
  99. };
  100. # Global use to limit memory use. Probably a bad idea : better to check
  101. # where is the memory leak.
  102. my ($status_line);
  103. my @match;
  104. my $msg;
  105. my $i;
  106. my $stat;
  107. my $message;
  108. my ($type,$data);
  109. my ($d,$type,$host,$service,$state,$output);
  110. my ( $host, $svc, $state, $id );
  111. my ($server, $nick, $addr, $target);
  112. my ( $param, $server, $window );
  113. my (@issue, $issue);
  114. #my $state_to_color = { OK => '', UP => '', WARNING => '', CRITICAL => '', DOWN => '', UNKNOWN => '' };
  115. # Alert Acknowledment ####################################################
  116. my $last_alert;
  117. my $DEBUG=Irssi::settings_get_str("nagios_ack_nick");
  118. # Simple subs ############################################################
  119. sub TRUE() { 1 } # some constants [perlsyn(1)
  120. sub FALSE() { "" } # "Constant Functions"]
  121. sub DEBUG(@) { print "%B", join(":", @_),"%n" }# DEBUG thingy
  122. sub time2date($) {
  123. ($d)=@_;
  124. return strftime("%d/%m/%y %H:%M", localtime($d));
  125. }
  126. ##########################################################################
  127. # Part B : user Actions ##################################################
  128. # Acknowledge alerts #####################################################
  129. # Global use to limit memory use. Probably a bad idea : better to check
  130. # where is the memory leak.
  131. # Four unused subs. Still there to be implemented later
  132. sub parse_status();
  133. sub nagios_ack($$$);
  134. sub nagios_check($$$);
  135. sub nagios_inject;
  136. #sub nagios_status();
  137. # Return alert ID "[FF01]"
  138. # F : caracter to display (! : ; C : change ; + : new alert ; - : green flag ; ? : green but not found)
  139. # I : numeric ID
  140. # state : state string, from @NagStates or %state_to_color
  141. sub format_alert_id($$$$); # ($Flag,$I,$state,$acked)
  142. # is_alert : is this alert in the DB ?
  143. # returns ID of alert ; -1 if not.
  144. sub is_alert($$$$$$) ;
  145. ##########################################################################
  146. # Part C : Logfile management ############################################
  147. # disable fifo and erase fifo file
  148. sub destroy_fifo($); # [2004-08-14]
  149. # Open logfile/fifo
  150. sub open_fifo($); # [2004-08-14]
  151. # read from fifo
  152. # (called by fifo input signal)
  153. sub read_fifo(); # [2004-08-14]
  154. # Read logfile ###########################################################
  155. # create named fifo and open it for input
  156. # (called on script load and fifo name changes)
  157. sub create_fifo($); # [2004-08-14]
  158. # Query nagios ###########################################################
  159. sub nagios_query_status($);
  160. ##########################################################################
  161. # Part D : Alert processing ##############################################
  162. # display a line resuming the alert
  163. # ($server,$chan,$alert_id,$prefix) -> alert_id from @ACKS
  164. sub display_alert($$$$);
  165. # Search for services/host informations, and post them to IRC ############
  166. # Delimiter : ; for logs; @ for direct nagios custom notification command
  167. # one parameter : the logline to parse.
  168. sub parse_nagios_log($);
  169. # Use alert fields to print and insert it
  170. # timestamp, alert-type, hostname, state, plugin output, service_name
  171. sub handle_alert($$$$$$$$); # $d,$type,$host,$state,$output,$service,$acked
  172. ##########################################################################
  173. # IRSSI Events ###########################################################
  174. # create new fifo (erase any old) and get command prefix
  175. # (called on script loading and on user /set)
  176. sub setup(); # [2004-08-13]
  177. # Interact with IRC chan users
  178. sub event_privmsg ($$$$);
  179. ##########################################################################
  180. # References #############################################################
  181. ##########################################################################
  182. # REFERENCES
  183. # ----------
  184. #
  185. # This script is mainly adapted from 3 other scripts related to nagios :
  186. #
  187. # https://github.com/zorkian/nagios-irc-bot/blob/master/nagiosirc.pl
  188. # http://www.update.uu.se/~zrajm/programs/irssi-scripts/fifo_remote.pl-0.5
  189. # https://github.com/mikegrb/irssi-scripts/blob/master/nagios-ack.pl
  190. ##########################################################################
  191. # CODE ###################################################################
  192. ##########################################################################
  193. ##########################################################################
  194. # Part B : user Actions ##################################################
  195. sub parse_status() {
  196. return $1 if $last_alert =~ /^PROBLEM - (\S+) is DOWN/;
  197. return ($1, $2) if $last_alert =~ /^PROBLEM - (\S+) on (\S+) is/;
  198. return;
  199. }
  200. sub nagios_ack($$$) {
  201. ( $param, $server, $window ) = @_;
  202. $msg='';
  203. @issue = parse_status();
  204. if (!@issue) {
  205. #$window->print("Failed to parse last status: $last_alert");
  206. }
  207. # $host / sticky / notif / persistant / user
  208. ($id,$message)=split " ",$param;
  209. if (($id =~ /^#(\d+)/) && ($id <= scalar @ACKS)) {
  210. $id=$1;
  211. $i=time;
  212. if ($ACKS[$id]->[5] == 1) {
  213. # Already ACKed, print an error.
  214. $msg = " ACK : impossible, c'est dejà fait" ;
  215. display_alert($server,$nagios_ack_channel,$id,' '
  216. # format_alert_id(' ', $id, $ACKS[$id]->[2], $ACKS[$id]->[5])
  217. );
  218. } elsif ($ACKS[$id]->[1] ne "") {
  219. $msg = " ACK ".$id." \"ACKNOWLEDGE_SVC_PROBLEM;$ACKS[$id]->[0];$ACKS[$id]->[1];2;0;1;nagiosadmin;$message\" $i > $nagioscmd";
  220. Irssi::active_server->command('MSG ' . $DEBUG . " ".
  221. `$PRINTF "[\%lu] ACKNOWLEDGE_SVC_PROBLEM;$ACKS[$id]->[0];$ACKS[$id]->[1];2;0;1;nagiosadmin;$message" $i> $nagioscmd`
  222. );
  223. } else {
  224. $msg = " ACK ".$id." \"ACKNOWLEDGE_HOST_PROBLEM;$ACKS[$id]->[0];2;0;1;nagiosadmin;$message\" $i > $nagioscmd";
  225. Irssi::active_server->command('MSG ' . $DEBUG . " ".
  226. `$PRINTF "[\%lu] ACKNOWLEDGE_HOST_PROBLEM;$ACKS[$id]->[0];2;0;1;nagiosadmin;$message" $i > $nagioscmd`
  227. );
  228. }
  229. $ACKS[$id]->[5] = 1;
  230. Irssi::active_server->command('MSG ' . $nagios_ack_channel . $msg);
  231. } else {
  232. $server->command ( "msg ".$nagios_ack_channel. " Usage : !nagios ACK #<alert_num> <ack message to be sent to nagios>. Please validate first, with a !nagios list #<alert_num>");
  233. display_alert($server,$nagios_ack_channel,$id,' '
  234. # format_alert_id(' ', $id, $ACKS[$id]->[2], $ACKS[$id]->[5])
  235. ) if $id =~ /^(\d+)/ ;
  236. }
  237. }
  238. sub nagios_check($$$) {
  239. #1412375470;filer2;DISK_all;2;W=10% C=5%
  240. ( $param, $server, $window ) = @_;
  241. @issue = parse_status();
  242. if (!@issue) {
  243. #$window->print("Failed to parse last status: $last_alert");
  244. }
  245. $message = " CHECK ".$param . join ' ', reverse @issue;
  246. Irssi::active_server->command('MSG ' . $DEBUG . $message);
  247. # handle_alert($$$$$$$) { # $d,$type,$host,$state,$output,$service }
  248. }
  249. sub nagios_inject {
  250. $last_alert = shift;
  251. }
  252. #sub nagios_status {
  253. # (undef, undef, $window) = @_;
  254. # $issue = join ',', map { "'" . $_ . "'" } reverse parse_status();
  255. # $window->print("Last issue: '$last_alert' ($issue)");
  256. #
  257. #}
  258. # Return alert ID
  259. sub format_alert_id($$$$) {
  260. my ($Flag,$I,$state,$acked)=@_;
  261. my $M=sprintf('%02d', scalar @ACKS % 100);
  262. my $N=sprintf('%02d', $I % 100);
  263. if (($I == 0) && (scalar @ACKS == 0)) {
  264. return "[".($acked==0?'!':' ').$Flag." ]/$M ";
  265. } else {
  266. return "[".($acked==0?'!':' ').$Flag.$state_to_color->{$state}.$N.$C{Z}."]/$M ";
  267. }
  268. }
  269. # is_alert : is this alert in the DB ?
  270. sub is_alert($$$$$$) {
  271. # returns ID of alert ; -1 if not.
  272. #Irssi::print(">> On vire $host/$svc");
  273. my $i;
  274. my $acked;
  275. ( $host, $svc, $state, $msg, $d, $acked ) = @_;
  276. # ALERT
  277. while ($i <= $#ACKS) {
  278. # Update, or insert ?
  279. if (($ACKS[$i]->[0] eq $host) && ($ACKS[$i]->[1] eq $svc)) {
  280. Irssi::print(">> found $i");
  281. return $i;
  282. }
  283. $i++;
  284. }
  285. return -1;
  286. }
  287. ##########################################################################
  288. # Part C : Logfile management ############################################
  289. # disable fifo and erase fifo file
  290. sub destroy_fifo($) { # [2004-08-14]
  291. my ($fifo) = @_; # get args
  292. if (defined $FIFO_TAG) { # if fifo signal is active
  293. Irssi::input_remove($FIFO_TAG); # disable fifo signal
  294. undef $FIFO_TAG; # and forget its tag
  295. } #
  296. if (defined $FIFO_HANDLE) { # if fifo is open
  297. close $FIFO_HANDLE; # close it
  298. undef $FIFO_HANDLE; # and forget handle
  299. } #
  300. if (-p $fifo) { # if named fifo exists
  301. unlink $fifo; # erase fifo file
  302. undef $FIFO; # and forget filename
  303. } #
  304. return 1; # return
  305. } #
  306. # Open logfile/fifo
  307. sub open_fifo($) { # [2004-08-14]
  308. my ($fifo) = @_; # get args
  309. if (not sysopen $FIFO_HANDLE, $fifo, # open fifo for non-blocking
  310. O_NONBLOCK | O_RDONLY) { # reading
  311. print CLIENTERROR "could not open nagios logfile for reading";
  312. return ""; #
  313. } #
  314. Irssi::input_remove($FIFO_TAG) # disable fifo reading signal
  315. if defined $FIFO_TAG; # if there is one
  316. $FIFO_TAG = Irssi::input_add # set up signal called when
  317. fileno($FIFO_HANDLE), INPUT_READ, # there's input in the pipe
  318. \&read_fifo, ''; #
  319. return 1; #
  320. }
  321. # read from fifo
  322. # (called by fifo input signal)
  323. sub read_fifo() { # [2004-08-14]
  324. # Read logfile ###########################################################
  325. foreach (<$FIFO_HANDLE>) { # for each input line
  326. chomp; # strip trailing newline
  327. parse_nagios_log($_); #if (/ALERT/ && /HARD;/);
  328. #Irssi::active_win->print( # show incoming commands (debug)
  329. # "\u$IRSSI{name} received command: \"$_\"", #
  330. # MSGLEVEL_CLIENTNOTICE); #
  331. # Irssi::active_win->command($_); # run incoming commands
  332. } #
  333. open_fifo($FIFO); # re-open fifo
  334. # TODO: Is the above re-opening of fifo really necessary? -- If not
  335. # invoked here `read_fifo' is called repeatedly, even though no input
  336. # is to be found on the fifo. (This seems a waste of resources to me.)
  337. }
  338. # create named fifo and open it for input
  339. # (called on script load and fifo name changes)
  340. sub create_fifo($) { # [2004-08-14]
  341. my ($new_fifo) = @_; # get args
  342. if (not -p $new_fifo) { # create fifo if non-existant
  343. if (system "mkfifo '$new_fifo' &>/dev/null" and
  344. system "chmod 777 '$new_fifo' &>/dev/null" and
  345. system "mknod '$new_fifo' &>/dev/null"){
  346. print CLIENTERROR "`mkfifo' failed -- could not create named pipe";
  347. # TODO: capture `mkfifo's stderr and show that here
  348. return ""; #
  349. } #
  350. } #
  351. $FIFO = $new_fifo; # remember fifo name
  352. open_fifo($new_fifo); # open fifo for reading
  353. } #
  354. # Query nagios ###########################################################
  355. sub nagios_query_status($){
  356. # 1412478149;hyppocampe;apt;2;CRITICAL : unknown: qemu-utils, qemu-kvm, qemu-keymaps
  357. my ($option)=@_;
  358. my @unixcat;
  359. my $unixline;
  360. my $acked;
  361. $nagioslive = Irssi::settings_get_str("nagios_live");
  362. foreach $acked (0,1) {
  363. foreach $unixline ( `echo "GET services
  364. Columns: last_state_change host_name display_name state plugin_output
  365. Filter: state > 0
  366. Filter: acknowledged = $acked
  367. And: 2" | unixcat $nagioslive` ) {
  368. chomp $unixline;
  369. ($d,$host,$service,$state,$output)=split /;/,$unixline;
  370. handle_alert($option,$d,'SERVICE',$host,$NagStates[$state+10],$output,$service,$acked);
  371. Irssi::print("%B>>%n $unixline", MSGLEVEL_CLIENTCRAP);
  372. }
  373. }
  374. return;
  375. foreach $acked (0,1) {
  376. foreach $unixline ( `echo "GET hosts
  377. Columns: last_state_change host_name state plugin_output
  378. Filter: state > 0
  379. Filter: acknowledged = 0
  380. And: 2" | unixcat $nagioslive` ) {
  381. chomp $unixline;
  382. ($d,$host,$state,$output)=split /;/,$unixline;
  383. handle_alert($option,$d,'HOST',$host,$NagStates[$state],$output,"",$acked);
  384. Irssi::print("%B>>%n $unixline", MSGLEVEL_CLIENTCRAP);
  385. }
  386. }
  387. }
  388. ##########################################################################
  389. # Part D : Alert processing ##############################################
  390. sub display_alert($$$$){
  391. my ($server,$chan,$alert_id,$prefix) = @_;
  392. #Irssi::print("... $alert_id");
  393. return unless defined $ACKS[$alert_id];
  394. # @ACKS; # [ hostname, service, state, plugin_output, last_state_change, ACKnowledged ] );
  395. # my ($server,$chan,$date,$prefix,$state,$acked,$hostname,$service,$output) = @_;
  396. $prefix=format_alert_id($prefix,$alert_id,$ACKS[$alert_id]->[2],$ACKS[$alert_id]->[5]) ;
  397. #format_alert_id(' ', $i, $K->[2], $K->[5]),
  398. $server->command ( 'msg ' . $chan .
  399. " NAGIOS ". time2date ($ACKS[$alert_id]->[4])." ".
  400. $prefix. " ". $state_to_color->{$ACKS[$alert_id]->[2]}.$ACKS[$alert_id]->[0]."/".$ACKS[$alert_id]->[1].$C{Z}.
  401. " / $ACKS[$alert_id]->[2]".($ACKS[$alert_id]->[5]?" Acked":"")." : $ACKS[$alert_id]->[3]"
  402. );
  403. }
  404. # Search for services/host informations, and post them to IRC ############
  405. # Delimiter : ; for logs; @ for direct nagios custom notification command
  406. sub parse_nagios_log($){
  407. my $option="";
  408. $d=0; $status_line=""; $host="";$output="";$service="";
  409. $status_line=shift;
  410. ### log :
  411. # [1412330770] SERVICE ALERT: ella;IMAPs_LOGIN;OK;SOFT;2;OK - CO1N OK LOGIN Ok.
  412. # /\[\d+\] (\w+) ALERT: (\w+);(\w+);(\w+);HARD;(\d+);(.+)/
  413. # [1410969598] HOST ALERT: filou;DOWN;SOFT;1;PING CRITICAL - Paquets perdus = 100%
  414. # [TIMESTAMP] PROCESS type ALERT: host;service;STATE1;HARD;num;commentaire
  415. if (@match=$status_line =~ /\[?(\d+)\]? HOST ALERT: ([^@;]+)[@;]([^;@]+)[@;]HARD[@;][^;@]*[@;](.+)/) {
  416. # HOST ########################
  417. ($d,$host,$state,$output)=@match;
  418. handle_alert($option,$d,"HOST",$host,$state,$output,$service,0);
  419. # SERVICE #####################
  420. } elsif (@match=$status_line =~ /\[?(\d+)\]? (\w+) ALERT: ([^;@]+)[@;]([^;@]+)[@;]([^;@]+)[@;]HARD[@;][^;@]*[@;](.+)/) {
  421. ($d,$type,$host,$service,$state,$output)=@match;
  422. $service=~s/[^\w\d_-]/_/g;
  423. handle_alert($option,$d,$type,$host,$state,$output,$service,0);
  424. # OTHER #######################
  425. } elsif (@match=$status_line =~ /\[\d+\] (\w+) ALERT: (.*)/) {
  426. ($type,$data)=@match;
  427. Irssi::print( "%B>>%n $IRSSI{name} $type - $data", MSGLEVEL_CLIENTCRAP) unless ($status_line =~ /[;@]SOFT[@;]/);
  428. # FALLBACK ####################
  429. } else {
  430. Irssi::print( #
  431. "%B>>%n $IRSSI{name} received message: \"$_\"",
  432. MSGLEVEL_CLIENTCRAP); #
  433. next
  434. }
  435. }
  436. # Use alert fields to print and insert it
  437. sub handle_alert($$$$$$$$) { # $option,$d,$type,$host,$state,$output,$service,acked
  438. # Temporisation
  439. my ($option,$acked);
  440. ($option,$d,$type,$host,$state,$output,$service,$acked)=@_;
  441. next if exists $renot{"$host:$service"} && $renot{"$host:$service"} >= time() - 5;
  442. $renot{"$host:$service"} = time();
  443. my $id=is_alert ( $host, $service, $state, $output, $d, $acked ) ;
  444. #( $host, $service, $state, $output, $d, $acked ) = @_;
  445. my $acked;
  446. # Silently ignore previously sent/acked alerts
  447. Irssi::print(">> $stat,$d,$id,$state,$acked,$host,$service,$output");
  448. $nagios_ack_channel= Irssi::settings_get_str("nagios_ack_channel");
  449. # ALERT
  450. if ($id == -1) {
  451. $id=scalar @ACKS;
  452. if ( $state eq 'WARNING' || $state eq 'CRITICAL' || $state eq 'UNKNOWN' || $state eq 'DOWN' ) {
  453. push (@ACKS, [ $host, $service, $state, $output, $d, $acked ] );
  454. # display
  455. display_alert(Irssi::active_server,$nagios_ack_channel,$id,'+'),
  456. unless ($option eq "silent");
  457. return ($#ACKS,'+');
  458. } else {
  459. push (@ACKS, [ $host, $service, $state, $output, $d, $acked ] );
  460. # display
  461. display_alert(Irssi::active_server,$nagios_ack_channel,$id,'?'),
  462. unless ($option eq "silent");
  463. pop @ACKS;
  464. return ($#ACKS,'?');
  465. }
  466. } else {
  467. if ( $state eq 'WARNING' || $state eq 'CRITICAL' || $state eq 'UNKNOWN' || $state eq 'DOWN' ) {
  468. $ACKS[$id]->[4]=$d;
  469. if ($ACKS[$id]->[2] ne $state) {
  470. # Same alert, but different level
  471. $ACKS[$id]->[2]=$state;
  472. # display
  473. display_alert(Irssi::active_server,$nagios_ack_channel,$id,'c'),
  474. unless ($option eq "silent");
  475. return ($id+1,'c');
  476. } else {
  477. # Same alert
  478. return ($id+1, '!');
  479. # No need to display...
  480. }
  481. # Clear alert
  482. } else {
  483. # display
  484. display_alert(Irssi::active_server,$nagios_ack_channel,$id,'-'),
  485. unless ($option eq "silent");
  486. # Put the last- alert instead of existing one.
  487. $ACKS[$id] = pop @ACKS;
  488. return ($i,'-');
  489. }
  490. }
  491. }
  492. ##########################################################################
  493. # IRSSI Events ###########################################################
  494. # create new fifo (erase any old) and get command prefix
  495. # (called on script loading and on user /set)
  496. sub setup() { # [2004-08-13]
  497. my $new_fifo = Irssi::settings_get_str # setting from Irssi
  498. 'fifo_remote_file'; # (and add path to it)
  499. return if $new_fifo eq $FIFO and -p $FIFO; # do nada if already exists
  500. destroy_fifo($FIFO) if -p $FIFO; # destroy old fifo
  501. create_fifo($new_fifo) # create new fifo
  502. and $FIFO = $new_fifo; # and remember that fifo
  503. # To ADD :
  504. # request to livestatus to fetch stored alerts
  505. }
  506. # Interact with IRC chan users
  507. sub event_privmsg($$$$) {
  508. # Commamd channel
  509. my $K;
  510. my ($server, $data, $nick, $mask) =@_;
  511. my ($target, $text, $arg) = $data =~ /^(\S*)\s:(.*)/;
  512. #print ( "C:$target X:$text A:$admin D:$warndate L:$last W:$warn N:$nick D:$data" );
  513. if ( $text =~ /^!nagios ?(.*)/i ) {
  514. $arg=$1;
  515. # If we are not in command channel : NoOP
  516. $nagios_ack_channel=Irssi::settings_get_str("nagios_ack_channel");
  517. Irssi::print(">> $arg - $nagios_ack_channel - $target");
  518. return if $target ne $nagios_ack_channel ;
  519. if ($arg =~ /^refresh ?(.*)/i) {
  520. $arg=$1;
  521. Irssi::print(">> $arg");
  522. if ($arg =~ /\bclear\b/) {
  523. $server->command ( "msg ".$nagios_ack_channel." Local alerts cleared" );
  524. @ACKS=();
  525. }
  526. if ($arg =~ /\bsilent\b/) {
  527. nagios_query_status("silent");
  528. } else {
  529. nagios_query_status("");
  530. $server->command ( "msg ".$nagios_ack_channel.
  531. ' Refresh nagios. Use "silent" keyword to disable display.');
  532. }
  533. $server->command ( "msg ".$nagios_ack_channel.
  534. " ".scalar @ACKS." alertes");
  535. }
  536. elsif ($arg =~ /^list ?(.*)/i) {
  537. my $i=0;
  538. # Do you search on pattern (whole database), or a list (reduced database) ?
  539. my $grepto;
  540. my $motif=$1;
  541. my $search_unack=0;
  542. $server->command ( "msg ".$nagios_ack_channel.
  543. " ".scalar @ACKS." alertes");
  544. $motif="" unless defined($motif) ;
  545. $motif="0\$" if $motif =~ /^ack$/i;
  546. $motif="1\$" if $motif =~ /^unack$/i;
  547. $search_unack="1" if $motif eq "";
  548. Irssi::print(">> List : $motif");
  549. foreach $K (@ACKS) {
  550. my $grepto="#$i @$K";
  551. # just display alert fields, with colors.
  552. # See ACKS fields organization
  553. # Field 5 is "Alert has been previously acknowledged"
  554. # display_alert($server,$chan,$date,$prefix,$state,$acked,$hostname,$service,$output)
  555. display_alert($server,$nagios_ack_channel,$i,' '
  556. #format_alert_id(' ', $i, $K->[2], $K->[5]),
  557. #$K->[2],$K->[5],$K->[0],$K->[1],$K->[3]
  558. ) if ( ($grepto =~ /$motif/i) || ($search_unack && ($K->[5]==0)) );
  559. # Display only if : $motif is found, or $motif is empty, and alert is unack
  560. $i++;
  561. }
  562. }
  563. elsif ($arg =~ /^help/i) {
  564. $server->command ( "msg ".$nagios_ack_channel.
  565. " !nagios list [pattern] [ack/unack] : liste des alertes nagios reçues ici");
  566. $server->command ( "msg ".$nagios_ack_channel.
  567. " !nagios help : l'aide");
  568. $server->command ( "msg ".$nagios_ack_channel.
  569. " !nagios ack <#ALERTE> <message> TODO : aquitte l'alerte");
  570. $server->command ( "msg ".$nagios_ack_channel.
  571. " !nagios check <#ALERTE> TODO : recheck une alerte donnée (service/host)");
  572. $server->command ( "msg ".$nagios_ack_channel.
  573. " !nagios refresh [silent] [clear] : interroge le nagios pour avoir la liste de toutes les alertes");
  574. $server->command ( "msg ".$nagios_ack_channel.
  575. " si le mot clef 'clear' est ajouté, il purge les alertes locales");
  576. $server->command ( "msg ".$nagios_ack_channel.
  577. " [!+00]/00 le numéro et le statut d'alerte sont résumés dans le premier symbole :");
  578. $server->command ( "msg ".$nagios_ack_channel.
  579. " [ ACK STATUT NUM ] / TOTAL un '!' signifie 'nouvelle alerte, unack'. le \"statut\" peut être de la forme :");
  580. $server->command ( "msg ".$nagios_ack_channel.
  581. " ! : alerte identique. C : niveau d'alerte changé. - : alerte terminée. ? : 'feu vert' inconnu. + : nouvelle alerte");
  582. $server->command ( "msg ".$nagios_ack_channel.
  583. " ");
  584. } elsif ( $arg =~ /^check ?(.*)/i ){
  585. nagios_check($1,$server,undef);
  586. } elsif ( $arg =~ /^ack ?(.*)/i ){
  587. nagios_ack($1,$server,undef);
  588. $server->command ( "msg ".$nagios_ack_channel.
  589. " ".scalar @ACKS." alertes");
  590. } else {
  591. $server->command ( "msg ".$nagios_ack_channel.
  592. " ".scalar @ACKS." alertes");
  593. }
  594. }
  595. return 1;
  596. }
  597. ##########################################################################
  598. # Main ###################################################################
  599. ##########################################################################
  600. print "starting...\n";
  601. # clean up fifo on unload
  602. # (called on /script unload)
  603. Irssi::signal_add_first #
  604. 'command script unload', sub { # [2004-08-13]
  605. my ($script) = @_; # get args
  606. return unless $script =~ # only do cleanup when
  607. /(?:^|\s) $IRSSI{name} # unloading *this* script
  608. (?:\.[^. ]*)? (?:\s|$) /x; #
  609. destroy_fifo($FIFO) if -p $FIFO; # destroy old fifo
  610. Irssi::print("%B>>%n $IRSSI{name} $VERSION unloaded", MSGLEVEL_CLIENTCRAP);
  611. }; #
  612. setup(); # initialize setup values
  613. Irssi::signal_add('event privmsg', 'event_privmsg');
  614. Irssi::signal_add("message public", "event_privmsg");
  615. Irssi::signal_add('setup changed', \&setup); # re-read setup when it changes
  616. print CLIENTCRAP "%B>>%n $IRSSI{name} $VERSION (by $IRSSI{authors}) loaded";
  617. 1;