nagios-fifo.pl 27 KB

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