From 0207651a64987a14b0c2456a7ce83b51974a63f3 Mon Sep 17 00:00:00 2001 From: Ramon Novoa Date: Tue, 14 Apr 2009 19:13:16 +0000 Subject: [PATCH] 2009-04-14 Ramon Novoa * pandora_ctl, pandora_network, pandora_wmi, pandora_plugin, pandora_prediction, bin/pandora_wmi, bin/pandora_plugin, bin/pandora_prediction, bin/pandora_snmpconsole, bin/pandora_recon, bin/pandora_network, pandora_recon, pandora_snmpconsole: Deleted from repository. Old server code and startup scripts. * Makefile.PL, pandora_server_installer, lib/PandoraFMS/Config.pm lib/PandoraFMS/DB.pm, lib/PandoraFMS/Tools.pm, bin/pandora_server, pandora_package_installer, pandora_server: Updated to work with the new code, removed unneeded dependencies, fixed some bugs etc. * lib/PandoraFMS/SNMPServer.pm, lib/PandoraFMS/PluginServer.pm, lib/PandoraFMS/ProducerConsumerServer.pm, lib/PandoraFMS/Server.pm, lib/PandoraFMS/PredictionServer.pm, lib/PandoraFMS/Core.pm, lib/PandoraFMS/ReconServer.pm, lib/PandoraFMS/DataServer.pm, lib/PandoraFMS/NetworkServer.pm, lib/PandoraFMS/WMIServer.pm: Added to repository. New server code. git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@1620 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f --- pandora_server/ChangeLog | 20 + pandora_server/Makefile.PL | 2 +- pandora_server/bin/pandora_network | 696 ----- pandora_server/bin/pandora_plugin | 402 --- pandora_server/bin/pandora_prediction | 431 --- pandora_server/bin/pandora_recon | 711 ----- pandora_server/bin/pandora_server | 515 +--- pandora_server/bin/pandora_snmpconsole | 198 -- pandora_server/bin/pandora_wmi | 405 --- pandora_server/lib/PandoraFMS/Config.pm | 172 +- pandora_server/lib/PandoraFMS/Core.pm | 1107 +++++++ pandora_server/lib/PandoraFMS/DB.pm | 2640 +---------------- pandora_server/lib/PandoraFMS/DataServer.pm | 246 ++ .../lib/PandoraFMS/NetworkServer.pm | 476 +++ pandora_server/lib/PandoraFMS/PluginServer.pm | 182 ++ .../lib/PandoraFMS/PredictionServer.pm | 242 ++ .../lib/PandoraFMS/ProducerConsumerServer.pm | 166 ++ pandora_server/lib/PandoraFMS/ReconServer.pm | 382 +++ pandora_server/lib/PandoraFMS/SNMPServer.pm | 178 ++ pandora_server/lib/PandoraFMS/Server.pm | 230 ++ pandora_server/lib/PandoraFMS/Tools.pm | 40 +- pandora_server/lib/PandoraFMS/WMIServer.pm | 195 ++ pandora_server/pandora_ctl | 44 - pandora_server/pandora_network | 74 - pandora_server/pandora_package_installer | 25 - pandora_server/pandora_plugin | 72 - pandora_server/pandora_prediction | 74 - pandora_server/pandora_recon | 72 - pandora_server/pandora_server | 16 +- pandora_server/pandora_server_installer | 43 - pandora_server/pandora_snmpconsole | 152 - pandora_server/pandora_wmi | 72 - pandora_server/util/pandora_dbstress.pl | 86 +- 33 files changed, 3745 insertions(+), 6621 deletions(-) delete mode 100755 pandora_server/bin/pandora_network delete mode 100755 pandora_server/bin/pandora_plugin delete mode 100755 pandora_server/bin/pandora_prediction delete mode 100755 pandora_server/bin/pandora_recon delete mode 100755 pandora_server/bin/pandora_snmpconsole delete mode 100755 pandora_server/bin/pandora_wmi create mode 100644 pandora_server/lib/PandoraFMS/Core.pm create mode 100644 pandora_server/lib/PandoraFMS/DataServer.pm create mode 100644 pandora_server/lib/PandoraFMS/NetworkServer.pm create mode 100644 pandora_server/lib/PandoraFMS/PluginServer.pm create mode 100644 pandora_server/lib/PandoraFMS/PredictionServer.pm create mode 100644 pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm create mode 100644 pandora_server/lib/PandoraFMS/ReconServer.pm create mode 100644 pandora_server/lib/PandoraFMS/SNMPServer.pm create mode 100644 pandora_server/lib/PandoraFMS/Server.pm create mode 100644 pandora_server/lib/PandoraFMS/WMIServer.pm delete mode 100755 pandora_server/pandora_ctl delete mode 100755 pandora_server/pandora_network delete mode 100755 pandora_server/pandora_plugin delete mode 100755 pandora_server/pandora_prediction delete mode 100755 pandora_server/pandora_recon delete mode 100755 pandora_server/pandora_snmpconsole delete mode 100755 pandora_server/pandora_wmi diff --git a/pandora_server/ChangeLog b/pandora_server/ChangeLog index d35d5b75fa..e615282343 100644 --- a/pandora_server/ChangeLog +++ b/pandora_server/ChangeLog @@ -1,3 +1,23 @@ +2009-04-14 Ramon Novoa + + * pandora_ctl, pandora_network, pandora_wmi, pandora_plugin, + pandora_prediction, bin/pandora_wmi, bin/pandora_plugin, + bin/pandora_prediction, bin/pandora_snmpconsole, bin/pandora_recon, + bin/pandora_network, pandora_recon, pandora_snmpconsole: Deleted + from repository. Old server code and startup scripts. + + * Makefile.PL, pandora_server_installer, lib/PandoraFMS/Config.pm + lib/PandoraFMS/DB.pm, lib/PandoraFMS/Tools.pm, bin/pandora_server, + pandora_package_installer, pandora_server: Updated to work with the + new code, removed unneeded dependencies, fixed some bugs etc. + + * lib/PandoraFMS/SNMPServer.pm, lib/PandoraFMS/PluginServer.pm, + lib/PandoraFMS/ProducerConsumerServer.pm, lib/PandoraFMS/Server.pm, + lib/PandoraFMS/PredictionServer.pm, lib/PandoraFMS/Core.pm, + lib/PandoraFMS/ReconServer.pm, lib/PandoraFMS/DataServer.pm, + lib/PandoraFMS/NetworkServer.pm, lib/PandoraFMS/WMIServer.pm: Added + to repository. New server code. + 2009-04-02 Sancho Lerena * DB.pm: Function to process SNMP Alerts was not properly exported, so diff --git a/pandora_server/Makefile.PL b/pandora_server/Makefile.PL index 4e31c87c40..b9cc906c9b 100644 --- a/pandora_server/Makefile.PL +++ b/pandora_server/Makefile.PL @@ -22,7 +22,7 @@ WriteMakefile( HTML::Entities => 0, SNMP => 0 }, - EXE_FILES => [ 'bin/pandora_server', 'bin/pandora_network', 'bin/pandora_wmi', 'bin/pandora_recon', 'bin/pandora_snmpconsole' , 'bin/pandora_plugin', 'bin/pandora_prediction', 'util/pandora_exec'], + EXE_FILES => [ 'bin/pandora_server', 'util/pandora_exec'], PMLIBDIRS => [ 'lib' ], 'dist' => { 'TAR' => 'tar', 'TARFLAGS' => 'cvfz', 'SUFFIX' => '.gz', 'COMPRESS' => 'gzip'} diff --git a/pandora_server/bin/pandora_network b/pandora_server/bin/pandora_network deleted file mode 100755 index 3acd35cd78..0000000000 --- a/pandora_server/bin/pandora_network +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/perl - -########################################################################## -# Pandora FMS Network Server -# http://www.pandorafms.com -########################################################################## -# Copyright (c) 2006-2008 Sancho Lerena, slerena@gmail.com -# (c) 2006-2008 Artica Soluciones Tecnologicas S.L -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use Time::Local; # DateTime basic manipulation -use Net::Ping; # For ICMP latency -use Time::HiRes; # For high precission timedate functions (Net::Ping) -use IO::Socket; # For TCP/UDP access -use threads; -use threads::shared; - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; -enterprise_load (); - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; -my @pending_task : shared; -my %pending_task_hash : shared; -my %current_task_hash : shared; -my $icmp_lock : shared; -my $queue_lock : shared; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config, "Pandora FMS Network Server"); - -# Read config file for Global variables -pandora_loadconfig (\%pa_config,1); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS Network Daemon starting", "SYSTEM", "System"); - -# Check for snmpget - -if (! -e $pa_config{"snmpget"}) { - print " [E] ".$pa_config{"snmpget"}." not found. Pandora FMS Network cannot run without it.\n\n"; - exit; -} - -# Thread startup - -if ($pa_config{"verbosity"} > 4){ - print " [*] Starting up network threads\n"; -} - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"verbosity"} > 0){ - print " [*] Backgrounding Pandora FMS Network Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - - -# Launch now all network threads -# $ax is local thread id for this server -for (my $ax=0; $ax < $pa_config{'network_threads'}; $ax++){ - threads->new( \&pandora_network_consumer, \%pa_config, $ax); -} - -# Launch now the network producer thread -threads->new( \&pandora_network_producer, \%pa_config); - - -if ($pa_config{"verbosity"} > 4){ - print " [*] All threads loaded and running \n\n"; -} - -# Start Pandora FMS loggin -pandora_startlog (\%pa_config); - -# Last thread is the main process (this process) -my $dbhost = $pa_config{'dbhost'}; -my $dbname = $pa_config{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", - $pa_config{'dbuser'}, - $pa_config{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); - -my $counter_status_report = 0; -my $counter_status_loops = (30 / $pa_config{"server_threshold"}) + 1; - -while (1) { - pandora_serverkeepaliver (\%pa_config, 1, $dbh); - - # Only call this each 30 loops - if ($counter_status_report > $counter_status_loops ){ - enterprise_hook('mcast_status_report', [\%pa_config, $dbh]); - $counter_status_report = 0; - } else { - $counter_status_report++; - } - - threads->yield; - sleep ($pa_config{"server_threshold"}); -} - -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#--------------------- Main Perl Code below this line----------------------- -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ - - -########################################################################## -# SUB pandora_network_subsystem -# Subsystem to process network modules -# This module runs each X seconds (server threshold) checking for network modules status -########################################################################## -sub pandora_network_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - - if ($pa_config->{"verbosity"} > 4){ - print " [*] Starting up Network Consumer Thread # $thread_id \n"; - } - - my $data_id_agent_module; - # Create Database handler - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $counter =0; - - LOOP: while (1) { - if ($counter > 10) { - threads->yield; - sleep (1); - $counter = 0; - } - - # Take the first element on the shared queue - # Insert this element on the current task hash - { - lock $queue_lock; - if (scalar(@pending_task) == 0){ - $counter++; - next LOOP; - } - - $data_id_agent_module = shift(@pending_task); - delete($pending_task_hash{$data_id_agent_module}); - $current_task_hash{$data_id_agent_module}=1; - } - - # Executing network task with unmanaged error trapping - eval { - # Call network execution process - exec_network_module ( $pa_config, $data_id_agent_module, $dbh); - }; - if ($@){ - logger ($pa_config, "[ERROR] Network Task for module $data_id_agent_module causes a system exception", 0); - logger ($pa_config, "ERROR Code: $@", 1); - } - - # Remove from queue. If catch an error, probably data is - # not been processed, but has been freed from task queue - { - lock $queue_lock; - delete($current_task_hash{$data_id_agent_module}); - } - $counter = 0; - } -} - -sub pandora_network_producer ($) { - my $pa_config = $_[0]; - - if ($pa_config->{"verbosity"} > 4){ - print " [*] Starting up Network Producer Thread ...\n"; - } - - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - - my $server_id = $pa_config->{'server_id'}; - - # Initialize variables for posterior usage - my $query1; - my @sql_data1; - my $data_id_agente_modulo; - my $data_flag; - my $exec_sql1; - - while (1) { - if ($pa_config->{"pandora_master"} != 1) { - # Query for normal server, not MASTER server - $query1 = "SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado - WHERE - id_network_server = $server_id - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.id_tipo_modulo > 4 - AND - tagente_modulo.id_tipo_modulo < 19 - AND - tagente_modulo.disabled = 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND ( - tagente_modulo.flag = 1 - OR - ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) - ) - ORDER BY - tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC "; - } else { - # Query for MASTER SERVER ! - # Apparently the MASTER SERVER takes over a SLAVE SERVER tasks' when it's down. Made a subquery of it so it doesn't do 3-4 joins - $query1 = "SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, tagente_estado.last_execution_try, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado - WHERE - ( - (tagente.id_network_server = $server_id) - OR - (tagente.id_network_server = ANY(SELECT id_server FROM tserver WHERE status = 0 AND id_server != $server_id AND network_server = 1)) - ) - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_modulo.id_tipo_modulo > 4 - AND - tagente_modulo.id_tipo_modulo < 19 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND ( - tagente_modulo.flag = 1 - OR - ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() ) - ) - ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC"; - } - $exec_sql1 = $dbh->prepare($query1); - $exec_sql1 ->execute; - while (@sql_data1 = $exec_sql1->fetchrow_array()) { - $data_id_agente_modulo = $sql_data1[0]; - $data_flag = $sql_data1[1]; - # Skip modules already queued - if ((!defined($pending_task_hash{$data_id_agente_modulo})) && - (!defined($current_task_hash{$data_id_agente_modulo}))) { - if ($data_flag == 1){ - $dbh->do("UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = $data_id_agente_modulo") - } - # Locking scope, do not remove redundant { } - { - lock $queue_lock; - push (@pending_task, $data_id_agente_modulo); - $pending_task_hash {$data_id_agente_modulo}=1; - } - } - } - #logger ($pa_config, "Items in Network Pending Queue: ".scalar(@pending_task), 5); - $exec_sql1->finish(); - sleep($pa_config->{"server_threshold"}); - } # Main loop -} - -############################################################################## -# pandora_ping_icmp (config, destination, timeout) -# Do a ICMP scan, return 1 if alive, 0 if not -############################################################################## -sub pandora_ping_icmp { - my $pa_config = $_[0]; - my $dest = $_[1]; - my $l_timeout = $_[2]; - # temporal vars. - my $result = 0; - my $result2 = 0; - my $temp; - - if ($pa_config->{'icmp_checks'} eq ""){ - $pa_config->{'icmp_checks'} = 1; - } - - # Make more than a single ping (as defined in icmp_checks - for ($temp =0; $temp < $pa_config->{'icmp_checks'}; $temp++){ - my $p; - # Some hosts don't accept ICMP with too small payload. Use 16 Bytes min - { - lock $icmp_lock; - $p = Net::Ping->new("icmp", $l_timeout, 32); - $result = $p->ping($dest); - } - - if (defined($result)){ - $p->close(); - if ($result == 1){ - $result2 = 1; - $temp = $pa_config->{'icmp_checks'}; # Exit for - } - } - undef ($p); - } - return $result2; -} - -############################################################################## -# pandora_ping_latency (destination, timeout, data, result) - Do a ICMP latency check -############################################################################## -sub pandora_ping_latency { - my $dest = $_[0]; - my $l_timeout = $_[1]; - my $module_data = $_[2]; - my $module_result = $_[3]; - my $icmp_return; - my $icmp_reply; - my $icmp_ip; - my $nm; - - # Locking for use ICMP call safety - { - lock $icmp_lock; - $nm = Net::Ping->new("icmp", $l_timeout, 32); - $nm->hires(); - ($icmp_return, $icmp_reply, $icmp_ip) = $nm->ping ($dest,$l_timeout); - } - if ($icmp_return) { - $$module_data = $icmp_reply * 1000; # milliseconds - $$module_result = 0; # Successful - } else { - $$module_result = 1; # Error. - $$module_data = 0; - } - $nm->close(); - undef($nm); -} - -########################################################################## -# SUB pandora_query_tcp (pa_config, tcp_port. ip_target, result, data, tcp_send, -# tcp_rcv, id_tipo_module, dbh) -# Makes a call to TCP modules to get a value. -########################################################################## -sub pandora_query_tcp (%$$$$$$$) { - my $pa_config = $_[0]; - my $tcp_port = $_[1]; - my $ip_target = $_[2]; - my $module_result = $_[3]; - my $module_data = $_[4]; - my $tcp_send = $_[5]; - my $tcp_rcv = $_[6]; - my $id_tipo_modulo = $_[7]; - - my $counter; - for ($counter =0; $counter < $pa_config->{'tcp_checks'}; $counter++){ - my $temp; my $temp2; - my $tam; - my $handle=IO::Socket::INET->new( - Proto=>"tcp", - PeerAddr=>$ip_target, - Timeout=>$pa_config->{'tcp_timeout'}, - PeerPort=>$tcp_port, - Blocking=>0 ); # Non blocking !!, very important ! - - if (defined($handle)){ - # Multi request patch, submitted by Glen Eustace (new zealand) - my @tcp_send = split( /\|/, $tcp_send ); - my @tcp_rcv = split( /\|/, $tcp_rcv ); - -next_pair: - $tcp_send = shift( @tcp_send ); - $tcp_rcv = shift( @tcp_rcv ); - - if ((defined($tcp_send)) && ($tcp_send ne "")){ # its Expected to sending data ? - # Send data - $handle->autoflush(1); - $tcp_send =~ s/\^M/\r\n/g; - # Replace Carriage rerturn and line feed - $handle->send($tcp_send); - } - # we expect to receive data ? (non proc types) - if ((defined($tcp_rcv)) && (($tcp_rcv ne "") || ($id_tipo_modulo == 10) || ($id_tipo_modulo ==8) || ($id_tipo_modulo == 11))) { - # Receive data, non-blocking !!!! (VERY IMPORTANT!) - $temp2 = ""; - for ($tam=0; $tam<($pa_config->{'tcp_timeout'}); $tam++){ - $handle->recv($temp,16000,0x40); - $temp2 = $temp2.$temp; - if ($temp ne ""){ - $tam++; # If doesnt receive data, increase counter - } - sleep(1); - } - if ($id_tipo_modulo == 9){ # only for TCP Proc - if ($temp2 =~ /$tcp_rcv/i){ # String match ! - if ( @tcp_send ) { # still more pairs - goto next_pair; - } - $$module_data = 1; - $$module_result = 0; - $counter = $pa_config->{'tcp_checks'}; - } else { - $$module_data = 0; - $$module_result = 0; - $counter = $pa_config->{'tcp_checks'}; - } - } elsif ($id_tipo_modulo == 10 ){ # TCP String (no int conversion)! - $$module_data = $temp2; - $$module_result =0; - } else { # TCP Data numeric (inc or data) - if ($temp2 ne ""){ - if ($temp2 =~ /[A-Za-z\.\,\-\/\\\(\)\[\]]/){ - $$module_result = 1; - $$module_data = 0; # invalid data - $counter = $pa_config->{'tcp_checks'}; - } else { - $$module_data = int($temp2); - $$module_result = 0; # Successful - $counter = $pa_config->{'tcp_checks'}; - } - } else { - $$module_result = 1; - $$module_data = 0; # invalid data - $counter = $pa_config->{'tcp_checks'}; - } - } - } else { # No expected data to receive, if connected and tcp_proc type successful - if ($id_tipo_modulo == 9){ # TCP Proc - $$module_result = 0; - $$module_data = 1; - $counter = $pa_config->{'tcp_checks'}; - } - } - $handle->close(); - undef ($handle); - } else { # Cannot connect (open sock failed) - $$module_result = 1; # Fail - if ($id_tipo_modulo == 9){ # TCP Proc - $$module_result = 0; - $$module_data = 0; # Failed, but data exists - $counter = $pa_config->{'tcp_checks'}; - } - } - } -} - -########################################################################## -# SUB pandora_query_snmp (pa_config, oid, community, target, version, error, dbh) -# Makes a call to SNMP modules to get a value, -########################################################################## -sub pandora_query_snmp (%$$$$$) { - my $pa_config = $_[0]; - my $snmp_oid = $_[1]; - my $snmp_community =$_[2]; - my $snmp_target = $_[3]; - my $snmp_version = $_[4]; - # $_[5] contains error var. - - if ($snmp_version ne '1' && $snmp_version ne '2' - && $snmp_version ne '2c' && $snmp_version ne '3') { - $snmp_version = '1'; - } - - my $snmp_timeout = $pa_config->{"snmp_timeout"}; - my $snmp_retries = $pa_config->{'snmp_checks'}; - - # TODO: Alternative version if command is not available or user select a new switch - # with "use internal SNMP" option. At this moment, due to locks() external snmp is much faster - - $_[5] = 0; - my $snmpget_cmd = $pa_config->{"snmpget"}; - my $output = `$snmpget_cmd -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -c $snmp_community $snmp_target $snmp_oid`; - if ($output eq ""){ - $_[5] = 1; - } - return $output; -} - -########################################################################## -# SUB exec_network_module (paconfig, id_agente_modulo, dbh ) -# Execute network module task -########################################################################## -sub exec_network_module { - my $pa_config = $_[0]; - my $id_agente_modulo = $_[1]; - my $dbh = $_[2]; - # Init variables - my $id_agente; - my $id_tipo_modulo; - my $nombre; - my $min; - my $max; - my $module_interval; - my $tcp_port; - my $tcp_send; - my $tcp_rcv; - my $snmp_community; - my $snmp_oid; - my $ip_target; - my $id_module_group; - my $flag; - my @sql_data; - if ((!defined($id_agente_modulo)) || ($id_agente_modulo eq "")){ - return 0; - } - my $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_agente_modulo"; - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - if (@sql_data = $exec_sql->fetchrow_array()){ - $id_agente= $sql_data[1]; - $id_tipo_modulo = $sql_data[2]; - $nombre = $sql_data[4]; - $min = $sql_data[6]; - $max = $sql_data[5]; - $module_interval = $sql_data[7]; - $tcp_port = $sql_data[8]; - $tcp_send = $sql_data[9]; - $tcp_rcv = $sql_data[10]; - $snmp_community = $sql_data[11]; - $snmp_oid = $sql_data[12]; - $ip_target = $sql_data[13]; - $id_module_group = $sql_data[14]; - $flag = $sql_data[15]; - $exec_sql->finish(); - } else { - $exec_sql->finish(); - logger (\%pa_config,"[ERROR] Processing data for invalid module", 0); - return 0; - } - - my $agent_name = dame_agente_nombre ($pa_config, $id_agente, $dbh); - my $error = "1"; - my $query_sql2; - my $temp=0; my $tam; my $temp2; - my $module_result = 1; # Fail by default - my $module_data = 0; - - if ((defined($ip_target)) && ($ip_target ne "")) { - - # ICMP Modules - # ------------ - if ($id_tipo_modulo == 6){ # ICMP (Connectivity only: Boolean) - $temp = pandora_ping_icmp ($pa_config, $ip_target, $pa_config->{'networktimeout'}); - if ($temp == 1 ){ - $module_result = 0; # Successful - $module_data = 1; - } else { - $module_result = 0; # If cannot connect, its down. - $module_data = 0; - } - } elsif ($id_tipo_modulo == 7){ # ICMP (data for latency in ms) - # This module only could be executed if executed as root - if ($> == 0){ - pandora_ping_latency ($ip_target, $pa_config->{"networktimeout"}, \$module_data, \$module_result); - } else { - $module_result = 0; # Done but, with zero value - $module_data = 0; # This should don't happen - } - # SNMP Modules (Proc=18, inc, data, string) - # ------------ - } elsif (($id_tipo_modulo == 15) || ($id_tipo_modulo == 18) || ($id_tipo_modulo == 16) || ($id_tipo_modulo == 17)) { # SNMP module - if ((defined($snmp_oid)) && ($snmp_oid ne "") && (defined($snmp_community)) && ($snmp_community ne "")) { # Port check - $temp2 = pandora_query_snmp ($pa_config, $snmp_oid, $snmp_community, $ip_target, $tcp_send, $error); - } else { - $error = 1 - } - if ($error == 0) { # A correct SNMP Query - $module_result = 0; - # SNMP_DATA_PROC - if ($id_tipo_modulo == 18){ #snmp_data_proc - # RFC1213-MIB where it says that: SYNTAX INTEGER { up(1), down(2), testing(3), - # unknown(4), dormant(5), notPresent(6), lowerLayerDown(7) } - if ($temp2 != 1){ # up state is 1, down state in SNMP is 2 .... - $temp2 = 0; - } - $module_data = $temp2; - } - # SNMP_DATA and SNMP_DATA_INC - elsif (($id_tipo_modulo == 15) || ($id_tipo_modulo == 16) ){ - if (!is_numeric($temp2)){ - $module_result = 1; - } else { - $module_data = $temp2; - } - } else { # String SNMP - $module_data = $temp2; - } - } else { # Failed SNMP-GET - $module_data = 0; - if ($id_tipo_modulo == 18){ # snmp_proc - # Feature from 10Feb08. If snmp_proc_deadresponse = 1 and cannot contact by an error - # this is a fail monitor - if ($pa_config->{"snmp_proc_deadresponse"} eq "1"){ - $module_result = 0; - } else { - $module_result = 1; - } - } else { - $module_result = 1; # No data, cannot connect - } - } - # TCP Module - # ---------- - } elsif (($id_tipo_modulo == 8) || ($id_tipo_modulo == 9) || ($id_tipo_modulo == 10) || ($id_tipo_modulo == 11)) { # TCP Module - if ((defined($tcp_port)) && ($tcp_port < 65536) && ($tcp_port > 0)) { # Port check - pandora_query_tcp ($pa_config, $tcp_port, $ip_target, \$module_result, \$module_data, $tcp_send, $tcp_rcv, $id_tipo_modulo); - } else { - # Invalid port, get no check - $module_result = 1; - } - } - } - - # Write data section - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp = &UnixDate("today","%s"); - - # Is everything goes ok - if ($module_result == 0) { - my %part; - $part{'name'}[0]=$nombre; - $part{'description'}[0]=""; - $part{'data'}[0] = $module_data; - $part{'max'}[0] = $max; - $part{'min'}[0] = $min; - my $tipo_modulo = dame_nombretipomodulo_idagentemodulo ($pa_config, $id_tipo_modulo, $dbh); - if (($tipo_modulo eq 'remote_snmp') || ($tipo_modulo eq 'remote_icmp') || ($tipo_modulo eq 'remote_tcp')) { - module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif ($tipo_modulo =~ /\_inc/ ) { - module_generic_data_inc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif ($tipo_modulo =~ /\_string/) { - module_generic_data_string ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif ($tipo_modulo =~ /\_proc/){ - module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - else { - logger ($pa_config, "Problem with unknown module type '$tipo_modulo'", 0); - $module_result = 1; - } - # Update agent last contact - # Insert Pandora version as agent version - pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'version'}, -1, $dbh); - - } else { - # Modules who cannot connect or something go bad, update last_execution_try field - update_on_error ($pa_config, $id_agente_modulo, $dbh); - } -} - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 1, $dbh); - if ($pa_config{"quiet"} == 0){ - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - } - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - exit; -} - diff --git a/pandora_server/bin/pandora_plugin b/pandora_server/bin/pandora_plugin deleted file mode 100755 index de026d1724..0000000000 --- a/pandora_server/bin/pandora_plugin +++ /dev/null @@ -1,402 +0,0 @@ -#!/usr/bin/perl - -########################################################################## -# Pandora FMS Plugin Server -# http://www.pandorafms.com -########################################################################## -# Copyright (c) 2008-2009 Sancho Lerena, slerena@gmail.com -# (c) 2008-2009 Artica Soluciones Tecnologicas S.L -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use Time::Local; # DateTime basic manipulation -use threads; -use threads::shared; -use HTML::Entities; - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; - -# Queue management -my @pending_task : shared; -my %pending_task_hash : shared; -my %current_task_hash : shared; -my $queue_lock : shared; - - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config, "Pandora FMS Plugin Server"); - -# Check for pandora_exec -my $pandora_exec = "pandora_exec"; -if (system("$pandora_exec > /dev/null 2>&1") != 256) { - print " [E] $pandora_exec not found.\n\n"; - exit; -} - -# Read config file for Global variables -pandora_loadconfig (\%pa_config, 4); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS Plugin server starting", "SYSTEM", "System"); - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"quiet"} eq "0"){ - print " [*] Backgrounding Pandora FMS Plugin Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - -# Launch now all plugin threads -# $ax is local thread id for this server -for (my $ax=0; $ax < $pa_config{'plugin_threads'}; $ax++){ - threads->new( \&pandora_plugin_consumer, \%pa_config, $ax); -} - -# Launch now the producer thread -threads->new( \&pandora_plugin_producer, \%pa_config); - -# Last thread is the main process (this process) -if ($pa_config{"quiet"} == 0){ - print " [*] All threads loaded and running \n\n"; -} - -# Start Pandora FMS loggin -pandora_startlog (\%pa_config); - -my $dbhost = $pa_config{'dbhost'}; -my $dbname = $pa_config{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", - $pa_config{'dbuser'}, - $pa_config{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); - -# Server keepalive thread running in main thread on a infinite loop -while (1) { - pandora_serverkeepaliver (\%pa_config, 4, $dbh); - threads->yield; - sleep ($pa_config{"server_threshold"}); -} - -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#--------------------- Main Perl Code below this line----------------------- -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 4, $dbh); - exit; -} - -########################################################################## -# SUB pandora_plugin_consumer_subsystem -# Subsystem to process Plugin modules -# This module runs each X seconds (server threshold) checking for Plugin modules status -########################################################################## -sub pandora_plugin_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - - if ($pa_config->{"quiet"} == 0){ - print " [*] Starting up Plugin Consumer Thread # $thread_id \n"; - } - - my $data_id_agent_module; - # Create Database handler - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $counter =0; - - LOOP: while (1) { - if ($counter > 10) { - threads->yield; - sleep (1); - $counter = 0; - } - - # Take the first element on the shared queue - # Insert this element on the current task hash - { - lock $queue_lock; - if (scalar(@pending_task) == 0){ - $counter++; - next LOOP; - } - - $data_id_agent_module = shift(@pending_task); - delete($pending_task_hash{$data_id_agent_module}); - $current_task_hash{$data_id_agent_module}=1; - } - - # Executing network task with unmanaged error trapping - eval { - # Call network execution process - # exec_network_module ( $pa_config, $data_id_agent_module, $dbh); - exec_plugin_module ($pa_config, $data_id_agent_module, $dbh); - }; - if ($@){ - logger ($pa_config, "[ERROR] Plugin Task for module $data_id_agent_module causes a system exception", 0); - logger ($pa_config, "ERROR Code: $@", 1); - } - - # Remove from queue. If catch an error, probably data is - # not been processed, but has been freed from task queue - { - lock $queue_lock; - delete($current_task_hash{$data_id_agent_module}); - } - $counter = 0; - threads->yield; - } -} - -sub pandora_plugin_producer ($) { - my $pa_config = $_[0]; - print " [*] Starting up Plugin Producer Thread ...\n"; - - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $server_id = $pa_config->{'server_id'}; - - # Initialize variables for posterior usage - my $query1; - my @sql_data1; - my $data_id_agente_modulo; - my $data_flag; - my $exec_sql1; - - while (1) { - if ($pa_config->{"pandora_master"} != 1) { - # Query for normal server, not MASTER server - $query1 = "SELECT - tagente_modulo.id_agente_modulo, - tagente_modulo.flag - UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado - WHERE - id_plugin_server = $server_id - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.id_plugin != 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND ( - tagente_modulo.flag = 1 - OR - (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() - ) - ORDER BY - tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC "; - } else { - # Query for MASTER SERVER ! - $query1 = "SELECT - DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado - WHERE - ( (tagente.id_plugin_server = $server_id) OR - (tagente.id_plugin_server = ANY(SELECT id_server FROM tserver WHERE status = 0 AND id_server != $server_id AND plugin_server = 1)) - ) - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_modulo.id_plugin != 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND - (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() ) - ORDER BY - tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC"; - } - $exec_sql1 = $dbh->prepare($query1); - $exec_sql1 ->execute; - while (@sql_data1 = $exec_sql1->fetchrow_array()) { - $data_id_agente_modulo = $sql_data1[0]; - $data_flag = $sql_data1[1]; - - # Skip modules already queued - if ((!defined($pending_task_hash{$data_id_agente_modulo})) && - (!defined($current_task_hash{$data_id_agente_modulo}))) { - if ($data_flag == 1){ - $dbh->do("UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = $data_id_agente_modulo") - } - # Locking scope, do not remove redundant { } - { - lock $queue_lock; - push (@pending_task, $data_id_agente_modulo); - $pending_task_hash {$data_id_agente_modulo}=1; - } - } - } - $exec_sql1->finish(); - threads->yield; - sleep($pa_config->{"server_threshold"}); - } # Main loop -} - - -########################################################################## -# SUB exec_plugin_module (paconfig, id_agente_modulo, dbh ) -# Execute plugin module task -########################################################################## -sub exec_plugin_module { - my $pa_config = $_[0]; - my $id_am = $_[1]; - my $dbh = $_[2]; - - # Set global variables for this sub - my $timeout = $pa_config->{'plugin_timeout'}; - my $agent_module; # hash container for tagente_modulo record - my $plugin; # hash container for tplugin - - # Get a full hash for agent_plugin record reference ($agent_module) - my $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_am"; - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - - # TODO: Do a check for void result ! (Gives a DBD::mysql::st execute failed: MySQL server has gone away at /usr/local/bin/pandora_plugin line 304) - - $agent_module = $exec_sql->fetchrow_hashref; - - # Get a full hash for plugin record reference ($plugin) - $query_sql = "SELECT * FROM tplugin WHERE id = ".$agent_module->{'id_plugin'}; - $exec_sql = $dbh->prepare($query_sql); - $exec_sql->execute(); - $plugin = $exec_sql->fetchrow_hashref; - - # Calculate min timeout for this call - if ($plugin->{'max_timeout'} < $timeout){ - $timeout = $plugin->{'max_timeout'}; - } - - # Initialize another global sub variables. - my $agent_name = dame_agente_nombre ($pa_config, $agent_module->{'id_agente'}, $dbh); - my $module_data = 0; # 0 data for default - my $module_interval = 0; - - # Build execution command to plugin - my $exec_output = ""; - my $plugin_command = $plugin->{"execute"}; - if ($plugin->{'net_dst_opt'} ne ""){ - $plugin_command = $plugin_command . " ". $plugin->{'net_dst_opt'} ." ". $agent_module->{'ip_target'}; - } - if ($plugin->{'net_port_opt'} ne "") { - $plugin_command = $plugin_command . " ". $plugin->{'net_port_opt'} ." ". $agent_module->{'tcp_port'}; - } - if ($plugin->{'user_opt'} ne "") { - $plugin_command = $plugin_command . " ". $plugin->{'user_opt'} ." ". $agent_module->{'plugin_user'}; - } - if ($plugin->{'pass_opt'} ne "") { - $plugin_command = $plugin_command . " ". $plugin->{'pass_opt'} ." ". $agent_module->{'plugin_pass'}; - } - - # Proccess field / optional / dynamic field - if ($agent_module->{'plugin_parameter'} ne "") { - $plugin_command = $plugin_command . " ". $agent_module->{'plugin_parameter'}; - } - - $plugin_command = decode_entities($plugin_command); - - logger ($pa_config, "Executing AM # $id_am plugin command '$plugin_command'", 9); - # Final command line execution is stored at "plugin_command" - - $module_data = `$pandora_exec $timeout $plugin_command`; - if ($module_data eq "") { - update_on_error ($pa_config, $id_am, $dbh); - undef $plugin; - undef $agent_module; - return; - } - - # Get current timestamp - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp = &UnixDate("today","%s"); - - my %part; - $part{'name'}[0] = $agent_module->{'nombre'}; - $part{'description'}[0] = ""; - $part{'data'}[0] = $module_data; - my $tipo_modulo = dame_nombretipomodulo_idagentemodulo ($pa_config, $agent_module->{'id_tipo_modulo'}, $dbh); - - # 1 - generic_data - # 2 - generic_proc - # 3 - generic_data_string - # 4 - generic_data_inc - # 19, 20 - image - - if (1 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif (4 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data_inc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif (3 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data_string ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - # Generic_proc - elsif (2 == $agent_module->{'id_tipo_modulo'}) { - module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif ( (19 == $agent_module->{'id_tipo_modulo'}) || (20 == $agent_module->{'id_tipo_modulo'}) ) { - module_generic_image ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - else { # Unknown module!, this IS a problem - logger ($pa_config, "Plugin Server Problem with unknown module type '$tipo_modulo'", 0); - } - # Update agent last contact - # Insert Pandora version as agent version - pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'version'}, -1, $dbh); - - $exec_sql->finish(); #close tagent_plugin hash reference - undef $plugin; - undef $agent_module; - undef %part; -} diff --git a/pandora_server/bin/pandora_prediction b/pandora_server/bin/pandora_prediction deleted file mode 100755 index 21d4bac517..0000000000 --- a/pandora_server/bin/pandora_prediction +++ /dev/null @@ -1,431 +0,0 @@ -#!/usr/bin/perl - -########################################################################## -# Pandora FMS Prediction Server -# http://www.pandorafms.com -########################################################################## -# Copyright (c) 2008 Sancho Lerena, slerena@gmail.com -# (c) 2008 Artica Soluciones Tecnologicas S.L -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use Time::Local; # DateTime basic manipulation -use threads; -use threads::shared; - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; - -# Queue management -my @pending_task : shared; -my %pending_task_hash : shared; -my %current_task_hash : shared; -my $queue_lock : shared; - - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config, "Pandora FMS Prediction Server"); - -# Read config file for Global variables -pandora_loadconfig (\%pa_config, 5); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS Prediction server starting", "SYSTEM", "System"); - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"quiet"} eq "0"){ - print " [*] Backgrounding Pandora FMS Prediction Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - -# Launch now all prediction threads -# $ax is local thread id for this server -for (my $ax=0; $ax < $pa_config{'prediction_threads'}; $ax++){ - threads->new( \&pandora_prediction_consumer, \%pa_config, $ax); -} - -# Launch now the producer thread -threads->new( \&pandora_prediction_producer, \%pa_config); - -# Last thread is the main process (this process) -if ($pa_config{"quiet"} == 0){ - print " [*] All threads loaded and running \n"; -} - -# Start Pandora FMS loggin -pandora_startlog (\%pa_config); - -my $dbhost = $pa_config{'dbhost'}; -my $dbname = $pa_config{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", - $pa_config{'dbuser'}, - $pa_config{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); - -# Server keepalive thread running in main thread on a infinite loop -while (1) { - pandora_serverkeepaliver (\%pa_config, 5, $dbh); - threads->yield; - sleep ($pa_config{"server_threshold"}); -} - -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#--------------------- Main Perl Code below this line----------------------- -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 5, $dbh); - exit; -} - - - -sub pandora_prediction_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - - if ($pa_config->{"quiet"} == 0){ - print " [*] Starting up Prediction Consumer Thread # $thread_id \n"; - } - - my $data_id_agent_module; - # Create Database handler - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $counter =0; - - LOOP: while (1) { - if ($counter > 10) { - sleep (1); - $counter = 0; - } - - # Take the first element on the shared queue - # Insert this element on the current task hash - { - lock $queue_lock; - if (scalar(@pending_task) == 0){ - $counter++; - next LOOP; - } - - $data_id_agent_module = shift(@pending_task); - delete($pending_task_hash{$data_id_agent_module}); - $current_task_hash{$data_id_agent_module}=1; - } - - # Executing network task with unmanaged error trapping - eval { - # Call network execution process - # exec_network_module ( $pa_config, $data_id_agent_module, $dbh); - exec_prediction_module ($pa_config, $data_id_agent_module, $dbh); - }; - if ($@){ - logger ($pa_config, "[ERROR] Prediction Task for module $data_id_agent_module causes a system exception", 0); - logger ($pa_config, "ERROR Code: $@", 1); - } - - # Remove from queue. If catch an error, probably data is - # not been processed, but has been freed from task queue - { - lock $queue_lock; - delete($current_task_hash{$data_id_agent_module}); - } - $counter = 0; - } -} - -sub pandora_prediction_producer ($) { - my $pa_config = $_[0]; - - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - - my $server_id = $pa_config->{'server_id'}; - - # Initialize variables for posterior usage - my $query1; - my @sql_data1; - my $data_id_agente_modulo; - my $data_flag; - my $exec_sql1; - - while (1) { - if ($pa_config->{"pandora_master"} != 1) { - # Query for normal server, not MASTER server - $query1 = "SELECT - tagente_modulo.id_agente_modulo, - tagente_modulo.flag - FROM - tagente, tagente_modulo, tagente_estado - WHERE - id_prediction_server = $server_id - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.prediction_module != 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND ( - tagente_modulo.flag = 1 - OR - (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() - ) - ORDER BY - last_execution_try ASC "; - } else { - # Query for MASTER SERVER ! - $query1 = "SELECT - DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag - FROM - tagente, tagente_modulo, tagente_estado - WHERE - ( (tagente.id_prediction_server = $server_id) OR - (tagente.id_prediction_server = ANY(SELECT id_server FROM tserver WHERE status = 0 AND id_server != $server_id AND prediction_server = 1) ) - ) AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_modulo.prediction_module != 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND - (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) - ORDER BY last_execution_try ASC"; - } - $exec_sql1 = $dbh->prepare($query1); - $exec_sql1 ->execute; - while (@sql_data1 = $exec_sql1->fetchrow_array()) { - $data_id_agente_modulo = $sql_data1[0]; - $data_flag = $sql_data1[1]; - # Skip modules already queued - if ((!defined($pending_task_hash{$data_id_agente_modulo})) && - (!defined($current_task_hash{$data_id_agente_modulo}))) { - if ($data_flag == 1){ - $dbh->do("UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = $data_id_agente_modulo") - } - # Locking scope, do not remove redundant { } - { - lock $queue_lock; - push (@pending_task, $data_id_agente_modulo); - $pending_task_hash {$data_id_agente_modulo}=1; - } - } - } - #logger ($pa_config, "Items in Network Pending Queue: ".scalar(@pending_task), 5); - $exec_sql1->finish(); - sleep($pa_config->{"server_threshold"}); - } # Main loop -} - - -########################################################################## -# SUB exec_prediction_module (paconfig, id_agente_modulo, dbh ) -# Execute prediction module task -########################################################################## -sub exec_prediction_module { - my $pa_config = $_[0]; - my $id_am = $_[1]; - my $dbh = $_[2]; - - # This function internal variables - my $i; # Internal counter for loops - my $n = 0; # total of real data values - - # Set global variables for this sub - my $agent_module; # hash reference for tagente_modulo record - my $target_module; # hash reference for targetted tagente_modulo - - # Get a full hash for agent_module record reference ($agent_module) - my $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_am"; - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - $agent_module = $exec_sql->fetchrow_hashref; - - # Get a full hash for target agent_module record reference ($target_module) - $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = " . $agent_module->{'prediction_module'}; - $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - $target_module = $exec_sql->fetchrow_hashref; - # Prediction mode explanation - # - # 0 is for target type of generic_proc. It compares latest data with current data. Needs to get - # data on a "middle" interval, so if interval is 300, get data to compare with 150 before - # and 150 in the future. If current data is ABOVE or BELOW average +- typical_deviation - # this is a BAD value (0), if not is ok (1) and written in target module as is. - # more interval configured for this module, more "margin" has to compare data. - # - # 1 is for target type of generic_data. It get's data in the future, using the interval given in - # module. It gets average from current timestamp to INTERVAL in the future and gets average - # value. Typical deviation is not used here. - - my $prediction_mode; - if ($agent_module->{'id_tipo_modulo'} == 2){ - $prediction_mode = 0; # proc - } else { - $prediction_mode = 1; # data - } - - # Initialize another global sub variables. - my $agent_name = dame_agente_nombre ($pa_config, $agent_module->{'id_agente'}, $dbh); - my $module_data = 0; # 0 data for default - - # Get current timestamp - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp = &UnixDate("today","%s"); - - # Get different data from each week one month ago (4 values) - # $agent_module->{'module_interval'} uses a margin of interval to get average data from the past - my @week_data; - my @week_utimestamp; - - for ($i=0; $i<4; $i++){ - $week_utimestamp[$i] = $utimestamp - (84600*7*($i+1)); - # Adjust for proc prediction - if ($prediction_mode == 0) { - $week_utimestamp[$i] = $week_utimestamp[$i] - ($agent_module->{'module_interval'} / 2); - } - } - - # Let's calculate statistical average using past data - my $average = 0; - my $temp1 = 0; - for ($i=0; $i < 4; $i++){ - my $first_data; - my $last_data; - my $average_interval; - my $sum_data = 0; - - $temp1 = $week_utimestamp[$i] + $agent_module->{'module_interval'}; - # Get data for week $i in the past - $query_sql = 'SELECT AVG(datos) FROM tagente_datos WHERE id_agente_modulo = '. $target_module->{'id_agente_modulo'}. ' AND utimestamp > '.$week_utimestamp[$i].' AND utimestamp < '.$temp1; - $average_interval = get_db_free_field ($query_sql, $dbh); - - # Need to get data outside interval because no data. - if ($average_interval == 0){ - $query_sql = 'SELECT datos FROM tagente_datos WHERE id_agente_modulo = '. $target_module->{'id_agente_modulo'}. ' AND utimestamp > '.$week_utimestamp[$i].' LIMIT 1'; - $last_data = get_db_free_field ($query_sql, $dbh); - if ($last_data != 0){ - $sum_data++; - } - $query_sql = 'SELECT datos FROM tagente_datos WHERE id_agente_modulo = '. $target_module->{'id_agente_modulo'}. ' AND utimestamp < '.$temp1.' LIMIT 1'; - $first_data = get_db_free_field ($query_sql, $dbh); - if ($first_data != 0){ - $sum_data++; - } - $week_data[$i] = (($last_data + $first_data) / $sum_data); - } else { - $week_data[$i] = $average_interval; - } - - # It's possible that one of the week_data[i] values was not valid (NULL) - # so recheck it and relay on n=0 for "no data" values set to 0 in result - # Calculate total ammount of valida data for each data sample - if ( (is_numeric($week_data[$i])) && ($week_data[$i] > 0) ){ - $n++; - # Average SUM - $average = $average + $week_data[$i]; - } - } - - # Real average value - if ($n > 0){ - $average = $average / $n; - } else { - $average = 0; - } - - # (PROC) Compare with current data - if ($prediction_mode == 0){ - # Calculate typical deviation - my $typical_deviation = 0; - for ($i=0; $i< $n; $i++){ - if ( (is_numeric($week_data[$i])) && ($week_data[$i] > 0) ) { - $typical_deviation = $typical_deviation + (($week_data[$i] - $average)**2); - } - } - $typical_deviation = sqrt ($typical_deviation / ($n-1)); - - $query_sql = 'SELECT datos FROM tagente_estado WHERE id_agente_modulo = '.$target_module->{'id_agente_modulo'}; - my $current_value = get_db_free_field ($query_sql, $dbh); - if ( ($current_value > ($average - $typical_deviation)) && ($current_value < ($average + $typical_deviation)) ){ - $module_data = 1; # OK !! - } else { - $module_data = 0; # Out of predictions - } - } else { - # Prediction based on data - $module_data = $average; - } - - # Build data for insertion - my %part; - $part{'name'}[0] = $agent_module->{'nombre'}; - $part{'description'}[0] = ""; - $part{'data'}[0] = $module_data; - my $tipo_modulo = dame_nombretipomodulo_idagentemodulo ($pa_config, $agent_module->{'id_tipo_modulo'}, $dbh); - - # 1 - generic_data - # 2 - generic_proc - if (1 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif (2 == $agent_module->{'id_tipo_modulo'}) { - module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - else { # Unknown module!, this IS a problem - logger ($pa_config, "[FATAL] Prediction Server Problem with unknown module type '$tipo_modulo'", 0); - print "[DEBUG] Executing Prediction UNKONWN MODULE TYPE$@\n"; - exit; - } - # Update agent last contact - # Insert Pandora version as agent version - pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'version'}, -1, $dbh); -} diff --git a/pandora_server/bin/pandora_recon b/pandora_server/bin/pandora_recon deleted file mode 100755 index 3b6c4fba91..0000000000 --- a/pandora_server/bin/pandora_recon +++ /dev/null @@ -1,711 +0,0 @@ -#!/usr/bin/perl - -########################################################################## -# Pandora FMS Recon Server -# http://www.pandorafms.com -########################################################################## -# Copyright (c) 2007-2008 Sancho Lerena, slerena@gmail.com -# (c) 2007-2008 Artica Soluciones Tecnologicas S.L -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats - # of input, output and compare -use Net::Ping; -use Time::Local; # DateTime basic manipulation -use NetAddr::IP; # To manage IP Addresses - -# Detect if Net:Traceroute::Pureperl is available. -# If not, parent detection will be disabled -my $traceroute_loaded = 1; -# Traceroute needs traceroute command -unless ( eval "use Net::Traceroute::PurePerl; 1" ) { - $traceroute_loaded = 0; -} - -use POSIX; # to use ceil() function -use Socket; # to resolve address -use threads; -use threads::shared; - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; -# Alarm signal management -$SIG{ALRM} = sub { return 0; }; - -# Queue management -my @pending_task : shared; -my %pending_task_hash : shared; -my %current_task_hash : shared; -my $queue_lock : shared; -my $icmp_lock : shared; - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config, "Pandora FMS Recon server"); - -# Read config file for Global variables -pandora_loadconfig (\%pa_config, 3); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS Recon Daemon starting", "SYSTEM", "System"); - -# Check for traceroute - -if ($traceroute_loaded == 0){ - print " [!] Traceroute has noot been compiled. Parent detection disabled.\n\n"; -} - -# Check for xprobe2 -my $xprobe2 = $pa_config{"xprobe2"}; - -if (! -e $xprobe2) { - print " [E] $xprobe2 not found. Pandora FMS Recon cannot detect OS types without it.\n\n"; - exit; -} else { - print " [*] $xprobe2 Detected.\n\n"; -} - -sleep(1); - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"quiet"} eq "0"){ - print " [*] Backgrounding Pandora FMS Recon Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - -# Launch now all plugin threads -# $ax is local thread id for this server - -for (my $ax=0; $ax < $pa_config{'recon_threads'}; $ax++){ - threads->new( \&pandora_recon_consumer, \%pa_config, $ax); -} - -# Launch now the producer thread -threads->new( \&pandora_recon_producer, \%pa_config); - -sleep(1); - -# Connect ONCE to Database, we cannot pass DBI handler to all subprocess because -# cannot share DBI between threads without use method CLONE. - -my $pa_config = \%pa_config; -my $dbhost = $pa_config->{'dbhost'}; -my $dbuser = $pa_config->{'dbuser'}; -my $dbpass = $pa_config->{'dbpass'}; -my $dbname = $pa_config->{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", $dbuser, $dbpass, { RaiseError => 1, AutoCommit => 1 }); - -while ( 1 ){ - pandora_serverkeepaliver ($pa_config, 3, $dbh); - threads->yield; - sleep($pa_config->{"server_threshold"}); -} - -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#--------------------- Main Perl Code below this line----------------------- -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ - -sub pandora_recon_producer ($) { - my $pa_config = $_[0]; - print " [*] Starting up Recon Producer Thread ...\n"; - - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $server_id = $pa_config->{'server_id'}; - - # Initialize variables for posterior usage - my $query_sql; - my @sql_data1; - my $data_id_task; - my $exec_sql1; - - while (1) { - $query_sql = "SELECT * FROM trecon_task - WHERE - id_recon_server = $server_id - AND ( - status = 1 - OR - (utimestamp + interval_sweep) < UNIX_TIMESTAMP() - ) - "; - - $exec_sql1 = $dbh->prepare($query_sql); - $exec_sql1 ->execute; - while (@sql_data1 = $exec_sql1->fetchrow_array()) { - $data_id_task = $sql_data1[0]; - - # Skip modules already queued - if ((!defined($pending_task_hash{$data_id_task})) && - (!defined($current_task_hash{$data_id_task}))) { - pandora_update_reconstatus ($pa_config, $dbh, $data_id_task, 0); - # Locking scope, do not remove redundant { } - { - lock $queue_lock; - push (@pending_task, $data_id_task); - $pending_task_hash {$data_id_task}=1; - } - } - } - $exec_sql1->finish(); - threads->yield; - sleep($pa_config->{"server_threshold"}); - } # Main loop -} - -########################################################################## -# SUB pandora_recon_consumer -# This module runs each X seconds (server threshold) checking for recon task -########################################################################## -sub pandora_recon_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - - if ($pa_config->{"quiet"} == 0){ - print " [*] Starting up Recon Consumer Thread # $thread_id \n"; - } - - my $data_id_task; - # Create Database handler - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $counter =0; - - LOOP: while (1) { - if ($counter > 10) { - threads->yield; - sleep($pa_config->{"server_threshold"}); - $counter = 0; - } - - # Take the first element on the shared queue - # Insert this element on the current task hash - { - lock $queue_lock; - if (scalar(@pending_task) == 0){ - $counter++; - next LOOP; - } - - $data_id_task = shift(@pending_task); - delete($pending_task_hash{$data_id_task}); - $current_task_hash{$data_id_task}=1; - } - - # Executing recon task with unmanaged error trapping - eval { - pandora_recon_exec_task ($pa_config, $data_id_task, $dbh); - }; - if ($@){ - logger ($pa_config, "[ERROR] Recon Task for Task ID $data_id_task causes a system exception", 0); - logger ($pa_config, "ERROR Code: $@", 1); - } - - # Remove from queue. If catch an error, probably data is - # not been processed, but has been freed from task queue - { - lock $queue_lock; - delete($current_task_hash{$data_id_task}); - } - $counter = 0; - threads->yield; - } -} - - -########################################################################## -# SUB pandora_detect_os (paconfig, host) -# Detect OS using xprobe2 tool. Return tconfig_os id code. -########################################################################## - -sub pandora_detect_os { - my $pa_config = $_[0]; - my $host = $_[1]; - - my $xprobe2 = $pa_config->{"xprobe2"}; - if (! -e $xprobe2){ - return 10; # other - } - my $command= ""; - eval { - $command = `$xprobe2 $host 2> /dev/null | grep "Running OS" 2> /dev/null | head -1 2> /dev/null`; - }; - if ($@){ - return 10; - } - return pandora_get_os ($command); -} - - -########################################################################## -# SUB pandora_exec_task (pa_config, id_task) -# Execute task -########################################################################## -sub pandora_recon_exec_task { - my $pa_config = $_[0]; - my $id_task = $_[1]; - my $dbh = $_[2]; - - my $target_ip; # Real ip to check - my @ip2; # temp array for NetAddr::IP - my $space; # temp var to store space of ip's for netaddr::ip - my $query_sql; # for use in SQL - my $exec_sql; # for use in SQL - my $sql_data; # for use in SQL - - $query_sql = "SELECT * FROM trecon_task WHERE id_rt = $id_task"; - $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - if ($exec_sql->rows == 0) { - # something wrong.. - return -1; - } - - $sql_data = $exec_sql->fetchrow_hashref(); - my $status = $sql_data->{"status"}; - my $interval = $sql_data->{"interval"}; - my $target_network = $sql_data->{"subnet"}; - my $task_name = $sql_data->{"name"}; - my $task_ncprofile = $sql_data->{"id_network_profile"}; - my $task_group = $sql_data->{"id_group"}; - my $task_create_incident = $sql_data->{"create_incident"}; - my $task_id_os = $sql_data->{"id_os"}; - - my $position = 0; - my $list_ip = ""; - my $list_host = ""; - my $host_found = 0; - my $add_host = 0; - my $id_parent = 0; - my $id_os = 0; - - my $detected = 0; - - # Asign target dir to netaddr object "space" - $space = new NetAddr::IP $target_network; - if (!defined($space)){ - logger ($pa_config, "Bad network $target_network for task $task_name", 2); - pandora_update_reconstatus ($pa_config, $dbh, $id_task, -1); - return -1; - } - - my $total_hosts= $space->num +1 ; - # Begin scanning main loop - do { - @ip2 = split(/\//,$space); - $target_ip = $ip2[0]; - $space++; - $position++; - - $add_host = 0; - # Is this IP listed for any agent ? - if (pandora_check_ip ($pa_config, $dbh, $target_ip) == 0){ - $detected = 0; - # Check first for ICMP for this IP. Sometimes ICMP is blocked so check for other ports as well - if ( scan_icmp ($target_ip, $pa_config->{'networktimeout'}) == 1) { - $detected = 1; - } elsif ( scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 3389) == 1 || scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 5900) == 1) { - #Check for Remote Desktop & VNC (Desktop & Server machines) - $detected = 1; - } elsif ( scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 10000) == 1 || scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 161) == 1) { - #Check for management ports 10000 = Webmin, 161 = SNMP (Most embedded devices) - $detected = 1; - } elsif ( scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 22) == 1 || scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 25) == 1) { - #Check for SSH & Mail (Servers and Unix machines) - $detected = 1; - } elsif ( scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 80) == 1 || scan_tcp ($target_ip, $pa_config->{'networktimeout'}, 3306) == 1) { - #Check for WWW & MySQL (Webservers and systems in a DMZ) - $detected = 1; - } - - if ($detected == 1){ - $id_os = pandora_detect_os ($pa_config, $target_ip); - if ($task_id_os == -1){ - $add_host = 1; - } elsif ($id_os == $task_id_os){ - $add_host = 1; - } - } - - if ($add_host == 1){ - $host_found ++; - my $target_ip_resolved = resolv_ip2name($target_ip); - $list_ip = $list_ip . " " . $target_ip; - $list_host = $list_host . " " . $target_ip_resolved; - $id_parent = pandora_getparent ($pa_config, $target_ip, $dbh); - - # If has a network profile, create agent and modules - my $agent_id; - if ($task_ncprofile > 0){ - # Create address, agent and more... - my $target_ip_id = pandora_task_create_address ($pa_config, $dbh, $id_task, $target_ip); - $agent_id = pandora_task_create_agent ($pa_config, $dbh, $target_ip, $target_ip_id, $task_group, $target_ip_resolved, $id_parent, $id_os); - pandora_task_create_agentmodules ($pa_config, $dbh, $agent_id, $task_ncprofile, $target_ip); - } else { - my $target_ip_id = pandora_task_create_address ($pa_config, $dbh, $id_task, $target_ip); - $agent_id = pandora_task_create_agent($pa_config, $dbh, $target_ip, $target_ip_id, $task_group, $target_ip_resolved, $id_parent, $id_os); - } - my $title = "[RECON] New host [$target_ip_resolved] detected on network [$target_network]"; - # Always create event about this detected IP - pandora_event ($pa_config, $title,$task_group, $agent_id, 2, 0, 0, 'recon_host_detected', $dbh); - } - } - - my $progress = ceil($position / ($total_hosts / 100)); - pandora_update_reconstatus ($pa_config, $dbh, $id_task, $progress); - } while ($space < $space->broadcast); # fin del buclie principal de iteracion de Ips - - # Create incident - if (($host_found > 0) && ($task_create_incident == 1)){ - my $my_timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $text = "At $my_timestamp a new hosts ($host_found) has been detected by Pandora FMS Recon Server running on [".$pa_config->{'servername'}."_Recon]. This incident has been automatically created following instructions for this recon task [$task_name].\n\n"; - if ($task_ncprofile > 0){ - $text = $text."Aditionally, and following instruction for this task, agent(s) has been created, with modules assigned to network component profile [".give_network_component_profile_name ($pa_config, $dbh, $task_ncprofile)."]. Please check this agent as soon as possible to verify it."; - } - $text = $text . "\n\nThis is the list of IP addresses found: \n\n$list_host "; - pandora_create_incident ( $pa_config, $dbh, "[RECON] New hosts detected", $text, 0, 0, "Pandora FMS Recon Server", $task_group); - } - # Mark RECON TASK as done (-1) - pandora_update_reconstatus ($pa_config, $dbh, $id_task, -1); -} - - - -############################################################################## -# pandora_ping_icmp (destination, timeout) - Do a ICMP scan, 1 if alive, 0 if not -############################################################################## -sub scan_icmp { - my $dest = $_[0]; - my $l_timeout = $_[1]; - - # Temp vars. - my $result = 0; - my $p; - - # Check for valid destination - if (!defined($dest)){ - return 0; - } - { - lock $icmp_lock; - $p = Net::Ping->new(); - } - - if ($p->ping($dest)){ - $p->close(); - undef ($p); - return 1; - } else { - return 0; - } -} - -############################################################################## -# pandora_ping_icmp (destination, timeout) - Do a ICMP scan, 1 if alive, 0 if not -############################################################################## -sub scan_tcp { - my $dest = $_[0]; - my $l_timeout = $_[1]; - my $dest_port = $_[2]; - - my $opened = 0; - - eval { - alarm $l_timeout; - my $handle=IO::Socket::INET->new( - Proto=>"tcp", - PeerAddr=>$dest, - PeerPort=>$dest_port); - if ($handle){ - $opened =1; - } - alarm 0; - }; - if ($@){ - return 0; - } - return $opened; -} - - -########################################################################## -# SUB resolv_ip2name (ip_address) -# return name (if could resolve) or ip of ipaddress -########################################################################## -sub resolv_ip2name { - my $ip = $_[0]; - my $addr=inet_aton($ip); - if ($addr) { - my $name=gethostbyaddr($addr, AF_INET); - if ($name) { - return $name; - } else { - return $ip; - } - } else { - return $ip; - } -} - -########################################################################## -# SUB pandora_check_ip (pa_config, dbh, ip_address) -# Return 1 if this IP exists, 0 if not -########################################################################## -sub pandora_check_ip { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $ip_address = $_[2]; - - my $query_sql = "SELECT * FROM taddress WHERE ip = '$ip_address' "; - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - if ($exec_sql->rows != 0) { - $exec_sql->finish(); - return 1; - } else { - $exec_sql->finish(); - return 0; - } -} - - -########################################################################## -# SUB pandora_get_agent_from_ip (pa_config, dbh, ip_address) -# Return Id_agent for agent with IP_Address passed as parameter -########################################################################## -sub pandora_get_agent_from_ip { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $ip_address = $_[2]; - if ((!defined($ip_address)) || ($ip_address eq "")){ - return 0; - } - - return get_db_free_field ("SELECT id_agent FROM taddress, taddress_agent WHERE taddress_agent.id_a = taddress.id_a AND ip = '$ip_address'", $dbh); -} - -########################################################################## -# SUB pandora_update_reconstatus (pa_config, dbh, id_task, status) -# Update recontask status flag -########################################################################## -sub pandora_update_reconstatus { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $id_task = $_[2]; - my $status = $_[3]; - - my $my_utimestamp = &UnixDate("today", "%s"); - my $query_sql2 = "UPDATE trecon_task SET utimestamp = $my_utimestamp, status = $status WHERE id_rt = $id_task"; - $dbh->do($query_sql2); -} - -########################################################################## -# SUB pandora_task_create_address (pa_config, dbh, id_task, address) -# Add address to table taddress, return ID of created record -########################################################################## -sub pandora_task_create_address { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $id_task = $_[2]; - my $ip_address = $_[3]; - my $query_sql2 = "INSERT INTO taddress (ip) VALUES ('$ip_address')"; - $dbh->do ($query_sql2); - my $lastid = $dbh->{'mysql_insertid'}; - return $lastid; -} - -########################################################################## -# SUB pandora_task_create_agent (pa_config, dbh, target_ip, target_ip_id, -# id_group, name, id_parent) -# Create agent, and associate address to agent in taddress_agent table. -# it returns created id_agent. -########################################################################## -sub pandora_task_create_agent { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $target_ip = $_[2]; - my $target_ip_id = $_[3]; - my $id_group = $_[4]; - my $name = $_[5]; - my $id_parent = $_[6]; - my $id_os = $_[7]; - - return pandora_create_agent ($pa_config, $dbh, $target_ip, $target_ip_id, $id_group, 0, $name, $id_parent, $id_os); -} - -########################################################################## -# SUB pandora_task_create_agentmodules (pa_config, dbh, agent_id, ncprofile, ipaddress) -# Create modules from a network component profile and associated to given agent -########################################################################## -sub pandora_task_create_agentmodules { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $agent_id = $_[2]; - my $ncprofile_id = $_[3]; - my $ip_adress = $_[4]; - my $sql_data; - - - # Search each network component that belongs to ncprofile_id - my $query_sql = "SELECT * FROM tnetwork_profile_component WHERE id_np = $ncprofile_id "; - - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - while ($sql_data = $exec_sql->fetchrow_hashref()) { - my $query_sql2 = "SELECT * FROM tnetwork_component wHERE id_nc = ". $sql_data->{"id_nc"}; - - my $exec_sql2 = $dbh->prepare($query_sql2); - $exec_sql2 ->execute; - if ($exec_sql2->rows != 0) { - my $sql_data2 = $exec_sql2->fetchrow_hashref(); - - my $name = ""; - $name = $sql_data2->{"name"}; - my $description = ""; - $description = $sql_data2->{"description"}; - - my $type = "1"; - $type = $sql_data2->{"type"}; - - my $max = 0; - $max = $sql_data2->{"max"}; - - my $min = 0; - $min = $sql_data2->{"min"}; - - my $interval = 300; - $interval = $sql_data2->{"module_interval"}; - - my $tcp_port = ""; - $tcp_port = $sql_data2->{"tcp_port"}; - - my $tcp_send = ""; - $tcp_send = $sql_data2->{"tcp_send"}; - - my $tcp_rcv = ""; - $tcp_rcv = $sql_data2->{"tcp_rcv"}; - - my $snmp_community = "public"; - $snmp_community = $sql_data2->{"snmp_community"}; - - my $snmp_oid = ""; - $snmp_oid = $sql_data2->{"snmp_oid"}; - - my $id_module_group = 0; - $id_module_group = $sql_data2->{"id_module_group"}; - - my $id_module = 0; - $id_module = $sql_data2->{"id_modulo"}; - - my $plugin_user = ""; - $plugin_user = $dbh->quote($sql_data2->{"plugin_user"}); - - my $plugin_pass = ""; - $plugin_pass = $dbh->quote($sql_data2->{"plugin_pass"}); - - my $plugin_parameter = ""; - $plugin_parameter = $dbh->quote($sql_data2->{"plugin_parameter"}); - - my $max_timeout = "30"; - $max_timeout = $sql_data2->{"max_timeout"}; - - my $query_sql3 = "INSERT INTO tagente_modulo (id_agente, id_tipo_modulo, descripcion, nombre, max, min, module_interval, tcp_port, tcp_send, tcp_rcv, snmp_community, snmp_oid, ip_target, id_module_group, flag, disabled, plugin_user, plugin_pass, plugin_parameter, max_timeout, id_modulo ) VALUES ( $agent_id, $type, '$description', '$name', $max, $min, $interval, $tcp_port, '$tcp_send', '$tcp_rcv', '$snmp_community', '$snmp_oid', '$ip_adress', $id_module_group, 1, 0, $plugin_user, $plugin_pass, $plugin_parameter, $max_timeout, $id_module)"; - - - $dbh->do($query_sql3); - my $last_id_agente_modulo = $dbh->{'mysql_insertid'}; - logger($pa_config,"Recon Server: Creating module $name for agent $ip_adress",3); - my $query_sql4; - $query_sql4 = "INSERT INTO tagente_estado (id_agente_modulo, datos, timestamp, estado, id_agente, last_try, utimestamp, current_interval, running_by) VALUES ($last_id_agente_modulo, '', '0000-00-00 00:00:00', 1, $agent_id, '0000-00-00 00:00:00', 0, $interval, 0)"; - - $dbh->do($query_sql4); - undef $sql_data2; - } - $exec_sql2->finish(); - undef $sql_data; - } - $exec_sql->finish(); -} - -sub pandora_getparent ($$){ - my $pa_config = $_[0]; - my $destination = $_[1]; - my $dbh = $_[2]; - - # If don't have traceroute available, just skip - if ($traceroute_loaded == 0){ - return 0; - } - - my $t = new Net::Traceroute::PurePerl( - backend => 'PurePerl', - host => $destination, - debug => 0, - max_ttl => 15, - query_timeout => $pa_config->{"networktimeout"}, - packetlen => 40, - protocol => 'udp', # udp or icmp - ); - - my $success = 0; - - eval { - alarm($pa_config->{"networktimeout"}); - $success = $t->traceroute(); - alarm(0); - }; - if ($@){ - return 0; - } - - if ($t->hops > 1){ - if ($success){ - my $parent_ip = $t->hop_query_host($t->hops-1,0); - return pandora_get_agent_from_ip ($pa_config, $dbh, $parent_ip); - } - } - return 0; -} - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, 0, 4, 0, 0, "system", $dbh); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 3, $dbh); - exit; -} diff --git a/pandora_server/bin/pandora_server b/pandora_server/bin/pandora_server index da5218ae00..b26ba8377e 100755 --- a/pandora_server/bin/pandora_server +++ b/pandora_server/bin/pandora_server @@ -1,10 +1,9 @@ #!/usr/bin/perl ########################################################################## -# Pandora Data Server +# Pandora Server ########################################################################## -# Copyright (c) 2004-2008 Sancho Lerena, slerena@gmail.com -# Copyright (c) 2008 Ramon Novoa, rnovoa@artica.es -# Copyright (c) 2005-2008 Artica Soluciones Tecnologicas S.L +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# Copyright (c) 2009 Artica Soluciones Tecnologicas S.L # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,464 +17,102 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ########################################################################## -# Includes list use strict; use warnings; -use XML::Simple; # Useful XML functions -use Digest::MD5; # MD5 generation -use Time::Local; # DateTime basic manipulation -use DBI; # DB interface with MySQL -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use File::Copy; # Needed to manipulate files -use threads; -use threads::shared; - # Pandora Modules +use PandoraFMS::DB; use PandoraFMS::Config; use PandoraFMS::Tools; -use PandoraFMS::DB; -enterprise_load (); +use PandoraFMS::Core; +use PandoraFMS::DataServer; +use PandoraFMS::NetworkServer; +use PandoraFMS::SNMPServer; +use PandoraFMS::ReconServer; +use PandoraFMS::WMIServer; +use PandoraFMS::PluginServer; +use PandoraFMS::PredictionServer; -# Queue management -my @pending_task : shared; -my %pending_task_hash : shared; -my %active_task_hash : shared; -my %incomplete_task_hash : shared; -my $queue_lock : shared; +# Load enterprise module +enterprise_load () && print " [*] Pandora FMS Enterprise module loaded.\n"; -# FLUSH in each IO, only for DEBUG, very slow ! -$| = 0; +# Global vars +my %Config; +my @Servers; +my $DBH; -my %pa_config; +######################################################################################## +# Server shutdown +######################################################################################## +sub pandora_shutdown () { + logger (\%Config, 'Pandora FMS Server \'' . $Config{'servername'} . '\' Shutdown by signal ', 0); + + # Stop servers + foreach my $server (@Servers) { + $server->stop (); + } + + print_message (\%Config, ' [*] Shutting down ' . $Config{'servername'} . "(received signal)...\n", 0); + db_disconnect ($DBH); + exit (0); +} $SIG{'TERM'} = 'pandora_shutdown'; $SIG{'INT'} = 'pandora_shutdown'; -# Init main loop -pandora_init(\%pa_config,"Pandora FMS Data Server"); - -# Read config file for Global variables -pandora_loadconfig (\%pa_config,0); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS Data Server Daemon starting", "SYSTEM", "System"); +# Initialize +pandora_init(\%Config, 'Pandora FMS Server'); +pandora_load_config (\%Config); # Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"verbosity"} > 0){ - print " [*] Backgrounding Pandora FMS Data Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); +if ($Config{'daemon'} == 1) { + print_message (\%Config, " [*] Backgrounding Pandora FMS Server process.\n\n", 0); + pandora_daemonize (\%Config); } -# Launch all data_consumer threads -for (my $ax=0; $ax < $pa_config{"dataserver_threads"}; $ax++){ - threads->new( \&pandora_data_consumer, \%pa_config, $ax); -} +# Connect to the DB +$DBH = db_connect ('mysql', $Config{'dbname'}, $Config{'dbhost'}, 3306, + $Config{'dbuser'}, $Config{'dbpass'}); -# Launch producer thread -threads->new( \&pandora_data_producer, \%pa_config); - -if ($pa_config{"verbosity"} > 0){ - print " [*] All threads loaded and running \n\n"; -} +pandora_audit (\%Config, 'Pandora FMS Server Daemon starting', 'SYSTEM', 'System', $DBH); # Start logging -pandora_startlog (\%pa_config); +pandora_start_log (\%Config); -my $dbhost = $pa_config{'dbhost'}; -my $dbname = $pa_config{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", - $pa_config{'dbuser'}, - $pa_config{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); +# Load servers +push (@Servers, new PandoraFMS::DataServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::NetworkServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::ReconServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::SNMPServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::WMIServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::PluginServer (\%Config, $DBH)); +push (@Servers, new PandoraFMS::PredictionServer (\%Config, $DBH)); +enterprise_hook('load_enterprise_servers', [\@Servers, \%Config, $DBH]); +# Remove disabled servers +@Servers = grep { defined ($_) } @Servers; +# Run +foreach my $server (@Servers) { + $server->run (); + $server->upEvent (); +} + +# Main loop while (1) { - pandora_serverkeepaliver (\%pa_config, 0, $dbh); - # Disabled until we can finish code from editor and update server code - pandora_planned_downtime (\%pa_config, $dbh); - # Ececute forced alerts - pandora_exec_forced_alerts (\%pa_config, $dbh); - keep_alive_check (\%pa_config, $dbh); + + # Update server status + foreach my $server (@Servers) { + exit (1) unless ($server->checkThreads () == 1); + $server->update (); + } + + eval { + pandora_planned_downtime (\%Config, $DBH); + pandora_exec_forced_alerts (\%Config, $DBH); + pandora_module_keep_alive_nd (\%Config, $DBH); + }; + threads->yield; - sleep ($pa_config{"server_threshold"}); -} - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 0, $dbh); - if ($pa_config{"verbosity"} > 0){ - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - } - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - exit; -} - -############################################################################### -# pandora_data_producer () -# Queue data files available for processing -############################################################################### -sub pandora_data_producer { - my $pa_config = $_[0]; - my $file_name; - my $file; - - # Main loop - while(1) { - - # Read all files in the incoming directory - opendir(DIR, $pa_config->{'incomingdir'} ) - || die "[FATAL] Cannot open Incoming data directory at " . - $pa_config->{'incomingdir'} . ": $!"; - - while (defined($file_name = readdir(DIR))){ - - # For backward compatibility - if ($file_name =~ /^.*\.checksum$/) { - unlink("$pa_config->{'incomingdir'}/$file_name"); - next; - } - - # Data files have the extension .data - if ($file_name !~ /^.*\.data$/) { - next; - } - - # Skip already queued/processed files - if (defined($pending_task_hash{$file_name}) || - defined($active_task_hash{$file_name})) { - next; - } - - # Queue data file - { - lock $queue_lock; - push (@pending_task, $file_name); - $pending_task_hash {$file_name} = 1; - if (! defined($incomplete_task_hash{$file_name})) { - $incomplete_task_hash{$file_name} = 0; - } - } - threads->yield; - } - - closedir(DIR); - threads->yield; - sleep $pa_config->{"server_threshold"}; - } -} - -############################################################################### -# pandora_data_consumer () -# Process data files -############################################################################### -sub pandora_data_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - my $file_name; - my $counter =0; - my $file; - - if ($pa_config->{"verbosity"} > 0){ - print " [*] Starting up Data Consumer Thread # $thread_id \n"; - } - - # Create database handler - my $dbh = DBI->connect("DBI:mysql:" . $pa_config->{'dbname'} . ":" . - $pa_config->{'dbhost'} . ":3306", - $pa_config->{'dbuser'},$pa_config->{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); - - LOOP: while (1) { - - if ($counter > 10) { - $counter = 0; - threads->yield; - sleep (1); - } - - # Check for pending data files - { - lock $queue_lock; - if (scalar(@pending_task) == 0) { - $counter++; - next LOOP; - } - - $file_name = shift(@pending_task); - delete($pending_task_hash{$file_name}); - $active_task_hash{$file_name} = 1; - } - - $file = "$pa_config->{'incomingdir'}/$file_name"; - - # Check file really exists to avoid race conditions - if (! -e "$file") { - $counter++; - delete($active_task_hash{$file_name}); - next LOOP; - } - - my $data; # Hash to store the XML data file - - # Parse the XML file - eval { - threads->yield; - $data = XMLin($file, forcearray=>'module'); - }; - - # Invalid MXL - if ($@) { - # Retry 3 times this XML - if ($incomplete_task_hash{$file_name} < 3) { - { - lock $queue_lock; - delete($active_task_hash{$file_name}); - $incomplete_task_hash{$file_name} += 1; - } - } - # Discard - else { - { - logger ($pa_config, "$file_name is a BAD XML. Removing", 3); - lock $queue_lock; - delete($active_task_hash{$file_name}); - delete($incomplete_task_hash{$file_name}); - rename($file, $file . "_BADXML"); - # Create event - pandora_event ($pa_config, "Unable to process XML data file ($file)", 0, 0, 0, 0, 0, 'error', $dbh); - } - } - - $counter = 0; - threads->yield; - next LOOP; - } - - process_datafile ($pa_config, $data, $dbh); - { - lock $queue_lock; - delete($active_task_hash{$file_name}); - delete($incomplete_task_hash{$file_name}); - unlink($file); - } - threads->yield; - $counter = 0; - } -} - -########################################################################## -## SUB keep_alive_check () -## Calculate a global keep alive check for agents without data and an alert defined -########################################################################## - -sub keep_alive_check { - my $pa_config = $_[0]; - my $dbh = $_[1]; - - my $query_idag = "SELECT tagente_modulo.id_agente_modulo, - tagente_estado.id_agente, - tagente.nombre AS agentname, - tagente_modulo.nombre AS modulename - FROM tagente_modulo, tagente_estado, tagente - WHERE tagente.id_agente = tagente_estado.id_agente - AND tagente.disabled = 0 - AND tagente_modulo.id_tipo_modulo = 100 - AND tagente_modulo.disabled = 0 - AND tagente_estado.datos = 1 - AND tagente_estado.estado = 0 - AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo - AND ( tagente_estado.utimestamp + (tagente.intervalo * 2) < UNIX_TIMESTAMP()) "; - - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - - my $id_agent_module; - my $id_agent; - my $agent_name; - my $module_name; - - # data needed in loop (we'll reuse it) - my $data; - if ($s_idag->rows != 0) { - while ($data = $s_idag->fetchrow_hashref()) { - threads->yield; - $id_agent_module = $data->{'id_agente_modulo'}; - $id_agent = $data->{'id_agente'}; - $agent_name = $data->{'agentname'}; - $module_name = $data->{'modulename'}; - pandora_writestate ($pa_config, $agent_name, "keep_alive", $module_name, 0, $dbh, 1); - } - } - undef $data; - $s_idag->finish(); -} - -########################################################################## -## SUB process_datafile (param_1, param_2, param_3) -## Process data packet (XML file) -########################################################################## -## param_1 : pandora_config hash -## param_2 : XML Hash structure reference -## param_3 - -sub process_datafile { - my $pa_config = $_[0]; - my $datos = $_[1]; - my $dbh = $_[2]; - my $tipo_modulo; - my $agent_name; - my $timestamp; - my $interval; - my $os_version; - my $os; - my $agent_version; - my $id_agente; - my $module_name; - - $agent_name = $datos->{'agent_name'}; - $timestamp = $datos->{'timestamp'}; - $agent_version = $datos->{'version'}; - $interval = $datos->{'interval'}; - $os_version = $datos->{'os_version'}; - - # Set default interval if not defined in agent (This is very very odd whatever!). - if (!defined($interval)){ - $interval = 300; - } - - # Check for parameteres, not all version agents gives the same parameters ! - if (length($interval) == 0){ - $interval = -1; # No update for interval ! - } - - if ((!defined ($os_version)) || (length($os_version) == 0)){ - $os_version = "N/A"; - } - - - if ((defined $agent_name) && ($agent_name ne "")){ - $id_agente = dame_agente_id($pa_config,$agent_name,$dbh); - - if ($id_agente == -1){ - if ($pa_config->{'autocreate'} == 1){ - $os = pandora_get_os ($datos->{'os'}); - $id_agente = pandora_create_agent ($pa_config, $dbh, "", 0, $pa_config->{'autocreate_group'}, 0, $datos->{'agent_name'}, 0, $os); - # Always create event about this detected IP - - } else { - logger($pa_config, "ERROR: There is no agent defined with name $agent_name", 3); - } - } - - if ($id_agente > 0) { - pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $os_version, $agent_version, $interval, $dbh); - update_keepalive_module ($pa_config, $id_agente, $agent_name, $dbh); - foreach my $part(@{$datos->{module}}) { - $tipo_modulo = $part->{type}->[0]; - $module_name = $part->{name}->[0]; - if (defined($module_name)){ # Skip modules without names - logger($pa_config, "Processing module Name ( $module_name ) type ( $tipo_modulo ) for agent ( $agent_name )", 5); - - # Data list - if (defined $part->{datalist}) { - foreach my $datalist (@{$part->{datalist}}) { - if (! defined $datalist->{data}) { - next; - } - - foreach my $data (@{$datalist->{data}}) { - if (! defined $data->{value}) { - next; - } - - $part->{data} = $data->{value}; - - # Data has its own timestamp - if (defined $data->{timestamp}) { - process_module_data($pa_config, $data->{timestamp}->[0], $agent_name, $tipo_modulo, $part, $dbh); - } - else { - process_module_data($pa_config, $timestamp, $agent_name, $tipo_modulo, $part, $dbh); - } - } - } - } - # Single data - else { - # Data has its own timestamp - if (defined $part->{timestamp}) { - $timestamp = $part->{timestamp}->[0]; - } - process_module_data($pa_config, $timestamp, $agent_name, $tipo_modulo, $part, $dbh); - } - } - } - } - } else { - logger($pa_config, "ERROR: Received data from an unknown agent", 2); - } -} - -########################################################################## -## SUB process_module_data () -## ($pa_config, $timestamp, $agent_name, $tipo_modulo, $module, $dbh) -## Process module data according to the module type. -########################################################################## - -sub process_module_data { - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $agent_name = $_[2]; - my $tipo_modulo = $_[3]; - my $module = $_[4]; - my $dbh = $_[5]; - - if (($tipo_modulo eq 'generic_data') || ($tipo_modulo eq 'async_data')) { - module_generic_data ($pa_config, $module, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif ($tipo_modulo eq 'generic_data_inc') { - module_generic_data_inc ($pa_config, $module, $timestamp, $agent_name,"generic_data_inc", $dbh); - } - elsif (($tipo_modulo eq 'generic_data_string') || ($tipo_modulo eq 'async_string')) { - module_generic_data_string ($pa_config, $module, $timestamp, $agent_name,$tipo_modulo, $dbh); - } - elsif (($tipo_modulo eq 'generic_proc') || ($tipo_modulo eq 'async_proc')) { - module_generic_proc ($pa_config, $module, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - else { - logger($pa_config, "ERROR: Received data from an unknown module ($tipo_modulo)", 2); - } -} - -########################################################################## -## SUB pandora_exec_forced_alerts ($pa_config, $dbh) -## Execute forced alerts. -########################################################################## - -sub pandora_exec_forced_alerts { - my $pa_config = $_[0]; - my $dbh = $_[1]; - - # Get alerts marked for forced execution (even disabled alerts) - my @alerts = get_db_all_rows ("SELECT talert_template_modules.id as id_template_module, talert_template_modules.*, talert_templates.*, tagente.* - FROM talert_template_modules, talert_templates, tagente, tagente_modulo - WHERE talert_template_modules.id_alert_template = talert_templates.id - AND talert_template_modules.id_agent_module = tagente_modulo.id_agente_modulo - AND tagente_modulo.id_agente = tagente.id_agente - AND force_execution = 1", $dbh); - - foreach my $alert (@alerts) { - execute_alert ($pa_config, $alert, $alert->{'id_agente'}, $alert->{'id_grupo'}, $alert->{'nombre'},'', 1, 0, $dbh); - - # Reset the force_execution flag, even if the alert could not be executed - db_do ("UPDATE talert_template_modules SET force_execution = 0 WHERE id = " . $alert->{'id_template_module'}, $dbh); - } + sleep ($Config{'server_threshold'}); } diff --git a/pandora_server/bin/pandora_snmpconsole b/pandora_server/bin/pandora_snmpconsole deleted file mode 100755 index 6fa508eeb4..0000000000 --- a/pandora_server/bin/pandora_snmpconsole +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/perl -########################################################################## -# Pandora Server. SNMP Console -########################################################################## -# Copyright (c) 2004-2008 Sancho Lerena, slerena@gmail.com -# Copyright (c) 2005-2008 Artica Soluciones Tecnologicas S.L -# -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use Time::Local; # DateTime basic manipulation -use Time::HiRes; # For high precission timedate functions (Net::Ping) - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; -enterprise_load (); - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config,"Pandora SNMP Console"); - -# Read config file for Global variables -pandora_loadconfig (\%pa_config, 2); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora Server SNMP Console Daemon starting", "SYSTEM", "System"); - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"quiet"} eq "0"){ - print " [*] Backgrounding Pandora FMS SNMP Console process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - -pandora_snmptrapd (\%pa_config); - -########################################################################## -## SUB pandora_snmptrapd -## Pandora SNMP Trap console/daemon subsystem -########################################################################## - -sub pandora_snmptrapd { - my $pa_config = $_[0]; - my $snmp_logfile = $pa_config->{'snmp_logfile'}; - my $logfile_size; # Size of logfile, use for calculating index file - my @array; - my $datos; - my $timestamp; - my $source; - my $oid; - my $type; - my $type_desc; - my $value; - my $custom_oid = ''; - my $custom_type = ''; - my $custom_value = ''; - my $sql_insert; - my @index_data; - - # open database, only ONCE. We pass reference to DBI handler ($dbh) to all subprocess - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306",$pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - - while ( ! -e $snmp_logfile) { # Wait until a snmplogfile exists - sleep ($pa_config{'server_threshold'}); - pandora_serverkeepaliver($pa_config,2,$dbh); - } - - open (SNMPLOGFILE, $snmp_logfile); - print " [*] SNMP Console enabled \n"; - $index_data[0]=0; - $index_data[1]=0; - # Check for index file - if ( -e $snmp_logfile.".index" ){ - open SNMPLOGFILE_INDEX, $snmp_logfile.".index"; - $datos = ; - close SNMPLOGFILE_INDEX; - @index_data = split(/\s+/,$datos); - # $index_data[0] is the last line readed - # $index_data[1] is the size of file (use for calculate new files or reset logfiles - } - $logfile_size = (stat($snmp_logfile))[7]; - - if ($logfile_size < $index_data[1]){ # Log size smaller last time we read it -> new one - unlink ($snmp_logfile.".index"); - $index_data[0]=0; - $index_data[1]=0; - logger ($pa_config,"New SNMP logfile detected, resetting index",1); - } - - if (($index_data[1] <= $logfile_size) && ($index_data[0] > 0)){ - # Skip already processed records - for ($value=0;$value < $index_data[0];$value++){ - $datos = readline SNMPLOGFILE; - } - } - - my $trap2agent = enterprise_hook('snmp_get_trap2agent', [$dbh]); - - # Main loop for reading file - while ( 1 ){ - while ($datos = ) { - $index_data[0]++; - $index_data[1]=(stat($snmp_logfile))[7]; - open SNMPLOGFILE_INDEX, ">".$snmp_logfile.".index"; - print SNMPLOGFILE_INDEX $index_data[0]," ",$index_data[1]; - close SNMPLOGFILE_INDEX; - - #print "DEBUG $datos \n"; - if (($datos !~ m/NET-SNMP/) && ($datos =~ m/\[\*\*\]/)) { # SKIP Headers - @array = split(/\[\*\*\]/, $datos); - $timestamp = $array[0]." ".$array[1]; - $source = $array[2]; - $oid = $array[3]; - $type = $array[4]; - $type_desc = $array[5]; - $value = limpia_cadena($array[6]); - if ($type == 6){ # Custom OID type - $datos = $array[7]; - if ($datos !~ m/STRING/) { # No string datatype, marked with " chars - $datos =~ m/([0-9\.]*)\s\=\s([A-Za-z0-9]*)\:\s(.+)/; - $custom_oid = $1; - $custom_type = $2; - $custom_value = limpia_cadena($3); - } else { # String type - if ($datos =~ m/([0-9\.]*)\s\=\s([A-Za-z0-9]*)\:\s\"(.+)\"/){ - $custom_oid = $1; - $custom_type = $2; - $custom_value = limpia_cadena($3); - } - } - } else { # not custom OID type, deleting old values in these vars - $custom_oid=""; - $custom_type=""; - $custom_value=$type_desc; # Bug fixed, 080108 by anonymous - } - - if (! defined(enterprise_hook ('snmp_insert_trap', [$pa_config, $source, $oid, $type, $value, $custom_oid, $custom_value, $custom_type, $timestamp, $dbh]))) { - $sql_insert = "INSERT INTO ttrap (timestamp, source, oid, type, value, oid_custom, value_custom, type_custom) VALUES ('$timestamp', '$source', '$oid', $type, '$value', '$custom_oid', '$custom_value', '$custom_type')"; - logger ($pa_config,"Received SNMP Trap from $source", 4); - eval { - $dbh->do($sql_insert) || logger ($pa_config, "Cannot write to database while updating SNMP Trap data (error in INSERT)",0); - # Evaluate TRAP Alerts for this trap - calcula_alerta_snmp ($pa_config, $source, $oid, $oid, $custom_value, $timestamp, $dbh); - }; - if ($@) { - logger ($pa_config, "[ERROR] Cannot access to database while updating SNMP Trap data",0); - logger ($pa_config, "[ERROR] SQL Errorcode: $@", 2); - } - } - enterprise_hook ('snmp_trap2agent', [defined($trap2agent) ? $trap2agent : 0, $pa_config, $source, $oid, $value, $custom_oid, $custom_value, $timestamp, $dbh]); - } - } - sleep ($pa_config{'server_threshold'}); - pandora_serverkeepaliver($pa_config,2,$dbh); - } - $dbh->disconnect(); -} - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config, "Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - my $dbh = DBI->connect("DBI:mysql:$pa_config{'dbname'}:$pa_config{'dbhost'}:3306",$pa_config{'dbuser'}, $pa_config{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 2, $dbh); - exit; -} diff --git a/pandora_server/bin/pandora_wmi b/pandora_server/bin/pandora_wmi deleted file mode 100755 index d00d55c118..0000000000 --- a/pandora_server/bin/pandora_wmi +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/perl -########################################################################## -# Pandora FMS WMI Server -# http://www.pandorafms.com -########################################################################## -# Copyright (c) 2008-2009 Artica Soluciones Tecnologicas S.L -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -########################################################################## - -# Includes list -use strict; -use warnings; - -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use Time::Local; # DateTime basic manipulation -use threads; -use threads::shared; -use HTML::Entities; - -# Pandora Modules -use PandoraFMS::Config; -use PandoraFMS::Tools; -use PandoraFMS::DB; -# use Win32::OLE qw(in); - -# Queue management -my @pending_task : shared; -my %pending_task_hash : shared; -my %current_task_hash : shared; -my $queue_lock : shared; - -# FLUSH in each IO (only for debug, very slooow) -# ENABLED in DEBUGMODE -# DISABLE FOR PRODUCTION -$| = 0; - -my %pa_config; - -$SIG{'TERM'} = 'pandora_shutdown'; -$SIG{'INT'} = 'pandora_shutdown'; - -# Inicio del bucle principal de programa -pandora_init(\%pa_config, "Pandora FMS WMI Server"); - -# Check for wmic -my $wmi_client = "wmic"; -if (system("$wmi_client > /dev/null 2>&1") != 256) { - print " [E] $wmi_client not found. Pandora FMS WMI Server needs a DCOM/WMI client.\n\n"; - exit; -} - -# Read config file for Global variables -pandora_loadconfig (\%pa_config, 6); - -# Audit server starting -pandora_audit (\%pa_config, "Pandora FMS WMI server starting", "SYSTEM", "System"); - -# Daemonize and put in background -if ( $pa_config{"daemon"} eq "1" ){ - if ($pa_config{"quiet"} eq "0"){ - print " [*] Backgrounding Pandora FMS WMI Server process.\n\n"; - } - &pandora_daemonize ( \%pa_config); -} - -# Launch now all WMI server threads -for (my $i=0; $i < $pa_config{'wmi_threads'}; $i++){ - threads->new( \&pandora_wmi_consumer, \%pa_config, $i); -} - -# Launch now the producer thread -threads->new( \&pandora_wmi_producer, \%pa_config); - -# Last thread is the main process (this process) -if ($pa_config{"quiet"} == 0){ - print " [*] All threads loaded and running \n\n"; -} - -# Start Pandora FMS loggin -pandora_startlog (\%pa_config); - -my $dbhost = $pa_config{'dbhost'}; -my $dbname = $pa_config{'dbname'}; -my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", - $pa_config{'dbuser'}, - $pa_config{'dbpass'}, - { RaiseError => 1, AutoCommit => 1 }); - -# Server keepalive thread running in main thread on a infinite loop -while (1) { - pandora_serverkeepaliver (\%pa_config, 6, $dbh); - threads->yield; - sleep ($pa_config{"server_threshold"}); -} - - -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#--------------------- Main Perl Code below this line----------------------- -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ -#------------------------------------------------------------------------------------ - -######################################################################################## -# pandora_shutdown () -# Close system -######################################################################################## -sub pandora_shutdown { - logger (\%pa_config,"Pandora FMS Server '".$pa_config{'servername'}.$pa_config{"servermode"}."' Shutdown by signal ",0); - print " [*] Shutting down ".$pa_config{'servername'}.$pa_config{"servermode"} ."(received signal)...\n"; - pandora_event (\%pa_config, $pa_config{'servername'}.$pa_config{"servermode"}." going Down", 0, - 0, 4, 0, 0, "system", $dbh); - pandora_updateserver (\%pa_config, $pa_config{'servername'}, 0, 6, $dbh); - exit; -} - -########################################################################## -# SUB pandora_wmi_consumer subsystem -# Subsystem to process WMI modules -# This module runs each X seconds (server threshold) checking for WMI modules status -########################################################################## -sub pandora_wmi_consumer ($$) { - my $pa_config = $_[0]; - my $thread_id = $_[1]; - - if ($pa_config->{"quiet"} == 0){ - print " [*] Starting up WMI Consumer Thread # $thread_id \n"; - } - - my $data_id_agent_module; - # Create Database handler - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $counter =0; - - LOOP: while (1) { - if ($counter > 10) { - threads->yield; - sleep (1); - $counter = 0; - } - - # Take the first element on the shared queue - # Insert this element on the current task hash - { - lock $queue_lock; - if (scalar(@pending_task) == 0){ - $counter++; - next LOOP; - } - - $data_id_agent_module = shift(@pending_task); - delete($pending_task_hash{$data_id_agent_module}); - $current_task_hash{$data_id_agent_module}=1; - } - eval { - exec_wmi_module ($pa_config, $data_id_agent_module, $dbh); - }; - if ($@){ - logger ($pa_config, "[ERROR] WMI Task for module $data_id_agent_module causes a system exception", 0); - logger ($pa_config, "ERROR Code: $@", 1); - } - - # Remove from queue. If catch an error, probably data is - # not been processed, but has been freed from task queue - { - lock $queue_lock; - delete ($current_task_hash {$data_id_agent_module} ); - } - $counter = 0; - threads->yield; - } -} - -sub pandora_wmi_producer ($) { - my $pa_config = $_[0]; - print " [*] Starting up WMI Producer Thread ...\n"; - - my $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - my $server_id = $pa_config->{'server_id'}; - - # Initialize variables for posterior usage - my $query1; - my @sql_data1; - my $data_id_agente_modulo; - my $data_flag; - my $exec_sql1; - - while (1) { - if ($pa_config->{"pandora_master"} != 1) { - # Query for normal server, not MASTER server - $query1 = "SELECT - tagente_modulo.id_agente_modulo, - tagente_modulo.flag - UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado - WHERE - id_wmi_server = $server_id - AND - tagente_modulo.id_agente = tagente.id_agente - AND - tagente.disabled = 0 - AND - tagente_modulo.id_modulo = 6 - AND - tagente_modulo.disabled = 0 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND ( - (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() - OR - tagente_modulo.flag = 1 - ) - ORDER BY - tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC "; - } else { - # Query for MASTER SERVER ! - $query1 = "SELECT - DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left - FROM - tagente, tagente_modulo, tagente_estado, tserver - WHERE - ( (tagente.id_wmi_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR - (tagente.id_wmi_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_wmi_server = tserver.id_server AND tserver.status = 0) - ) AND - tagente.disabled = 0 - AND - tagente_modulo.disabled = 0 - AND - tagente_modulo.id_modulo = 6 - AND - tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo - AND - ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() OR tagente_modulo.flag = 1 ) - ORDER BY - tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC"; - } - $exec_sql1 = $dbh->prepare($query1); - $exec_sql1 ->execute; - while (@sql_data1 = $exec_sql1->fetchrow_array()) { - $data_id_agente_modulo = $sql_data1[0]; - $data_flag = $sql_data1[1]; - - # Skip modules already queued - if ((!defined($pending_task_hash{$data_id_agente_modulo})) && - (!defined($current_task_hash{$data_id_agente_modulo}))) { - if ($data_flag == 1){ - $dbh->do("UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = $data_id_agente_modulo") - } - # Locking scope, do not remove redundant { } - { - lock $queue_lock; - push (@pending_task, $data_id_agente_modulo); - $pending_task_hash {$data_id_agente_modulo}=1; - } - } - } - $exec_sql1->finish(); - threads->yield; - sleep($pa_config->{"server_threshold"}); - } # Main loop -} - -########################################################################## -# SUB exec_wmi_module (paconfig, id_agente_modulo, dbh ) -# Execute WMI module task -########################################################################## -sub exec_wmi_module { - my $pa_config = $_[0]; - my $id_am = $_[1]; - my $dbh = $_[2]; - - # Set global variables for this sub - my $timeout = $pa_config->{'wmi_timeout'}; - my $agent_module; # hash container for tagente_modulo record - - # Get a full hash for agent_plugin record reference ($agent_module) - my $query_sql = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_am"; - my $exec_sql = $dbh->prepare($query_sql); - $exec_sql ->execute; - $agent_module = $exec_sql->fetchrow_hashref; - - # Calculate min timeout for this call - if ($agent_module->{'max_timeout'} < $timeout){ - $timeout = $agent_module->{'max_timeout'}; - } - - # Initialize another global sub variables. - my $agent_name = dame_agente_nombre ($pa_config, $agent_module->{'id_agente'}, $dbh); - my $module_interval = $agent_module->{'module_interval'}; - - # Build execution command to plugin - - my $wmi_query = decode_entities($agent_module->{'snmp_oid'}); - $wmi_query =~ s/\"/\'/g; - - my $wmi_command = "$wmi_client -U \"". $agent_module->{'plugin_user'} ."\"%\"". $agent_module->{'plugin_pass'} . "\""; - - # Custom namespace - if ($agent_module->{'tcp_send'} ne '') { - $agent_module->{'tcp_send'} =~ s/\"/\'/g; - $wmi_command .= " --namespace=\"" . $agent_module->{'tcp_send'} . "\""; - } - - $wmi_command .= " //". $agent_module->{'ip_target'}; - $wmi_command .= " \"". $wmi_query . "\""; - logger ($pa_config, "Executing AM # $id_am WMI command '$wmi_command'", 9); - - my $module_data = `$wmi_command`; - if (! defined($module_data)) { - update_on_error ($pa_config, $id_am, $dbh); - undef $agent_module; - return; - } - - my @data = split("\n", $module_data); - if ($#data < 2) { - update_on_error ($pa_config, $id_am, $dbh); - undef $agent_module; - return; - } - - my @data2 = split(/\|/, $data[2]); # get third line (containing data) - - # Take a specific field number from returned data. It uses tcp_port - # field from tagente_module table. - - $module_data = $data2[$agent_module -> {'tcp_port'}]; - - # Look for errors - if ($module_data =~ /ERROR/) { - update_on_error ($pa_config, $id_am, $dbh); - undef $agent_module; - return 0; - } - - # Special word to know is something is OK or not, - # for example "Running". Defined by user in console. It uses snmp_community - # field from tagente_modulo. - - if ($agent_module -> {'snmp_community'} ne ""){ - my $temp_filter1 = $agent_module -> {'snmp_community'}; - if ($module_data =~ /$temp_filter1/){ - $module_data = 1; - } else { - $module_data = 0; - } - } - - # Get current timestamp - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp = &UnixDate("today","%s"); - - # If module execution get a valid value - my %part; - $part{'name'}[0] = $agent_module->{'nombre'}; - $part{'description'}[0] = ""; - $part{'data'}[0] = $module_data; - my $tipo_modulo = dame_nombretipomodulo_idagentemodulo ($pa_config, $agent_module->{'id_tipo_modulo'}, $dbh); - - # 1 - generic_data - # 2 - generic_proc - # 3 - generic_data_string - # 4 - generic_data_inc - - if (1 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif (4 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data_inc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - elsif (3 == $agent_module->{'id_tipo_modulo'}) { - module_generic_data_string ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - # Generic_proc - elsif (2 == $agent_module->{'id_tipo_modulo'}) { - module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); - } - else { # Unknown module!, this IS a problem - logger ($pa_config, "WMI Server Problem with unknown module type '$tipo_modulo'", 0); - update_on_error ($pa_config, $id_am, $dbh); - undef $agent_module; - undef %part; - return 0; - } - # Update agent last contact - # Insert Pandora version as agent version - pandora_lastagentcontact ($pa_config, $timestamp, $agent_name, $pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'version'}, -1, $dbh); - - $exec_sql->finish(); #close tagent_plugin hash reference - undef %part; - undef $agent_module; -} diff --git a/pandora_server/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index b9ee24a54c..4232304227 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -19,10 +19,11 @@ package PandoraFMS::Config; ########################################################################## use warnings; +use POSIX qw(strftime); use Time::Local; -use Date::Manip; use PandoraFMS::Tools; use PandoraFMS::DB; +use PandoraFMS::Core; require Exporter; our @ISA = ("Exporter"); @@ -31,15 +32,13 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( pandora_help_screen pandora_init - pandora_loadconfig - pandora_startlog + pandora_load_config + pandora_start_log ); -# There is no global vars, all variables (setup) passed as hash reference - # version: Defines actual version of Pandora Server for this module only my $pandora_version = "3.0-dev"; -my $pandora_build="PS090305"; +my $pandora_build = "PS090310"; our $VERSION = $pandora_version." ".$pandora_build; # Setup hash @@ -125,11 +124,8 @@ sub pandora_init { # Read external configuration file ########################################################################## -sub pandora_loadconfig { +sub pandora_load_config { my $pa_config = $_[0]; - my $opmode = $_[1]; # 0 dataserver, 1 network server, 2 snmp console - # 3 recon srv, 4 plugin srv, 5 prediction srv - # 6 WMI server, 7 export server my $archivo_cfg = $pa_config->{'pandora_path'}; my $buffer_line; my @command_line; @@ -162,6 +158,7 @@ sub pandora_loadconfig { $pa_config->{"predictionserver"} = 1; # default $pa_config->{"exportserver"} = 1; # default $pa_config->{"inventoryserver"} = 1; # default + $pa_config->{"webserver"} = 1; # 3.0 $pa_config->{"servermode"} = ""; $pa_config->{'snmp_logfile'} = "/var/log/pandora_snmptrap.log"; $pa_config->{"network_threads"} = 3; # Fixed default @@ -171,18 +168,23 @@ sub pandora_loadconfig { $pa_config->{"alert_recovery"} = 0; # Introduced on 1.3.1 $pa_config->{"snmp_checks"} = 1; # Introduced on 1.3.1 $pa_config->{"snmp_timeout"} = 8; # Introduced on 1.3.1 + $pa_config->{"snmp_trapd"} = '/usr/sbin/snmptrapd'; # 3.0 $pa_config->{"tcp_checks"} = 1; # Introduced on 1.3.1 $pa_config->{"tcp_timeout"} = 20; # Introduced on 1.3.1 $pa_config->{"snmp_proc_deadresponse"} = 1; # Introduced on 1.3.1 10 Feb08 $pa_config->{"plugin_threads"} = 2; # Introduced on 2.0 + $pa_config->{"plugin_exec"} = 'pandora_exec'; # 3.0 $pa_config->{"recon_threads"} = 2; # Introduced on 2.0 $pa_config->{"prediction_threads"} = 1; # Introduced on 2.0 $pa_config->{"plugin_timeout"} = 5; # Introduced on 2.0 $pa_config->{"wmi_threads"} = 2; # Introduced on 2.0 $pa_config->{"wmi_timeout"} = 5; # Introduced on 2.0 + $pa_config->{"wmi_client"} = 'wmic'; # 3.0 $pa_config->{"compound_max_depth"} = 5; # Maximum nested compound alert depth. Not in config file. $pa_config->{"dataserver_threads"} = 2; # Introduced on 2.0 $pa_config->{"inventory_threads"} = 2; # 2.1 + $pa_config->{"export_threads"} = 1; # 3.0 + $pa_config->{"web_threads"} = 1; # 3.0 # Internal MTA for alerts, each server need its own config. $pa_config->{"mta_address"} = '127.0.0.1'; # Introduced on 2.0 @@ -345,6 +347,12 @@ sub pandora_loadconfig { elsif ($parametro =~ m/^exportserver\s([0-9]*)/i) { $pa_config->{'exportserver'}= clean_blank($1); } + elsif ($parametro =~ m/^inventoryserver\s([0-9]*)/i) { + $pa_config->{'inventoryserver'}= clean_blank($1); + } + elsif ($parametro =~ m/^webserver\s([0-9]*)/i) { + $pa_config->{'webserver'}= clean_blank($1); + } elsif ($parametro =~ m/^servername\s(.*)/i) { $pa_config->{'servername'}= clean_blank($1); } @@ -439,6 +447,27 @@ sub pandora_loadconfig { elsif ($parametro =~ m/^mcast_change_port\s([0-9]*)/i) { $pa_config->{'mcast_change_port'}= clean_blank($1); } + elsif ($parametro =~ m/^wmi_threads\s([0-9]*)/i) { + $pa_config->{'wmi_threads'}= clean_blank($1); + } + elsif ($parametro =~ m/^wmi_client\s(.*)/i) { + $pa_config->{'wmi_client'}= clean_blank($1); + } + elsif ($parametro =~ m/^web_threads\s([0-9]*)/i) { + $pa_config->{'web_threads'}= clean_blank($1); + } + elsif ($parametro =~ m/^snmp_trapd\s(.*)/i) { + $pa_config->{'snmp_trapd'}= clean_blank($1); + } + elsif ($parametro =~ m/^plugin_exec\s(.*)/i) { + $pa_config->{'plugin_exec'}= clean_blank($1); + } + elsif ($parametro =~ m/^inventory_threads\s([0-9]*)/i) { + $pa_config->{'inventory_threads'}= clean_blank($1); + } + elsif ($parametro =~ m/^export_threads\s([0-9]*)/i) { + $pa_config->{'export_threads'}= clean_blank($1); + } } # end of loop for parameter # @@ -458,103 +487,7 @@ sub pandora_loadconfig { print " [ERROR] Bad Config values. Be sure that $archivo_cfg is a valid setup file. \n\n"; exit; } - if (($opmode ==0) && ($pa_config->{"dataserver"} ne 1)) { - print " [ERROR] You must enable 'dataserver' in setup file to run Pandora FMS Data Server. \n\n"; - exit; - } - if (($opmode ==1) && ($pa_config->{"networkserver"} ne 1)) { - print " [ERROR] You must enable 'networkserver' in setup file to run Pandora FMS Network Server. \n\n"; - exit; - } - if (($opmode ==2) && ($pa_config->{"snmpconsole"} ne 1)) { - print " [ERROR] You must enable 'snmpconsole' in setup file to run Pandora FMS SNMP Console. \n\n"; - exit; - } - if (($opmode ==3) && ($pa_config->{"reconserver"} ne 1)) { - print " [ERROR] You must enable 'reconserver' in setup file to run Pandora FMS Recon server. \n\n"; - exit; - } - if (($opmode ==4) && ($pa_config->{"pluginserver"} ne 1)) { - print " [ERROR] You must enable 'pluginserver' in setup file to run Pandora FMS Plugin server. \n\n"; - exit; - } - if (($opmode ==5) && ($pa_config->{"predictionserver"} ne 1)) { - print " [ERROR] You must enable 'predictionserver' in setup file to run Pandora FMS Prediction server. \n\n"; - exit; - } - if (($opmode ==6) && ($pa_config->{"wmiserver"} ne 1)) { - print " [ERROR] You must enable 'wmiserver' in setup file to run Pandora FMS WMI server. \n\n"; - exit; - } - if (($opmode ==7) && ($pa_config->{"exportserver"} ne 1)) { - print " [ERROR] You must enable 'exportserver' in setup file to run Pandora FMS Export server. \n\n"; - exit; - } - # Show some config options in startup - if ($opmode == 0){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Data Server. \n"; - } - $parametro ="Pandora FMS Data Server"; - $pa_config->{"servermode"}="_Data"; - } - if ($opmode == 1){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Network Server. \n"; - } - $parametro ="Pandora FMS Network Server"; - $pa_config->{"servermode"}="_Net"; - } - if ($opmode == 2){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS SNMP Console. \n"; - } - $parametro ="Pandora FMS SNMP Console"; - $pa_config->{"servermode"}="_SNMP"; - } - if ($opmode == 3){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Recon Server. \n"; - } - $parametro ="Pandora FMS Recon Server"; - $pa_config->{"servermode"}="_Recon"; - } - if ($opmode == 4){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Plugin Server. \n"; - } - $parametro ="Pandora FMS Plugin Server"; - $pa_config->{"servermode"}="_Plugin"; - } - if ($opmode == 5){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Prediction Server. \n"; - } - $parametro ="Pandora FMS Prediction Server"; - $pa_config->{"servermode"}="_Prediction"; - } - if ($opmode == 6){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS WMI Server. \n"; - } - $parametro ="Pandora FMS WMI Server"; - $pa_config->{"servermode"}="_WMI"; - } - if ($opmode == 7){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Export Server. \n"; - } - $parametro ="Pandora FMS Export Server"; - $pa_config->{"servermode"}="_Export"; - } - if ($opmode == 8){ - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] You are running Pandora FMS Inventory Server. \n"; - } - $parametro ="Pandora FMS Inventory Server"; - $pa_config->{"servermode"}="_Inventory"; - } if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { if ($pa_config->{"pandora_check"} == 1) { print " [*] MD5 Security enabled.\n"; @@ -567,34 +500,17 @@ sub pandora_loadconfig { logger ($pa_config, "Launching $pa_config->{'version'} $pa_config->{'build'}", 0); my $config_options = "Logfile at ".$pa_config->{"logfile"}.", Basepath is ".$pa_config->{"basepath"}.", Checksum is ".$pa_config->{"pandora_check"}.", Master is ".$pa_config->{"pandora_master"}.", SNMP Console is ".$pa_config->{"snmpconsole"}.", Server Threshold at ".$pa_config->{"server_threshold"}." sec, verbosity at ".$pa_config->{"verbosity"}.", Alert Threshold at $pa_config->{'alert_threshold'}, ServerName is '".$pa_config->{'servername'}.$pa_config->{"servermode"}."'"; logger ($pa_config, "Config options: $config_options", 1); - my $dbh; - # Check valid Database variables and update server status - eval { - $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - pandora_updateserver ($pa_config, $pa_config->{'servername'}, 1, $opmode, $dbh); # Alive status - }; - if ($@) { - logger ($pa_config, "Error connecting database in init Phase. Aborting startup.",0); - print (" [ERROR] Error connecting database in init Phase. Aborting startup. \n\n"); - print $@; - exit; - } - if (($pa_config->{"quiet"} == 0) && ($pa_config->{"verbosity"} > 4)) { - print " [*] Pandora FMS Server [".$pa_config->{'servername'}.$pa_config->{"servermode"}."] is running and operative \n"; - } - $pa_config->{'server_id'} = dame_server_id ($pa_config, $pa_config->{'servername'}.$pa_config->{"servermode"}, $dbh); - pandora_event ($pa_config, $pa_config->{'servername'}.$pa_config->{"servermode"}." going UP", 0, 0, 3, 0, 0, "system", $dbh); } -sub pandora_startlog ($){ - my $pa_config = $_[0]; +sub pandora_start_log ($){ + my $pa_config = shift; # Dump all errors to errorlog - open STDERR, ">>$pa_config->{'errorlogfile'}" or die " [ERROR] Pandora FMS can't write to Errorlog. Aborting : \n $! \n"; - my $time_now = &UnixDate("today","%Y/%m/%d %H:%M:%S"); - print STDERR "$time_now - ".$pa_config->{'servername'}.$pa_config->{"servermode"}." Starting Pandora FMS Server. Error logging activated \n"; + open (STDERR, ">> " . $pa_config->{'errorlogfile'}) or die " [ERROR] Pandora FMS can't write to Errorlog. Aborting : \n $! \n"; + print STDERR strftime ("%Y-%m-%d %H:%M:%S", localtime()) . ' - ' . $pa_config->{'servername'} . $pa_config->{'servermode'} . " Starting Pandora FMS Server. Error logging activated \n"; } + # End of function declaration # End of defined Code diff --git a/pandora_server/lib/PandoraFMS/Core.pm b/pandora_server/lib/PandoraFMS/Core.pm new file mode 100644 index 0000000000..21d3aa89c6 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/Core.pm @@ -0,0 +1,1107 @@ +package PandoraFMS::Core; +########################################################################## +# Core Pandora FMS functions. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; +use DBI; +use XML::Simple; +use HTML::Entities; + +use POSIX qw(strftime); + +use PandoraFMS::DB; +use PandoraFMS::Config; +use PandoraFMS::Tools; + +require Exporter; + +our @ISA = ("Exporter"); +our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT = qw( + pandora_audit + pandora_create_agent + pandora_create_incident + pandora_create_module + pandora_evaluate_alert + pandora_evaluate_compound_alert + pandora_event + pandora_event_status + pandora_execute_alert + pandora_execute_action + pandora_exec_forced_alerts + pandora_generate_alerts + pandora_generate_compound_alerts + pandora_module_keep_alive + pandora_module_keep_alive_nd + pandora_planned_downtime + pandora_process_alert + pandora_process_module + pandora_server_keep_alive + pandora_update_agent + pandora_update_module_on_error + pandora_update_server + pandora_evaluate_snmp_alerts + + @ServerTypes + @ServerSuffixes + @ServerColumns + ); + +# Some global variables +our @DayNames = qw(monday tuesday wednesday thursday friday saturday sunday); +our @ServerTypes = qw (dataserver networkserver snmpconsole reconserver pluginserver predictionserver wmiserver exportserver inventoryserver webserver); +our @ServerSuffixes = qw (_Data _Net _SNMP _Recon _Plugin _Prediction _WMI _Export _Inventory _Web); +our @ServerColumns = qw (data_server network_server snmp_server recon_server plugin_server prediction_server wmi_server export_server inventory_server web_server); + +########################################################################## +#Generate alerts for a given module. +########################################################################## +sub pandora_generate_alerts ($$$$$$$) { + my ($pa_config, $data, $status, $agent, $module, $utimestamp, $dbh) = @_; + + # Do not generate alerts for disabled groups + if (is_group_disabled ($dbh, $agent->{'id_grupo'})) { + return; + } + + # Get enabled alerts associated with this module + my @alerts = get_db_rows ($dbh, 'SELECT talert_template_modules.id as id_template_module, + talert_template_modules.*, talert_templates.* + FROM talert_template_modules, talert_templates + WHERE talert_template_modules.id_alert_template = talert_templates.id + AND id_agent_module = ? AND disabled = 0', $module->{'id_agente_modulo'}); + + foreach my $alert (@alerts) { + my $rc = pandora_evaluate_alert($pa_config, $data, $status, $alert, + $utimestamp, $dbh); + + pandora_process_alert ($pa_config, $data, $agent, $module, + $alert, $rc, $dbh); + + # Evaluate compound alerts even if the alert status did not change in + # case the compound alert does not recover + pandora_generate_compound_alerts ($pa_config, $data, $agent, $module, + $alert, $utimestamp, $dbh) + } +} + +########################################################################## +#Evaluate trigger conditions for a given alert. Returns: +# 0 Execute the alert. +# 1 Do not execute the alert. +# 2 Do not execute the alert, but increment its internal counter. +# 3 Cease the alert. +# 4 Recover the alert. +# 5 Reset internal counter (alert not fired, interval elapsed). +########################################################################## +sub pandora_evaluate_alert ($$$$$$) { + my ($pa_config, $data, $last_status, $alert, $utimestamp, $dbh) = @_; + + # Value returned on valid data + my $status = 1; + + # Get current time + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time()); + + # Check weekday + return 1 if ($alert->{$DayNames[$wday]} != 1); + + # Check time slot + my $time = sprintf ("%.2d:%.2d:%.2d", $hour, $min, $sec); + return 1 if (($alert->{'time_to'} ne $alert->{'time_from'}) && + (($time ge $alert->{'time_to'}) || ($time le $alert->{'time_from'}))); + + # Check time threshold + my $limit_utimestamp = $alert->{'last_reference'} + $alert->{'time_threshold'}; + + if ($alert->{'times_fired'} > 0) { + + # Reset fired alerts + if ($utimestamp > $limit_utimestamp) { + + # Cease on valid data + $status = 3; + + # Always reset + ($alert->{'internal_counter'}, $alert->{'times_fired'}) = (0, 0); + } + + # Recover takes precedence over cease + $status = 4 if ($alert->{'recovery_notify'} == 1); + + } elsif ($utimestamp > $limit_utimestamp) { + $status = 5; + } + + # Check for valid data + # Simple alert + if (defined ($alert->{'id_template_module'})) { + return $status if ($alert->{'type'} eq "min" && $data >= $alert->{'min_value'}); + return $status if ($alert->{'type'} eq "max" && $data <= $alert->{'max_value'}); + + if ($alert->{'type'} eq "max_min") { + return $status if ($alert->{'matches_value'} == 1 && + $data <= $alert->{'min_value'} && + $data >= $alert->{'max_value'}); + + return $status if ($data >= $alert->{'min_value'} && + $data <= $alert->{'max_value'}); + } + + return $status if ($alert->{'type'} eq "equal" && $data != $alert->{'value'}); + return $status if ($alert->{'type'} eq "not_equal" && $data == $alert->{'value'}); + + if ($alert->{'type'} eq "regex") { + return $status if ($alert->{'value'} == 1 && $data =~ m/$alert->{'value'}/i); + + return $status if ($data !~ m/$alert->{'value'}/i); + } + + return $status if ($last_status == 1 && $alert->{'type'} eq 'critical'); + return $status if ($last_status == 2 && $alert->{'type'} eq 'warning'); + } + # Compound alert + elsif (pandora_evaluate_compound_alert($pa_config, $alert->{'id'}, $dbh) == 0) { + return $status + } + + # Check min and max alert limits + return 2 if (($alert->{'internal_counter'} < $alert->{'min_alerts'}) || + ($alert->{'times_fired'} >= $alert->{'max_alerts'})); + + return 0; +} + +########################################################################## +#Process an alert given the status returned by pandora_evaluate_alert. +########################################################################## +sub pandora_process_alert ($$$$$$$) { + my ($pa_config, $data, $agent, $module, $alert, $rc, $dbh) = @_; + + # Do not execute + return if ($rc == 1); + + # Cease + if ($rc == 3) { + + # Update alert status + db_do($dbh, 'UPDATE talert_template_modules SET times_fired = 0, + internal_counter = 0 WHERE id = ?', $alert->{'id_template_module'}); + + # Generate an event + pandora_event ($pa_config, "Alert ceased (" . + $alert->{'descripcion'} . ")", $agent->{'id_grupo'}, + $agent->{'id_agente'}, $alert->{'priority'}, $alert->{'id_template_module'}, $alert->{'id_agent_module'}, + "alert_recovered", $dbh); + + return; + } + + # Recover + if ($rc == 4) { + + # Update alert status + db_do($dbh, 'UPDATE talert_template_modules SET times_fired = 0, + internal_counter = 0 WHERE id = ?', $alert->{'id_template_module'}); + + pandora_execute_alert ($pa_config, $data, $agent, $module, $alert, 0, $dbh); + return; + } + + # Reset internal counter + if ($rc == 5) { + db_do($dbh, 'UPDATE talert_template_modules SET internal_counter = 0 WHERE id = ?', + $alert->{'id_template_module'}); + return; + } + + # Get current date + my $utimestamp = time (); + + # Do we have to start a new interval? + my $new_interval = ($alert->{'internal_counter'} == 0) ? + ', last_reference = ' . $utimestamp : ''; + + # Increment internal counter + if ($rc == 2) { + + # Update alert status + $alert->{'internal_counter'} += 1; + + # Do not increment times_fired, but set it in case the alert was reset + db_do($dbh, 'UPDATE talert_template_modules SET times_fired = ?, + internal_counter = ? ' . $new_interval . ' WHERE id = ?', + $alert->{'times_fired'}, $alert->{'internal_counter'}, $alert->{'id_template_module'}); + + return; + } + + # Execute + if ($rc == 0) { + + # Update alert status + $alert->{'times_fired'} += 1; + $alert->{'internal_counter'} += 1; + + db_do($dbh, 'UPDATE talert_template_modules SET times_fired = ?, + last_fired = ?, internal_counter = ? ' . $new_interval . ' WHERE id = ?', + $alert->{'times_fired'}, $utimestamp, $alert->{'internal_counter'}, $alert->{'id_template_module'}); + + pandora_execute_alert ($pa_config, $data, $agent, $module, $alert, 1, $dbh); + return; + } +} + +########################################################################## +#Evaluate the given compound alert. Returns 1 if the alert should be +#fired, 0 if not. +########################################################################## +sub pandora_evaluate_compound_alert ($$$) { + my ($pa_config, $id, $dbh) = @_; + + # Return value + my $status = 0; + + # Get all the alerts associated with this compound alert + my @compound_alerts = get_db_rows ($dbh, 'SELECT id_alert_template_module, operation FROM talert_compound_elements + WHERE id_alert_compound = ? ORDER BY order', $id); + + foreach my $compound_alert (@compound_alerts) { + + # Get alert data if enabled + my $times_fired = get_db_value ($dbh, "SELECT times_fired FROM talert_template_modules WHERE id = ? + AND disabled = 0", $compound_alert->{'id_alert_template_module'}); + next unless defined ($times_fired); + + # Check whether the alert was fired + my $fired = ($times_fired > 0) ? 1 : 0; + my $operation = $compound_alert->{'operation'}; + + # Operate... + if ($operation eq "AND") { + $status &= $fired; + } + elsif ($operation eq "OR") { + $status |= $fired; + } + elsif ($operation eq "XOR") { + $status ^= $fired; + } + elsif ($operation eq "NAND") { + $status &= ! $fired; + } + elsif ($operation eq "NOR") { + $status |= ! $fired; + } + elsif ($operation eq "NXOR") { + $status ^= ! $fired; + } + elsif ($operation eq "NOP") { + $status = $fired; + } + } + + return $status; +} + +########################################################################## +#Generate compound alerts that depend on a given alert. +########################################################################## +sub pandora_generate_compound_alerts ($$$$$$$) { + my ($pa_config, $data, $agent, $module, $alert, $utimestamp, $dbh) = @_; + + # Get all compound alerts that depend on this alert + my @elements = get_db_rows ($dbh, 'SELECT id_alert_compound FROM talert_compound_elements + WHERE id_alert_template_module = ?', + $alert->{'id_template_module'}); + + foreach my $element (@elements) { + + # Get compound alert parameters + my $compound_alert = get_db_single_row ($dbh, 'SELECT * FROM talert_compound WHERE id = ?', $element->{'id_alert_compound'}); + next unless defined ($compound_alert); + + # Evaluate the alert + my $rc = pandora_evaluate_alert ($pa_config, $data, '', $alert, + $utimestamp, $dbh); + + pandora_process_alert ($pa_config, $data, $agent, $module, + $compound_alert, $rc, $dbh); + } +} + +########################################################################## +# Execute the given alert. +########################################################################## +sub pandora_execute_alert ($$$$$$$) { + my ($pa_config, $data, $agent, $module, + $alert, $alert_mode, $dbh) = @_; + + # Get active actions/commands + my @actions; + + # Simple alert + if (defined ($alert->{'id_template_module'})) { + @actions = get_db_rows ($dbh, 'SELECT * FROM talert_template_module_actions, talert_actions, talert_commands + WHERE talert_template_module_actions.id_alert_action = talert_actions.id + AND talert_actions.id_alert_command = talert_commands.id + AND talert_template_module_actions.id_alert_template_module = ? + AND ((fires_min = 0 AND fires_max = 0) + OR (? >= fires_min AND ? <= fires_max))', + $alert->{'id_template_module'}, $alert->{'times_fired'}, $alert->{'times_fired'}); + + # Get default action + if ($#actions < 0) { + @actions = get_db_rows ($dbh, 'SELECT * FROM talert_actions, talert_commands + WHERE talert_actions.id = ? + AND talert_actions.id_alert_command = talert_commands.id', + $alert->{'id_alert_action'}, ); + } + } + # Compound alert + else { + @actions = get_db_rows ($dbh, 'SELECT * FROM talert_compound_actions, talert_actions, talert_commands + WHERE talert_compound_actions.id_alert_action = talert_actions.id + AND talert_actions.id_alert_command = talert_commands.id + AND talert_compound_actions.id_alert_compound = ? + AND ((fires_min = 0 AND fires_max = 0) + OR (? >= fires_min AND ? <= fires_max))', $alert->{'id'}, $alert->{'times_fired'}, $alert->{'times_fired'}); + } + + # No actions defined + return if ($#actions < 0); + + # Execute actions + foreach my $action (@actions) { + logger($pa_config, "Alert (" . $alert->{'name'} . ") executed for agent " . $agent->{'nombre'}, 2); + if (pandora_execute_action ($pa_config, $data, $agent, $alert, $alert_mode, $action, $dbh) == 1) { + + # Generate an event + my $tm_id = (defined ($alert->{'id_template_module'})) ? $alert->{'id_template_module'} : 0; + my ($text, $event) = ($alert_mode == 0) ? ('recovered', 'alert_recovered') : ('fired', 'alert_fired'); + + pandora_event ($pa_config, "Alert $text (" . $alert->{'description'} . ")", + $agent->{'id_grupo'}, $agent->{'id_agente'}, $alert->{'priority'}, $tm_id, + $alert->{'id_agent_module'}, $event, $dbh); + } + } +} + +########################################################################## +# Execute the given action. +########################################################################## +sub pandora_execute_action ($$$$$$$) { + my ($pa_config, $data, $agent, $alert, + $alert_mode, $action, $dbh) = @_; + + my $field1 = $action->{'field1'} ne "" ? $action->{'field1'} : $alert->{'field1'}; + my $field2 = $action->{'field2'} ne "" ? $action->{'field2'} : $alert->{'field2'}; + my $field3 = $action->{'field3'} ne "" ? $action->{'field3'} : $alert->{'field3'}; + + # Recovery fields, thanks to Kato Atsushi + if ($alert_mode == 0){ + $field2 = $alert->{'field2_recovery'} ne "" ? $alert->{'field2_recovery'} : "[RECOVER]" . $field2; + $field3 = $alert->{'field3_recovery'} ne "" ? $alert->{'field3_recovery'} : "[RECOVER]" . $field3; + } + + # Alert macros + my %macros = (_field1_ => $field1, + _field2_ => $field2, + _field3_ => $field3, + _agent_ => (defined ($agent)) ? $agent->{'nombre'} : '', + _address_ => (defined ($agent)) ? $agent->{'direccion'} : '', + _timestamp_ => localtime(), + _data_ => $data, + _alert_description_ => $alert->{'description'}, + _alert_threshold_ => $alert->{'time_threshold'}, + _alert_times_fired_ => $alert->{'times_fired'}, + ); + + + # User defined alerts + if ($action->{'internal'} == 0) { + my $command = decode_entities(subst_alert_macros ($action->{'command'}, \%macros)); + + eval { + system ($command); + if ($? != 0) { + logger($pa_config, 'Executed command for alert ' . $alert->{'name'} . ' returned with errorlevel ' . ($? >> 8), 1); + } + }; + + if ($@){ + logger($pa_config, "Error $@ executing command $command", 1); + } + + # Internal Audit + } elsif ($action->{'name'} eq "Internal Audit") { + logger($pa_config, "Internal audit for agent $agent", 3); + $field1 = subst_alert_macros ($field1, \%macros); + pandora_audit ($pa_config, $field1, $agent->{'nombre'}, "Alert (" . $alert->{'description'} . ")", $dbh); + + # Do not generate an event + return 0; + + # Email + } elsif ($action->{'name'} eq "eMail") { + $field2 = subst_alert_macros ($field2, \%macros); + $field3 = subst_alert_macros ($field3, \%macros); + pandora_sendmail ($pa_config, $field1, $field2, $field3); + + # Internal event + } elsif ($action->{'name'} eq "Pandora FMS Event") { + + # Unknown + } else { + logger($pa_config, "Unknown action " . $action->{'name'}, 1); + + # Do not generate an event + return 0 ; + } + + return 1; +} + +########################################################################## +#Update agent access table. +########################################################################## +sub pandora_access_update ($$$) { + my ($pa_config, $agent_id, $dbh) = @_; + + return if ($agent_id < 0); + + db_insert ($dbh, "INSERT INTO tagent_access (`id_agent`, `utimestamp`) VALUES (?, ?)", $agent_id, time ()); +} + +########################################################################## +#Process Pandora module. +########################################################################## +sub pandora_process_module ($$$$$$$$) { + my ($pa_config, $data, $agent, $module, $module_type, + $timestamp, $utimestamp, $dbh) = @_; + + # Get module type + if ($module_type eq '') { + $module_type = get_db_value ($dbh, 'SELECT nombre FROM ttipo_modulo WHERE id_tipo = ?', $module->{'id_tipo_modulo'}); + return unless defined ($module_type); + } + + # Process data + $data = process_data ($data, $module, $module_type, $utimestamp, $dbh); + if (! defined ($data)) { + logger($pa_config, "Received invalid data from module '" . $module->{'nombre'} . "'", 3); + return; + } + + # Get agent information + if ($agent eq '') { + $agent = get_db_single_row ($dbh, 'SELECT * FROM tagente WHERE id_agente = ?', $module->{'id_agente'}); + return unless defined ($agent); + } + + if ($timestamp eq '') { + $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + } + + # Export data + export_module_data ($data, $agent, $module, $module_type, $timestamp, $dbh); + + # Get previous status + my $agent_status = get_db_single_row ($dbh, 'SELECT * FROM tagente_estado WHERE id_agente_modulo = ?', $module->{'id_agente_modulo'}); + return unless defined ($agent_status); + + # Get current status + my $status = get_module_status ($data, $module, $module_type); + + # Generate alerts + pandora_generate_alerts ($pa_config, $data, $status, $agent, $module, $utimestamp, $dbh); + + #Update module status + my $current_utimestamp = time (); + my ($status_changes, $last_status) = ($status != $agent_status->{'estado'}) ? + (0, $agent_status->{'estado'}) : + ($agent_status->{'status_changes'} + 1, $agent_status->{'last_status'}); + + # Generate events + if ($agent_status->{'status_changes'} == $module->{'min_ff_event'}) { + generate_status_event ($pa_config, $data, $agent, $module, $status, $last_status, $dbh); + } + + # Do we have to save module data? + #my $save = ($module->{'history_data'} == 1 && ($agent_status->{'datos'} ne $data || $agent_status->{'last_try'} < (time() - 86400))) ? 1 : 0; + my $save = ($module->{'history_data'} == 1 && $agent_status->{'datos'} ne $data) ? 1 : 0; + + db_do ($dbh, 'UPDATE tagente_estado SET datos = ?, estado = ?, last_status = ?, status_changes = ?, utimestamp = ?, timestamp = ?, + id_agente = ?, current_interval = ?, running_by = ?, last_execution_try = ?, last_try = ? + WHERE id_agente_modulo = ?', $data, $status, $last_status, $status_changes, + $current_utimestamp, $timestamp, $module->{'id_agente'}, $module->{'module_interval'}, $pa_config->{'server_id'}, + $utimestamp, ($save == 1) ? $timestamp : $agent_status->{'last_try'}, $module->{'id_agente_modulo'}); + + # Save module data + if ($save == 1) { + save_module_data ($data, $module, $module_type, $utimestamp, $dbh); + } +} + +########################################################################## +#Update planned downtimes. +########################################################################## +sub pandora_planned_downtime ($$) { + my ($pa_config, $dbh) = @_; + + my $utimestamp = time(); + + # Start pending downtimes (disable agents) + my @downtimes = get_db_rows($dbh, 'SELECT * FROM tplanned_downtime WHERE executed = 0 AND date_from <= ? AND date_to >= ?', $utimestamp, $utimestamp); + + foreach my $downtime (@downtimes) { + + db_do($dbh, 'UPDATE tplanned_downtime SET executed = 1 WHERE id = ?', $downtime->{'id'}); + pandora_event ($pa_config, "Server ".$pa_config->{'servername'}." started planned downtime: ".$downtime->{'description'}, 0, 0, 1, 0, 0, 'system', $dbh); + + my @downtime_agents = db_do($dbh, 'SELECT * FROM tplanned_downtime_agents WHERE id_downtime = ' . $downtime->{'id'}); + + foreach my $downtime_agent (@downtime_agents) { + db_do ($dbh, 'UPDATE tagente SET disabled = 1 WHERE id_agente = ?', $downtime_agent->{'id_agent'}); + } + } + + # Stop executed downtimes (enable agents) + @downtimes = get_db_rows($dbh, 'SELECT * FROM tplanned_downtime WHERE executed = 1 AND date_to <= ?', $utimestamp); + foreach my $downtime (@downtimes) { + + db_do($dbh, 'UPDATE tplanned_downtime SET executed = 0 WHERE id = ?', $downtime->{'id'}); + pandora_event ($pa_config, 'Server ' . $pa_config->{'servername'} . ' stopped planned downtime: ' . $downtime->{'description'}, 0, 0, 1, 0, 0, 'system', $dbh); + + my @downtime_agents = get_db_rows($dbh, 'SELECT * FROM tplanned_downtime_agents WHERE id_downtime = ' . $downtime->{'id'}); + + foreach my $downtime_agent (@downtime_agents) { + db_do ($dbh, 'UPDATE tagente SET disabled = 0 WHERE id_agente = ?', $downtime_agent->{'id_agent'}); + } + } +} + +########################################################################## +#Update server status: 0 dataserver, 1 network server, 2 snmp console, +#3 recon, 4 plugin, 5 prediction, 6 wmi. +########################################################################## +sub pandora_update_server ($$$$$;$$) { + my ($pa_config, $dbh, $server_name, $status, + $server_type, $num_threads, $queue_size) = @_; + + $num_threads = 0 unless defined ($num_threads); + $queue_size = 0 unless defined ($queue_size); + + my $server = get_db_single_row ($dbh, 'SELECT * FROM tserver WHERE name = ? AND server_type = ?', $server_name, $server_type); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime()); + + # Create an entry in tserver + if (! defined ($server)){ + my $server_id = db_insert ($dbh, 'INSERT INTO tserver (`name`, `server_type`, `description`, `version`, `threads`, `queued_modules`) + VALUES (?, ?, ?, ?, ?, ?)', $server_name, $server_type, + 'Autocreated at startup', $pa_config->{'version'} . ' (P) ' . $pa_config->{'build'}, $num_threads, $queue_size); + $server = get_db_single_row ($dbh, 'SELECT * FROM tserver + WHERE id_server = ?', + $server_id); + return unless defined ($server); + } + + # Server going up + if ($server->{'status'} == 0) { + my $version = $pa_config->{'version'} . ' (P) ' . $pa_config->{'build'}; + + db_do ($dbh, 'UPDATE tserver SET status = ?, keepalive = ?, master = ?, laststart = ?, version = ?, threads = ?, queued_modules = ? + WHERE id_server = ?', + $status, $timestamp, $pa_config->{'pandora_master'}, $timestamp, $version, $num_threads, $queue_size, $server->{'id_server'}); + return; + } + + db_do ($dbh, 'UPDATE tserver SET status = ?, keepalive = ?, master = ?, threads = ?, queued_modules = ? + WHERE id_server = ?', $status, $timestamp, $pa_config->{'pandora_master'}, $num_threads, $queue_size, $server->{'id_server'}); +} + +########################################################################## +#Update last contact field in agent table +########################################################################## +sub pandora_update_agent ($$$$$$$) { + my ($pa_config, $agent_timestamp, $agent_id, $os_version, + $agent_version, $agent_interval, $dbh) = @_; + + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime()); + + pandora_access_update ($pa_config, $agent_id, $dbh); + + # No update for interval field (some old agents don't support it) + if ($agent_interval == -1){ + db_do($dbh, 'UPDATE tagente SET agent_version = ?, ultimo_contacto_remoto = ?, ultimo_contacto = ?, os_version = ? WHERE id_agente = ?', + $agent_version, $agent_timestamp, $timestamp, $os_version, $agent_id); + return; + } + + db_do ($dbh, 'UPDATE tagente SET intervalo = ?, agent_version = ?, ultimo_contacto_remoto = ?, ultimo_contacto = ?, os_version = ? WHERE id_agente = ?', + $agent_interval, $agent_version, $agent_timestamp, $timestamp, $os_version, $agent_id); +} + +########################################################################## +#Updates the keep_alive module for the given agent. +########################################################################## +sub pandora_module_keep_alive ($$$$) { + my ($pa_config, $id_agent, $agent_name, $dbh) = @_; + + # Update keepalive module + my $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente = ? AND id_tipo_modulo = 100', $id_agent); + return unless defined ($module); + + pandora_process_module ($pa_config, 1, '', $module, 'keep_alive', '', time(), $dbh); +} + +########################################################################## +#Create an internal Pandora incident. +########################################################################## +sub pandora_create_incident ($$$$$$$$) { + my ($pa_config, $dbh, $title, $text, + $priority, $status, $origin, $id_group) = @_; + + db_do($dbh, 'INSERT INTO tincidencia (`inicio`, `titulo`, `descripcion`, `origen`, `estado`, `prioridad`, `id_grupo`) + VALUES (NOW(), ?, ?, ?, ?, ?, ?)', $title, $text, $origin, $status, $priority, $id_group); +} + + +########################################################################## +#Create an internal audit entry. +########################################################################## +sub pandora_audit ($$$$$) { + my ($pa_config, $description, $name, $action, $dbh) = @_; + my $disconnect = 0; + + my $utimestamp = time(); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + db_insert($dbh, 'INSERT INTO tsesion (`ID_usuario`, `IP_origen`, `accion`, `fecha`, `descripcion`, `utimestamp`) + VALUES (?, ?, ?, ?, ?, ?)', + 'SYSTEM', $name, $action , $timestamp , $description , $utimestamp); + + db_disconnect($dbh) if ($disconnect == 1); +} + +########################################################################## +#Create a new entry in tagente_modulo and the corresponding entry in +#tagente_estado. +########################################################################## +sub pandora_create_module ($$$$$$$) { + my ($agent_id, $module_type_id, $module_name, $max, + $min, $description, $dbh) = @_; + + # Provide some default values + $max = 0 if ($max eq ''); + $min = 0 if ($min eq ''); + $description = 'N/A' if ($description eq ''); + + my $module_id = db_insert($dbh, 'INSERT INTO tagente_modulo (`id_agente`, `id_tipo_modulo`, `nombre`, `max`, `min`, `descripcion`, `id_modulo`) + VALUES (?, ?, ?, ?, ?, ?, 1)', $agent_id, $module_type_id, $module_name, $max, $min, $description); + db_do ($dbh, 'INSERT INTO tagente_estado (`id_agente_modulo`) VALUES (?)', $module_id); + return $module_id; +} + +########################################################################## +# Create a new entry in tagente. +########################################################################## +sub pandora_create_agent ($$$$$$$$$$) { + my ($pa_config, $server_name, $agent_name, $address, + $address_id, $group_id, $server_id, $parent_id, + $os_id, $dbh) = @_; + + logger ($pa_config, "$server_name: Creating agent $agent_name ($address)", 1); + + if ($server_id eq '') { + $server_id = get_db_value ($dbh, 'SELECT id_server FROM tserver WHERE network_server = 1 AND master = 1 LIMIT 1'); + $server_id = 0 unless defined ($server_id); + } + + my $agent_id = db_insert ($dbh, 'INSERT INTO tagente (`nombre`, `direccion`, `comentarios`, `id_grupo`, `id_os`, `id_network_server`, `intervalo`, `id_parent`, `modo`, `id_prediction_server`, `id_wmi_server`, `id_plugin_server`) + VALUES (?, ?, ?, ?, ?, ?, 300, ?, 1, 0, 0, 0)', $agent_name, $address, "Created by $server_name", $group_id, $os_id, $server_id, $parent_id); + + pandora_event ($pa_config, "Agent '$agent_name' created by $server_name", $pa_config->{'autocreate_group'}, $agent_id, 2, 0, 0, 'new_agent', $dbh); + return $agent_id; +} + +########################################################################## +#Generate an event. +########################################################################## +sub pandora_event (%$$$$$$$$) { + my ($pa_config, $evento, $id_grupo, $id_agente, $severity, + $id_alert_am, $id_agentmodule, $event_type, $dbh) = @_; + + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime ($utimestamp)); + + db_do ($dbh, 'INSERT INTO tevento (`id_agente`, `id_grupo`, `evento`, `timestamp`, `estado`, `utimestamp`, `event_type`, `id_agentmodule`, `id_alert_am`, `criticity`) + VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?)', $id_agente, $id_grupo, $evento, $timestamp, $utimestamp, $event_type, $id_agentmodule, $id_alert_am, $severity); +} + +########################################################################## +#Generate an event with the given status. TODO: Merge with pandora_event +########################################################################## +sub pandora_event_status ($$$$$$$$$$) { + my ($pa_config, $evento, $id_grupo, $id_agente, $severity, + $id_alert_am, $id_agentmodule, $event_type, $status, $dbh) = @_; + + my $utimestamp = time(); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + db_do ($dbh, 'INSERT INTO tevento (`id_agente`, `id_grupo`, `evento`, `timestamp`, `estado`, `utimestamp`, `event_type`, `id_agentmodule`, `id_alert_am`, `criticity`) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $id_agente, $id_grupo, $evento, $timestamp, $status, $utimestamp, $event_type, $id_agentmodule, $id_alert_am, $severity); +} + +########################################################################## +# Update module status on error. +########################################################################## +sub pandora_update_module_on_error ($$$) { + my ($pa_config, $id_agent_module, $dbh) = @_; + + # Update last_execution_try + db_do ($dbh, 'UPDATE tagente_estado SET current_interval = ?, last_execution_try = ? + WHERE id_agente_modulo = ?', 300, time (), $id_agent_module); +} + +########################################################################## +#Execute forced alerts. +########################################################################## +sub pandora_exec_forced_alerts { + my ($pa_config, $dbh) = @_; + + # Get alerts marked for forced execution (even disabled alerts) + my @alerts = get_db_rows ($dbh, 'SELECT talert_template_modules.id as id_template_module, talert_template_modules.*, talert_templates.*, tagente.* + FROM talert_template_modules, talert_templates, tagente, tagente_modulo + WHERE talert_template_modules.id_alert_template = talert_templates.id + AND talert_template_modules.id_agent_module = tagente_modulo.id_agente_modulo + AND tagente_modulo.id_agente = tagente.id_agente + AND force_execution = 1'); + + foreach my $alert (@alerts) { + + # $alert already contains agent data! + pandora_execute_alert ($pa_config, 'N/A', $alert, undef, $alert, 1, $dbh); + + # Reset the force_execution flag, even if the alert could not be executed + db_do ("UPDATE talert_template_modules SET force_execution = 0 WHERE id = " . $alert->{'id_template_module'}, $dbh); + } +} + +########################################################################## +#Update keep_alive modules for agents without data. +########################################################################## +sub pandora_module_keep_alive_nd { + my ($pa_config, $dbh) = @_; + + my @modules = get_db_rows ($dbh, 'SELECT tagente_modulo.* + FROM tagente_modulo, tagente_estado, tagente + WHERE tagente.id_agente = tagente_estado.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.id_tipo_modulo = 100 + AND tagente_modulo.disabled = 0 + AND tagente_estado.datos = 1 + AND tagente_estado.estado = 0 + AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo + AND ( tagente_estado.utimestamp + (tagente.intervalo * 2) < UNIX_TIMESTAMP())'); + + foreach my $module (@modules) { + pandora_process_module ($pa_config, 1, '', $module, 'keep_alive', '', time (), $dbh); + } +} + +########################################################################## +# Execute alerts that apply to the given SNMP trap. +########################################################################## +sub pandora_evaluate_snmp_alerts { + my ($pa_config, $trap_agent, $trap_oid, $trap_oid_text, + $trap_custom_value, $timestamp, $dbh, $alert_fired) = @_; + + # Get all SNMP alerts + my @snmp_alerts = get_db_rows ($dbh, 'SELECT * FROM talert_snmp'); + + # Find those that apply to the given SNMP trap + foreach my $alert (@snmp_alerts) { + my ($fire_alert, $alert_data) = (0, ''); + my ($times_fired, $internal_counter, $alert_type) = + ($alert->{'times_fired'}, $alert->{'internal_counter'}, $alert->{'alert_type'}); + + # OID only + if ($alert->{'alert_type'} == 0) { + my $oid = $alert->{'oid'}; + ($fire_alert, $alert_data) = (1, 'SNMP/OID:' . $oid) if ($trap_oid =~ m/$oid/i || + $trap_oid_text =~ m/$oid/i); + # Custom value + } elsif ($alert_type == 1){ # type 1 is custom value + my $custom_oid = $alert->{'custom_oid'}; + ($fire_alert, $alert_data) = (1, 'SNMP/VALUE:' . $custom_oid) if ($trap_custom_value =~ m/$custom_oid/i); + # Agent IP + } else { + my $agent = $alert->{'agent'}; + ($fire_alert, $alert_data) = (1, 'SNMP/SOURCE:' . $agent) if ($trap_agent =~ m/$agent/i ); + } + + next unless ($fire_alert == 1); + + # Check time threshold + my $last_fired = 0; + if ($alert->{'last_fired'} =~ /(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/) { + $last_fired = timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900); + } + + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Out limits, start a new interval + ($times_fired, $internal_counter) = (0, 0) if ($utimestamp >= ($last_fired + $alert->{'time_threshold'})); + + # Execute the alert + my ($min_alerts, $max_alerts) = ($alert->{'min_alerts'}, $alert->{'max_alerts'}); + if (($internal_counter + 1 >= $min_alerts) && ($times_fired + 1 <= $max_alerts)) { + ($times_fired++, $internal_counter++); + + logger ($pa_config, 'Executing SNMP Trap alert for ' . $alert->{'agent'} . ' - ' . $alert->{'alert_data'}, 2); + + my %alert = ( + 'name' => '', + 'agent' => 'N/A', + 'alert_data' => 'N/A', + 'id_agent_module' => 0, + 'id_template_module' => 0, + 'field1' => $alert->{'al_field1'}, + 'field2' => $alert->{'al_field2'}, + 'field3' => $alert->{'al_field3'}, + 'description' => $alert->{'description'}, + 'times_fired' => $times_fired, + 'time_threshold' => 0, + 'id_alert_action' => $alert->{'id_alert'}, + 'priority' => $alert->{'priority'}, + ); + + # Execute alert + my $action = get_db_single_row ($dbh, 'SELECT * + FROM talert_actions, talert_commands + WHERE talert_actions.id_alert_command = talert_commands.id + AND talert_actions.id = ?', $alert->{'id_alert'}); + if (defined ($action)) { + pandora_execute_action ($pa_config, '', undef, \%alert, 1, $action, $dbh); + } + + # Update alert status + db_do ($dbh, 'UPDATE talert_snmp SET times_fired = ?, last_fired = ?, internal_counter = ? WHERE id_as = ?', + $times_fired, $timestamp, $internal_counter, $alert->{'id_as'}); + + db_do ($dbh, 'UPDATE ttrap SET alerted = 1, PRIORITY = ? WHERE timestamp = ? AND source = ?', + $alert->{'priority'}, $timestamp, $trap_agent); + } else { + $internal_counter++; + if ($internal_counter < $min_alerts){ + # Now update the new value for times_fired & last_fired if we are below min limit for triggering this alert + db_do ($dbh, 'UPDATE talert_snmp SET internal_counter = ?, times_fired = ?, last_fired = ? WHERE id_as = ?', + $internal_counter, $times_fired, $timestamp, $alert->{'id_as'}); + } else { + db_do ($dbh, 'UPDATE talert_snmp SET times_fired=, internal_counter = ? WHERE id_as = ?', + $times_fired, $internal_counter, $alert->{'id_as'}); + } + } + } +} + +########################################################################## +# Utility functions, not to be exported. +########################################################################## + +########################################################################## +# Search string for macros and substitutes them with their values. +########################################################################## +sub subst_alert_macros ($$) { + my ($string, $macros) = @_; + + while ((my $macro, my $value) = each (%{$macros})) { + $string =~ s/($macro)/$value/ig; + } + + return $string; +} + +########################################################################## +# Process module data. +########################################################################## +sub process_data ($$$$$) { + my ($data, $module, $module_type, $utimestamp, $dbh) = @_; + + # String data + if ($module_type =~ m/_string$/) { + + # Empty strings are not allowed + return undef if ($data eq ''); + + return $data; + } + + # Not a number + if (! is_numeric ($data)) { + return undef; + } + + # Out of bounds + return undef if (($module->{'max'} != $module->{'min'}) && + ($data > $module->{'max'} || $data < $module->{'min'})); + + # Process INC modules + if ($module_type =~ m/_inc$/) { + $data = process_inc_data ($data, $module, $utimestamp, $dbh); + + # Not an error, no previous data + return 0 unless defined ($data); + } + + # Post process + if (is_numeric ($module->{'post_process'}) && $module->{'post_process'} != 0) { + $data = $data * $module->{'post_process'}; + } + + # Format data + $data = sprintf("%.2f", $data); + + return $data; +} + +########################################################################## +# Process data of type *_inc. +########################################################################## +sub process_inc_data ($$$$) { + my ($data, $module, $utimestamp, $dbh) = @_; + + my $data_inc = get_db_single_row ($dbh, 'SELECT * FROM tagente_datos_inc WHERE id_agente_modulo = ?', $module->{'id_agent_module'}); + + # No previous data + if (! defined ($data_inc)) { + db_insert ($dbh, 'INSERT INTO tagente_datos_inc + (`id_agente_modulo`, `datos`, `utimestamp`) + VALUES (?, ?, ?)', $module->{'id_agente_modulo'}, $data, $utimestamp); + return undef; + } + + # Should not happen + return 0 if ($utimestamp == $data_inc->{'utimestamp'}); + + return ($data - $data_inc->{'datos'}) / ($utimestamp - $data_inc->{'utimestamp'}); +} + +########################################################################## +# Returns the status of the module: 0 (NORMAL), 1 (CRITICAL), 2 (WARNING). +########################################################################## +sub get_module_status ($$) { + my ($data, $module, $module_type) = @_; + + # Critical + if (($module->{'min_critical'} ne $module->{'max_critical'})) { + return 1 if ($data >= $module->{'min_critical'} && $data <= $module->{'max_critical'}); + + # Proc modules default to critical if data is 0 and no max/min was specified + } elsif ($module_type =~ m/_proc$/ && $data == 0) { + return 1; + } + + # Warning + return 2 if (($module->{'min_warning'} ne $module->{'max_warning'}) && + ($data >= $module->{'min_warning'} && $data <= $module->{'max_warning'})); + + # Normal + return 0; +} + +########################################################################## +# Generates an event according to the change of status of a module. +########################################################################## +sub generate_status_event ($$$$$$$) { + my ($pa_config, $data, $agent, $module, $status, $last_status, $dbh) = @_; + my ($event_type, $severity); + + my $description = "Module " . $module->{'nombre'} . " ($data) is "; + + # Normal + if ($status == 0) { + ($event_type, $severity) = ('going_down_normal', 2); + $description .= "going down to NORMAL"; + + # Critical + } elsif ($status == 1) { + ($event_type, $severity) = ('going_up_critical', 4); + $description .= "going up to CRITICAL"; + + # Warning + } elsif ($status == 2) { + + # From normal + if ($last_status == 0) { + ($event_type, $severity) = ('going_up_warning', 3); + $description .= "going up to WARNING"; + + # From critical + } elsif ($last_status == 1) { + ($event_type, $severity) = ('going_down_warning', 3); + $description .= "going down to WARNING"; + } else { + # Unknown last_status + return; + } + } else { + # Unknown status + return; + } + + # Generate the event + pandora_event_status ($pa_config, $description, $agent->{'id_grupo'}, $module->{'id_agente'}, + $severity, 0, $module->{'id_agente_modulo'}, $event_type, $status, $dbh); +} + +########################################################################## +# Saves module data to the DB. +########################################################################## +sub save_module_data ($$$$$) { + my ($data, $module, $module_type, $utimestamp, $dbh) = @_; + + my $table = ($module_type =~ m/_string/) ? 'tagente_datos_string' : 'tagente_datos'; + + db_do($dbh, 'INSERT INTO ' . $table . ' (id_agente_modulo, datos, utimestamp) + VALUES (?, ?, ?)', $module->{'id_agente_modulo'}, $data, $utimestamp); +} + +########################################################################## +# Export module data. +########################################################################## +sub export_module_data ($$$$$$) { + my ($data, $agent, $module, $module_type, $timestamp, $dbh) = @_; + + # Data export is disabled + return if ($module->{'id_export'} < 1); + + db_do($dbh, 'INSERT INTO tserver_export_data + (`id_export_server`, `agent_name` , `module_name`, `module_type`, `data`, `timestamp`) VALUES + (?, ?, ?, ?, ?, ?)', $module->{'id_export'}, $agent->{'nombre'}, $module->{'nombre'}, $module_type, $data, $timestamp); +} + +# End of function declaration +# End of defined Code + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/DB.pm b/pandora_server/lib/PandoraFMS/DB.pm index 527d99e398..418436f279 100644 --- a/pandora_server/lib/PandoraFMS/DB.pm +++ b/pandora_server/lib/PandoraFMS/DB.pm @@ -3,6 +3,7 @@ package PandoraFMS::DB; # Database Package # Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org ########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es # Copyright (c) 2004-2009 Sancho Lerena, slerena@gmail.com # Copyright (c) 2005-2009 Artica Soluciones Tecnologicas S.L # @@ -20,17 +21,7 @@ package PandoraFMS::DB; use strict; use warnings; -use Time::Local; -use Time::Format qw(%time %strftime %manip); # For data mangling use DBI; -use Date::Manip;# Needed to manipulate DateTime formats of input, output and compare -use XML::Simple; -use HTML::Entities; - -use POSIX qw(strtod); - -use PandoraFMS::Tools; -enterprise_load (); require Exporter; @@ -38,2561 +29,185 @@ our @ISA = ("Exporter"); our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( - calcula_alerta_snmp - crea_agente_modulo - update_on_error - dame_server_id - dame_agente_id - dame_agente_modulo_id - dame_agente_nombre - dame_comando_alerta - dame_desactivado - dame_grupo_agente - dame_id_tipo_modulo - dame_intervalo - dame_learnagente - dame_modulo_id - dame_nombreagente_agentemodulo - dame_nombretipomodulo_idagentemodulo - dame_ultimo_contacto - give_networkserver_status - - pandora_writedata - pandora_writestate - - pandora_updateserver - pandora_serverkeepaliver - pandora_audit - pandora_lastagentcontact - pandora_evaluate_alert - pandora_evaluate_compound_alert - pandora_generate_alerts - pandora_generate_compound_alerts - pandora_process_alert - pandora_planned_downtime - pandora_create_agent - pandora_event - module_generic_proc - module_generic_data - module_generic_data_inc - module_generic_data_string - execute_alert - give_network_component_profile_name - pandora_create_incident - update_keepalive_module - - get_db_value - get_db_free_row - get_db_all_rows - get_db_free_field - db_insert + db_connect + db_disconnect db_do + db_insert + get_agent_id + get_agent_name + get_db_rows + get_db_single_row + get_db_value + get_module_id + get_nc_profile_name + get_server_id + is_group_disabled ); -# Spanish translation note: -# 'Crea' in spanish means 'create' -# 'Dame' in spanish means 'give' - ########################################################################## -## SUB subst_alert_macros (string, macros) { -## Searches string for macros and substitutes them with their values. +## Connect to the DB. ########################################################################## +sub db_connect ($$$$$$) { + my ($rdbms, $db_name, $db_host, $db_port, $db_user, $db_pass) = @_; -sub subst_alert_macros ($\%) { - my $string = $_[0]; - my %macros = %{$_[1]}; - - while ((my $macro, my $value) = each (%macros)) { - $string =~ s/($macro)/$value/ig; - } - - return $string; -} - -########################################################################## -## SUB pandora_generate_alerts -## (paconfig, timestamp, agent_name, $id_agent, id_agent_module, -## id_group, module_data, module_type, dbh) -## Generate alerts for a given module. -########################################################################## - -sub pandora_generate_alerts (%$$$$$$$) { - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $agent_name = $_[2]; - my $id_agent = $_[3]; - my $id_agent_module = $_[4]; - my $id_group = $_[5]; - my $module_data = $_[6]; - my $dbh = $_[7]; - - # Do not generate alerts for disabled groups - if (give_group_disabled ($pa_config, $id_group, $dbh) == 1) { - return; - } - - # Get enabled alerts associated with this module - my @alerts = get_db_all_rows ("SELECT talert_template_modules.id as id_template_module, talert_template_modules.*, talert_templates.* - FROM talert_template_modules, talert_templates - WHERE talert_template_modules.id_alert_template = talert_templates.id - AND id_agent_module = $id_agent_module - AND disabled = 0", $dbh); - - foreach my $alert_data (@alerts) { - my $rc = pandora_evaluate_alert($pa_config, $timestamp, $alert_data, - $module_data, 0, $dbh); - pandora_process_alert ($pa_config, $timestamp, $rc, $agent_name, - $id_agent, $id_group, $alert_data, $module_data, 0, - $dbh); - - # Evaluate compound alerts even if the alert status did not change in - # case the compound alert does not recover - pandora_generate_compound_alerts ($pa_config, $timestamp, - $agent_name, $id_agent, - $alert_data->{'id_template_module'}, - $id_group, 0, $dbh); - } -} - -########################################################################## -## SUB pandora_evaluate_alert -## (paconfig, timestamp, alert_data, module_data, dbh) -## Evaluate trigger conditions for a given alert. Returns: -## 0 Execute the alert. -## 1 Do not execute the alert. -## 2 Do not execute the alert, but increment its internal counter. -## 3 Cease the alert. -## 4 Recover the alert. -## 5 Reset internal counter (alert not fired, interval elapsed). -########################################################################## - -sub pandora_evaluate_alert ($$$$$$) { - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $alert_data = $_[2]; - my $module_data = $_[3]; - my $compound = $_[4]; - my $dbh = $_[5]; - - my $status = 1; # Value returned on valid data - my $err; - - # Check weekday - if ($alert_data->{lc(&UnixDate("today","%A"))} != 1) { - return 1; - } - - # Check time slot - my $time = &UnixDate("today","%H:%M"); - - if (($alert_data->{'time_to'} ne $alert_data->{'time_from'}) && - (($time ge $alert_data->{'time_to'}) || - ($time le $alert_data->{'time_from'}))) { - return 1; - } - - # Check time threshold - my $limit_date = DateCalc (ParseDateString("epoch " . $alert_data->{'last_reference'}), "+ " . - $alert_data->{'time_threshold'} . " seconds", - \$err); - my $date = ParseDate($timestamp); - - if ($alert_data->{'times_fired'} > 0) { - - # Reset fired alerts - if (Date_Cmp ($date, $limit_date) >= 0) { - - # Cease on valid data - $status = 3; - - # Always reset - $alert_data->{'internal_counter'} = 0; - $alert_data->{'times_fired'} = 0; - } - - # Recover takes precedence over cease - if ($alert_data->{'recovery_notify'} == 1) { - $status = 4; - } - } elsif (Date_Cmp ($date, $limit_date) >= 0) { - $status = 5; - } - - # Check for valid data - if ($compound == 0) { - if ($alert_data->{'type'} eq "min" && $module_data >= $alert_data->{'min_value'}) { - return $status; - } - elsif ($alert_data->{'type'} eq "max" && $module_data <= $alert_data->{'max_value'}) { - return $status; - } - elsif ($alert_data->{'type'} eq "max_min") { - if ($alert_data->{'matches_value'} == 1 && - $module_data <= $alert_data->{'min_value'} && - $module_data >= $alert_data->{'max_value'}) { - return $status; - } - - if ($module_data >= $alert_data->{'min_value'} && - $module_data <= $alert_data->{'max_value'}) { - return $status; - } - } - elsif ($alert_data->{'type'} eq "equal" && $module_data != $alert_data->{'value'}) { - return $status; - } - elsif ($alert_data->{'type'} eq "not_equal" && $module_data == $alert_data->{'value'}) { - return $status; - } - elsif ($alert_data->{'type'} eq "regex") { - if ($alert_data->{'value'} == 1 && $module_data =~ m/$alert_data->{'value'}/i) { - return $status; - } - - if ($module_data !~ m/$alert_data->{'value'}/i) { - return $status; - } - } - } - elsif (pandora_evaluate_compound_alert($pa_config, $alert_data->{'id'}, $dbh) == 0) { - return $status - } - - # Check min and max alert limits - if (($alert_data->{'internal_counter'} < $alert_data->{'min_alerts'}) || - ($alert_data->{'times_fired'} >= $alert_data->{'max_alerts'})) { - return 2; - } - - return 0; -} - -########################################################################## -## SUB pandora_process_alert -## ($pa_config, $timestamp, $rc, $agent_name, $id_agent, $id_group, -## $alert_data, $module_data, $dbh) -## Process an alert given the status returned by pandora_evaluate_alert. -########################################################################## - -sub pandora_process_alert (%$$$$$%$$) { - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $rc = $_[2]; - my $agent_name = $_[3]; - my $id_agent = $_[4]; - my $id_group = $_[5]; - my $alert_data = $_[6]; - my $module_data = $_[7]; - my $compound = $_[8]; - my $dbh = $_[9]; - - # Do not execute - if ($rc == 1) { - return; - } - - # Compound or simple alert? - my $table; - my $id; - - if ($compound == 0) { - $table = 'talert_template_modules'; - $id = 'id_template_module'; - } - else { - $table = 'talert_compound'; - $id = 'id'; - } - - # Cease - if ($rc == 3) { - - # Update alert status - db_do("UPDATE $table SET times_fired = 0, - internal_counter = 0 WHERE id = " . - $alert_data->{$id}, $dbh); - - # Generate an event - pandora_event ($pa_config, "Alert ceased (" . - $alert_data->{'descripcion'} . ")", $id_group, - $id_agent, $alert_data->{'priority'}, $alert_data->{'id_template_module'}, $alert_data->{'id_agent_module'}, - "alert_recovered", $dbh); - - return; - } - - # Recover - if ($rc == 4) { - - # Update alert status - db_do("UPDATE $table SET times_fired = 0, - internal_counter = 0 WHERE id = " . - $alert_data->{$id}, $dbh); - - execute_alert ($pa_config, $alert_data, $id_agent, $id_group, $agent_name, - $module_data, 0, $compound, $dbh); - return; - } - - # Reset internal counter - if ($rc == 5) { - db_do("UPDATE $table SET internal_counter = 0 WHERE id = " . - $alert_data->{$id}, $dbh); - return; - } - - # Get current date - my $date_db = &UnixDate("today","%s"); - - # Increment internal counter - if ($rc == 2) { - my $new_interval = ""; - - # Start a new interval - if ($alert_data->{'internal_counter'} == 0) { - $new_interval = ", last_reference = $date_db"; - } - - # Update alert status - $alert_data->{'internal_counter'} += 1; - - # Do not increment times_fired, but set it in case the alert was reset - db_do("UPDATE $table SET times_fired = " . - $alert_data->{'times_fired'} . ", internal_counter = " . - $alert_data->{'internal_counter'} . $new_interval . - " WHERE id = " . $alert_data->{$id}, $dbh); - return; - } - - # Execute - if ($rc == 0) { - my $new_interval = ""; - - # Start a new interval - if ($alert_data->{'internal_counter'} == 0) { - $new_interval .= ", last_reference = $date_db"; - } - - # Update alert status - $alert_data->{'times_fired'} += 1; - $alert_data->{'internal_counter'} += 1; - - db_do("UPDATE $table SET times_fired = " . - $alert_data->{'times_fired'} . ", last_fired = $date_db, internal_counter = " . - $alert_data->{'internal_counter'} . $new_interval . " WHERE id = " . $alert_data->{$id}, $dbh); - - execute_alert ($pa_config, $alert_data, $id_agent, $id_group, $agent_name, - $module_data, 1, $compound, $dbh); - - return; - } -} - -########################################################################## -## SUB pandora_evaluate_compound_alert -## (pa_config, id, dbh) -## Evaluate a given compound alert. Returns 1 if the alert should be -## fired, 0 if not. -########################################################################## -sub pandora_evaluate_compound_alert (%$$) { - my $pa_config = $_[0]; - my $id = $_[1]; - my $dbh = $_[2]; - - my @data; - - # Return value - my $status = 0; - - # Get all the alerts associated with this compound alert - my $query_compound = "SELECT id_alert_template_module, operation FROM talert_compound_elements - WHERE id_alert_compound = '$id' ORDER BY `order`"; - my $handle_compound = $dbh->prepare($query_compound); - $handle_compound ->execute; - - if ($handle_compound->rows == 0) { - return 0; - } - - my $query_alert = "SELECT times_fired FROM - talert_template_modules WHERE id = ? AND disabled = 0"; - my $handle_alert = $dbh->prepare($query_alert); - - while (my $data_compound = $handle_compound->fetchrow_hashref()) { - - # Get alert data if enabled - $handle_alert->execute($data_compound->{'id_alert_template_module'}); - if ($handle_alert->rows == 0) { - next; - } - - my $data_alert = $handle_alert->fetchrow_hashref(); - - # Check whether the alert was fired - my $fired = $data_alert->{'times_fired'} > 0 ? 1 : 0; - - my $operation = $data_compound->{'operation'}; - - # Operate... - if ($operation eq "AND") { - $status &= $fired; - } - elsif ($operation eq "OR") { - $status |= $fired; - } - elsif ($operation eq "XOR") { - $status ^= $fired; - } - elsif ($operation eq "NAND") { - $status &= ! $fired; - } - elsif ($operation eq "NOR") { - $status |= ! $fired; - } - elsif ($operation eq "NXOR") { - $status ^= ! $fired; - } - elsif ($operation eq "NOP") { - $status = $fired; - } - } - - $handle_alert->finish(); - $handle_compound->finish(); - return $status; -} - -########################################################################## -## SUB pandora_generate_compound_alerts -## (pa_config, timestamp, agent_name, id_agent, id_alert_agent_module, id_group, -## module_data, module_type, depth, dbh) -## Generate compound alerts that depend on a given alert. -########################################################################## - -sub pandora_generate_compound_alerts (%$$$$$$$) { - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $agent_name = $_[2]; - my $id_agent = $_[3]; - my $id_alert_template_module = $_[4]; - my $id_group = $_[5]; - my $depth = $_[6]; - my $dbh = $_[7]; - - # Get all compound alerts that depend on this alert - my $query_compound = "SELECT id_alert_compound FROM talert_compound_elements WHERE id_alert_template_module = '" . - $id_alert_template_module . "'"; - - my $handle_compound = $dbh->prepare($query_compound); - - $handle_compound->execute; - - if ($handle_compound->rows == 0) { - $handle_compound->finish(); - return; - } - - my $query_alert = "SELECT * FROM talert_compound WHERE id = ?"; - my $handle_alert = $dbh->prepare($query_alert); - - while (my $data_compound = $handle_compound->fetchrow_hashref()) { - - # Get compound alert parameters - $handle_alert->execute($data_compound->{'id_alert_compound'}); - if ($handle_alert->rows == 0) { - next; - } - - my $data_alert = $handle_alert->fetchrow_hashref(); - - # Evaluate the alert - my $rc = pandora_evaluate_alert($pa_config, $timestamp, $data_alert, - '', 1, $dbh); - - pandora_process_alert ($pa_config, $timestamp, $rc, $agent_name, $id_agent, - $id_group, $data_alert, '', 1, $dbh); - - # Evaluate nested compound alerts - #if ($depth >= $pa_config->{'compound_max_depth'}) { - # logger($pa_config, "ERROR: Error in SUB pandora_generate_compound_ - # alerts(): Maximum nested compound alert depth - # reached.", 2); - # next; - #} - - #&pandora_generate_compound_alerts ($pa_config, $timestamp, $agent_name, - # $id_agent, $data_compound->{'id'}, - # $id_group, $depth + 1, $dbh); - } - - $handle_alert->finish(); - $handle_compound->finish(); -} - -########################################################################## -## SUB execute_alert -## Do a execution of given alert with this parameters -########################################################################## - -sub execute_alert ($$$$$$$$$) { - my $pa_config = $_[0]; - my $alert = $_[1]; - my $id_agent = $_[2]; - my $id_group = $_[3]; - my $agent = $_[4]; - my $data = $_[5]; - my $alert_mode = $_[6]; # 0 recovery, 1 normal - my $compound = $_[7]; - my $dbh = $_[8]; - - # Get active actions/commands - my @actions; - - if ($compound == 0) { - @actions = get_db_all_rows ("SELECT * FROM talert_template_module_actions, talert_actions, talert_commands - WHERE talert_template_module_actions.id_alert_action = talert_actions.id - AND talert_actions.id_alert_command = talert_commands.id - AND talert_template_module_actions.id_alert_template_module = " . - $alert->{'id_template_module'} . - " AND ((fires_min = 0 AND fires_max = 0) - OR (" . $alert->{'times_fired'} . " >= fires_min AND " . $alert->{'times_fired'} . " <= fires_max))", $dbh); - } - else { - @actions = get_db_all_rows ("SELECT * FROM talert_compound_actions, talert_actions, talert_commands - WHERE talert_compound_actions.id_alert_action = talert_actions.id - AND talert_actions.id_alert_command = talert_commands.id - AND talert_compound_actions.id_alert_compound = " . - $alert->{'id'} . - " AND ((fires_min = 0 AND fires_max = 0) - OR (" . $alert->{'times_fired'} . " >= fires_min AND " . $alert->{'times_fired'} . " <= fires_max))", $dbh); - } - - # Get default action - if ($#actions < 0) { - - # Compound alert don't have a default action - if ($compound == 1) { - return; - } - - @actions = get_db_all_rows ("SELECT * FROM talert_actions, talert_commands - WHERE talert_actions.id = " . $alert->{'id_alert_action'} . - " AND talert_actions.id_alert_command = talert_commands.id", $dbh); - if ($#actions < 0) { - return; - } + if ($rdbms eq 'mysql') { + return DBI->connect("DBI:mysql:$db_name:$db_host:3306", $db_user, $db_pass, { RaiseError => 1, AutoCommit => 1 }); } - # Get agent address - my $address = get_db_value ('direccion', 'tagente', 'id_agente', $id_agent, $dbh); - - # Execute actions - foreach my $action (@actions) { - my $field1 = $action->{'field1'} ne "" ? $action->{'field1'} : $alert->{'field1'}; - my $field2 = $action->{'field2'} ne "" ? $action->{'field2'} : $alert->{'field2'}; - my $field3 = $action->{'field3'} ne "" ? $action->{'field3'} : $alert->{'field3'}; - - # Recovery fields, thanks to Kato Atsushi - if ($alert_mode == 0){ - $field2 = $alert->{'field2_recovery'} ne "" ? $alert->{'field2_recovery'} : "[RECOVER]" . $field2; - $field3 = $alert->{'field3_recovery'} ne "" ? $alert->{'field3_recovery'} : "[RECOVER]" . $field3; - } - - # Alert macros - my %macros = (_field1_ => $field1, - _field2_ => $field2, - _field3_ => $field3, - _agent_ => $agent, - _address_ => $address, - _timestamp_ => &UnixDate ("today", "%Y-%m-%d %H:%M:%S"), - _data_ => $data, - _alert_description_ => $alert->{'description'}, - _alert_threshold_ => $alert->{'time_threshold'}, - _alert_times_fired_ => $alert->{'times_fired'}, - ); - - logger($pa_config, "Alert (" . $alert->{'name'} . ") executed for agent $agent", 2); - - # User defined alerts - if ($action->{'internal'} == 0) { - my $command = subst_alert_macros ($action->{'command'}, %macros); - $command = decode_entities($command); - eval { - system ($command); - my $rc = $? >> 8; # Shift 8 bits to get a "classic" errorlevel - if ($rc != 0) { - logger($pa_config, "Executed command for alert " . $alert->{'name'} . " returned with errorlevel $rc", 1); - } - }; - - if ($@){ - logger($pa_config, "Error $@ executing command $command", 1); - } - # Internal Audit - } elsif ($action->{'name'} eq "Internal Audit") { - logger($pa_config, "Internal audit for agent $agent", 3); - $field1 = subst_alert_macros ($field1, %macros); - pandora_audit ($pa_config, $field1, $agent, "Alert (" . $alert->{'description'} . ")", $dbh); - - # Return without creating an event - return; - # Email - } elsif ($action->{'name'} eq "eMail") { - $field2 = subst_alert_macros ($field2, %macros); - $field3 = subst_alert_macros ($field3, %macros); - pandora_sendmail ($pa_config, $field1, $field2, $field3); - # Internal event - } elsif ($action->{'name'} eq "Pandora FMS Event") { - # Unknown - } else { - logger($pa_config, "Unknown action " . $action->{'name'}, 1); - return; - } - - # Create an event - if ($alert_mode == 0){ - pandora_event ($pa_config, "Alert recovered (" . $alert->{'description'} . ")", $id_group, $id_agent, $alert->{'priority'}, $compound == 0 ? $alert->{'id_template_module'} : 0, - $alert->{'id_agent_module'}, 'alert_recovered', $dbh); - } else { - pandora_event ($pa_config, "Alert fired (" . $alert->{'description'} . ")", $id_group, $id_agent, $alert->{'priority'}, $compound == 0 ? $alert->{'id_template_module'} : 0, - $alert->{'id_agent_module'}, 'alert_fired', $dbh); - } - } -} - - -########################################################################## -## SUB pandora_writestate (pa_config, nombre_agente,tipo_modulo, -# nombre_modulo,valor_datos, dbh, needupdate) -## Alter data, chaning status of modules in state table -########################################################################## - -sub pandora_writestate (%$$$$$$) { - # slerena, 05/10/04 : Fixed bug because differences between agent / server time source. - # now we use only local timestamp to stamp state of modules - my $pa_config = $_[0]; - my $nombre_agente = $_[1]; - my $tipo_modulo = $_[2]; # passed as string - my $nombre_modulo = $_[3]; - my $datos = $_[4]; # Careful: This don't reference a hash, only a single value - my $dbh = $_[5]; - my $needs_update = $_[6]; - - my $cambio = 0; - - # Get current timestamp / unix numeric time - my $timestamp = &UnixDate ("today", "%Y-%m-%d %H:%M:%S"); # string timestamp - my $utimestamp = &UnixDate($timestamp, "%s"); # convert from human to integer - - # Get server id - my $server_name = $pa_config->{'servername'}.$pa_config->{"servermode"}; - my $id_server = dame_server_id($pa_config, $server_name, $dbh); - - # Get id - # BE CAREFUL: We don't verify the strings chains - # TO DO: Verify errors - - my $agent_data = get_db_free_row ("SELECT * FROM tagente WHERE nombre = '$nombre_agente'", $dbh); - if ($agent_data == -1){ - return -1; - } - - my $id_modulo = dame_modulo_id ($pa_config, $tipo_modulo, $dbh); - my $id_agente_modulo = dame_agente_modulo_id($pa_config, $agent_data->{'id_agente'}, $id_modulo, $nombre_modulo, $dbh); - - # Valid agent ? - if ($id_agente_modulo == -1) { - return -1; - } - - my $module_data = get_db_free_row ("SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_agente_modulo", $dbh); - - # Valid string data ? (not null) - if (($id_modulo == 3) || ($id_modulo == 17) || ($id_modulo == 10) || ($id_modulo == 23)){ - if ($datos eq "") { - return -1; - } - } - - # Take group for this module - my $id_grupo = dame_grupo_agente($pa_config, $agent_data->{'id_agente'}, $dbh); - - # Postprocess management - if ( defined($module_data->{'post_process'}) && ($module_data->{'post_process'}> 0)) { - if (($id_modulo == 1) || ($id_modulo == 7) || ($id_modulo == 15) || ($id_modulo == 22) || ($id_modulo == 4) || ($id_modulo == 8) || ($id_modulo == 16) ){ - $datos = $datos * $module_data->{'post_process'}; - } - } - - # Status management - my $estado = 0; # Normal (OK) by default - - - # Only PROC modules have min_critical/max_critical default - if ( $tipo_modulo =~ m/proc/ ){ - if ($module_data->{'min_critical'} eq $module_data->{'max_critical'}){ - $module_data->{'min_critical'} = 0; - $module_data->{'max_critical'} = 1; - } - } - - if ($module_data->{'min_warning'} ne $module_data->{'max_warning'}){ - if (($datos >= $module_data->{'min_warning'}) && ($datos < $module_data->{'max_warning'})){ - $estado = 2; - } - if (($datos >= $module_data->{'min_warning'}) && ($module_data->{'max_warning'} < $module_data->{'min_warning'})){ - $estado = 2; - } - } - - if ($module_data->{'min_critical'} ne $module_data->{'max_critical'}){ - if (($datos >= $module_data->{'min_critical'}) && ($datos < $module_data->{'max_critical'})){ - $estado = 1; - } - if (($datos >= $module_data->{'min_critical'}) && ($module_data->{'max_critical'} < $module_data->{'min_critical'})){ - $estado = 1; - } - } - - # Get module interval or agent interval if module don't defined - my $module_interval = $module_data->{'module_interval'}; - if ($module_data->{'module_interval'} == 0){ - $module_interval = dame_intervalo ($pa_config, $module_data->{'id_agente'}, $dbh); - } - - # Check alert subroutine - Protect execution on an eval block - - eval { - pandora_generate_alerts ($pa_config, $timestamp, $nombre_agente, $module_data->{'id_agente'}, $id_agente_modulo, $id_grupo, $datos, $dbh); - }; - if ($@) { - logger($pa_config, "ERROR: Error in SUB calcula_alerta(). ModuleName: $nombre_modulo ModuleType: $tipo_modulo AgentName: $nombre_agente", 4); - logger($pa_config, "ERROR Code: $@",10) - } - - # Let's see if there is any entry at tagente_estado table - my $data_status = get_db_free_row ("SELECT * from tagente_estado WHERE id_agente_modulo = ". $module_data->{'id_agente_modulo'}, $dbh); - - # Apply Mysql quotes to data to prepare for database insertion / update - $datos = $dbh->quote($datos); # Parse data entry for adecuate SQL representation. - - my $query_act; - - if ($data_status == -1) { # Doesnt exist entry in table, lets make the first entry - logger($pa_config, "Create entry in tagente_estado for module $nombre_modulo", 4); - $query_act = "INSERT INTO tagente_estado (id_agente_modulo, datos, timestamp, estado, id_agente, last_try, utimestamp, current_interval, running_by, last_execution_try, last_status, status_changes) VALUES (". $module_data->{'id_agente_modulo'} ." , $datos,'$timestamp',". $estado ." , " . $agent_data->{'id_agente'}. ",'$timestamp',$utimestamp, $module_interval, $id_server, $utimestamp, 0, 0)"; - - } else { # An entry in table already exists - $data_status->{'estado'} = $estado; - if ( $data_status->{'last_execution_try'} == 0){ - $needs_update = 1; - } - - # Track status change - if ( $data_status->{'last_status'} == $data_status->{'estado'}){ - $data_status->{'status_changes'} = 0; - } else { - $data_status->{'status_changes'} = $data_status->{'status_changes'} + 1 ; - - # Raise event ? - if ($data_status->{'status_changes'} > $module_data->{'min_ff_event'}){ - - $needs_update = 1; - my $event_type = ""; - my $status_name = ""; - my $severity = 0; - - if (($data_status->{'last_status'} == 0) && ($data_status->{'estado'} == 2)){ - $event_type = "going_up_warning"; - $status_name = "going up to WARNING"; - $severity = 3; - enterprise_hook('mcast_change_report', [$pa_config, $module_data->{'nombre'}, $module_data->{'custom_id'}, $timestamp, 'WARN', $dbh]); - } elsif (($data_status->{'last_status'} == 1) && ($data_status->{'estado'} == 2)){ - $event_type = "going_down_warning"; - $status_name = "going down to WARNING"; - $severity = 3; - enterprise_hook('mcast_change_report', [$pa_config, $module_data->{'nombre'}, $module_data->{'custom_id'}, $timestamp, 'WARN', $dbh]); - } elsif ($data_status->{'estado'} == 1){ - $event_type = "going_down_critical"; - $status_name = "going down to CRITICAL"; - $severity = 4; - enterprise_hook('mcast_change_report', [$pa_config, $module_data->{'nombre'}, $module_data->{'custom_id'}, $timestamp, 'ERR', $dbh]); - } elsif ($data_status->{'estado'} == 0){ - $event_type = "going_up_normal"; - $status_name = "going up to NORMAL"; - $severity = 2; - enterprise_hook('mcast_change_report', [$pa_config, $module_data->{'nombre'}, $module_data->{'custom_id'}, $timestamp, 'OK', $dbh]); - } - $data_status->{'status_changes'} = 0; - $data_status->{'last_status'} = $data_status->{'estado'}; - - my $description = "Module ".$module_data->{'nombre'}." ($datos) is $status_name"; - pandora_event ($pa_config, $description, $id_grupo, - $module_data->{'id_agente'}, $severity, 0, $module_data->{'id_agente_modulo'}, - $event_type, $dbh); - - if ($event_type eq "going_up_warning"){ - # Clean up and system mark all active CRITICAL events for this module - db_do ("UPDATE tevento SET estado=1 WHERE id_agentmodule = ".$module_data->{'id_agente_modulo'}." AND event_type = 'going_down_critical'", $dbh); - } - elsif ($event_type eq "going_up_normal"){ - # Clean up and system mark all active WARNING and CRITICAL events for this module - db_do ("UPDATE tevento SET estado=1 WHERE id_agentmodule = ".$module_data->{'id_agente_modulo'}." AND (event_type = 'going_up_warning' OR event_type = 'going_down_warning' OR event_type = 'going_down_critical')", $dbh); - } - } - } - - my $needs_update_sql = ""; - if ($needs_update == 1) { - $needs_update_sql = " , last_try = '$timestamp' "; - } - - $query_act = "UPDATE tagente_estado SET utimestamp = $utimestamp, datos = $datos, last_status = " . $data_status->{'last_status'} . ", status_changes = " . $data_status->{'status_changes'} . ", timestamp = '$timestamp', estado = ".$data_status->{'estado'}.", id_agente = $module_data->{'id_agente'}, current_interval = $module_interval, running_by = $id_server, last_execution_try = $utimestamp " . $needs_update_sql . " WHERE id_agente_modulo = ". $module_data->{'id_agente_modulo'}; - } - db_do ($query_act, $dbh); + return undef; } ########################################################################## -#### MODULOS implementados en Pandora +## Disconnect from the DB. ########################################################################## +sub db_disconnect ($) { + my $dbh = shift; -# ----------------------------------------+ -# Modulos genericos de Pandora | -# ----------------------------------------+ - -# Los modulos genericos de pandora son de 4 tipos -# -# generic_data . Almacena numeros enteros largos, util para monitorizar proceos que -# general valores o sensores que devuelven valores. - -# generic_proc . Almacena informacion booleana (cierto/false), util para monitorizar -# procesos logicos. - -# generic_data_inc . Almacena datos igual que generic_data pero tiene una logica -# que sirve para las fuentes de datos que alimentan el agente con datos -# que se incrementan continuamente, por ejemplo, los contadores de valores -# en las MIB de los adaptadores de red, las entradas de cierto tipo en -# un log o el nº de segundos que ha pasado desde X momento. Cuando el valor -# es mejor que el anterior o es 0, se gestiona adecuadamente el cambio. - -# generic_data_string. Store a string, max 255 chars. - -########################################################################## -## SUB pandora_accessupdate (pa_config, id_agent, dbh) -## Update agent access table -########################################################################## - -sub pandora_accessupdate (%$$) { - my $pa_config = $_[0]; - my $id_agent = $_[1]; - my $dbh = $_[2]; - my $err; - - if (!defined($id_agent)){ - return -1; - } - - if ($id_agent != -1){ - my $intervalo = dame_intervalo ($pa_config, $id_agent, $dbh); - my $utimestamp = &UnixDate("today","%s"); - my $query2 = "INSERT INTO tagent_access (id_agent, utimestamp) VALUES ($id_agent,'$utimestamp')"; - $dbh->do($query2); - } + $dbh->disconnect(); } ########################################################################## -## SUB module_generic_proc (param_1, param_2, param_3) -## Procesa datos genericos sobre un proceso +## Return agent ID given the agent name. ########################################################################## -## param_1 : Nombre de la estructura contenedora de datos (XML) -## paran_2 : Timestamp del paquete de datos -## param_3 : Agent name -## param_4 : Module Type +sub get_agent_id ($$) { + my ($dbh, $agent_name) = @_; -sub module_generic_proc (%$$$$$) { - my $pa_config = $_[0]; - my $datos = $_[1]; - my $a_timestamp = $_[2]; - my $agent_name = $_[3]; - my $module_type = $_[4]; - my $dbh = $_[5]; - my $bUpdateDatos = 0; # added, patch submitted by Dassing - my $estado; - # Leemos datos de la estructura - my $a_datos = $datos->{data}->[0]; - - if ((ref($a_datos) eq "HASH")){ - $a_datos = 0; # If get bad data, then this is bad value, not "unknown" (> 1.3 version) - } else { - if (!is_numeric($a_datos)){ - $a_datos = 0; - } else { - $a_datos = sprintf("%.2f", $a_datos); # Two decimal float. We cannot store more - } - } # to change this, you need to change mysql structure - $a_datos =~ s/\,/\./g; # replace "," by "." avoiding locale problems - my $a_name = $datos->{name}->[0]; - my $a_desc = $datos->{description}->[0]; - my $a_max = $datos->{max}->[0]; - my $a_min = $datos->{min}->[0]; - - if (ref($a_max) eq "HASH") { - $a_max = ""; - } - if (ref($a_min) eq "HASH") { - $a_min = ""; - } - if (pandora_writedata ($pa_config, $a_timestamp, $agent_name, $module_type, $a_name, - $a_datos, $a_max, $a_min, $a_desc, $dbh, \$bUpdateDatos) != -1){ - pandora_writestate ($pa_config, $agent_name, $module_type, $a_name, $a_datos, $dbh, $bUpdateDatos); - } + my $rc = get_db_value ($dbh, "SELECT id_agente FROM tagente WHERE nombre = ? OR direccion = ?", $agent_name, $agent_name); + return defined ($rc) ? $rc : -1; } ########################################################################## -## SUB module_generic_data (param_1, param_2,param_3, param_4) -## Process generated data form numeric data module acquire +## Return server ID given the name of server. ########################################################################## -## param_1 : XML name -## paran_2 : Timestamp -## param_3 : Agent name -## param_4 : Module type (generic_data, async_data or network data) +sub get_server_id ($$$) { + my ($dbh, $server_name, $server_type) = @_; -sub module_generic_data (%$$$$$) { - my $pa_config = $_[0]; - my $datos = $_[1]; - my $m_timestamp = $_[2]; - my $agent_name = $_[3]; - my $module_type = $_[4]; - my $dbh = $_[5]; - - # Leemos datos de la estructura - my $m_name = $datos->{name}->[0]; - my $a_desc = $datos->{description}->[0]; - my $m_data = $datos->{data}->[0]; - - # Notes to improve module_generic_* functions. - # - # #1 checking for correct data should be made before calling writedata or writestate - # #2 a new procedure called return modulehash should detect if exists that module, - # create them, and always return a hash with agent needed information and module needed information - # #3 this hash should be used as parameter in writedata and writestate in order to have all needed - # information and don't need to ask again for the same data. At this time this code is very low and bad - # written, need to be optimized. - - my $bUpdateDatos = 0; # added, patch submitted by Dassing - if (ref($m_data) ne "HASH"){ - if (!is_numeric($m_data)){ - logger($pa_config, "(data) Invalid data (non-numeric) received from $agent_name, module $m_name", 3); - return -1; - } - if ($m_data =~ /[0-9]*/){ - $m_data =~ s/\,/\./g; # replace "," by "." - $m_data = sprintf("%.2f", $m_data);# Two decimal float. We cannot store more - } else { - $m_data =0; - } - $m_data =~ s/\,/\./g; # replace "," by "." - my $a_max = $datos->{max}->[0]; - my $a_min = $datos->{min}->[0]; - - if (ref($a_max) eq "HASH") { - $a_max = ""; - } - if (ref($a_min) eq "HASH") { - $a_min = ""; - } - if (pandora_writedata($pa_config, $m_timestamp,$agent_name,$module_type,$m_name,$m_data,$a_max,$a_min,$a_desc,$dbh,\$bUpdateDatos) != -1){ - pandora_writestate ($pa_config, $agent_name, $module_type, $m_name, $m_data, $dbh, $bUpdateDatos); - } - } else { - logger($pa_config, "(data) Invalid data value received from $agent_name, module $m_name", 3); - } + my $rc = get_db_value ($dbh, "SELECT id_server FROM tserver + WHERE name = ? AND server_type = ?", + $server_name, $server_type); + return defined ($rc) ? $rc : -1; } -########################################################################## -## SUB module_generic_data_inc (param_1, param_2,param_3, param_4) -## Process generated data form incremental numeric data module acquire -########################################################################## -## param_1 : XML name -## paran_2 : Timestamp -## param_3 : Agent name -## param_4 : Module type -sub module_generic_data_inc (%$$$$$) { - my $pa_config = $_[0]; - my $datos = $_[1]; - my $m_timestamp = $_[2]; - my $agent_name = $_[3]; - my $module_type = $_[4]; - my $dbh = $_[5]; - my $bUpdateDatos = 0; # added, patch submitted by Dassing - # Read structure data - my $m_name = $datos->{name}->[0]; - my $a_desc = $datos->{description}->[0]; - my $m_data = $datos->{data}->[0]; - my $a_max = $datos->{max}->[0]; - my $a_min = $datos->{min}->[0]; - if (is_numeric($m_data)){ - $m_data =~ s/\,/\./g; # replace "," by "." - $m_data = sprintf("%.2f", $m_data);# Two decimal float. We cannot store more - # to change this, you need to change mysql structure - $m_data =~ s/\,/\./g; # replace "," by "." - - if (!is_numeric($a_max)) { - $a_max = ""; - } - if (!is_numeric($a_min)) { - $a_min = ""; - } - # my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - # Algorith description: - # 1) Search prev. value in database - # 2) If new value is bigger than previous, store in tagente_datos differente between - # last value and actual value, and in aux. table tagente_datos_inc the last real value - # 3) If new data is lower than previous or no previous value (RESET), store 0 in tagente_datos and store - # real value in aux. table, replacing the old one - - # Obtemos los ID's a traves del paquete de datos - my $id_agente = dame_agente_id ($pa_config, $agent_name, $dbh); - my $id_modulo = dame_modulo_id ($pa_config, $module_type, $dbh); - my $id_agente_modulo = dame_agente_modulo_id($pa_config,$id_agente,$id_modulo,$m_name,$dbh); - - # Take last real data from tagente_datos_inc - # in this table, store the last real data, not the difference who its stored in tagente_datos table and - # tagente_estado table - - my $diferencia = 0; - my $no_existe = 0; - my $need_reset = 0; - my $need_update = 0; - my $new_data = 0; - my $data_anterior = 0; - my $timestamp_diferencia; - my $timestamp_anterior = 0; - my $m_utimestamp = &UnixDate ($m_timestamp, "%s"); - - # tagente_datos_inc do not store real data (if real data has any post-process, data is compared and - # stored in tagente_datos_inc with its original value). - - if (($id_agente_modulo == -1) && (dame_learnagente($pa_config, $id_agente, $dbh) eq "1" )) { - $id_agente_modulo = crea_agente_modulo ($pa_config, $agent_name, $module_type, $m_name, $a_max, $a_min, $a_desc, $dbh); - $no_existe = 1; - } else { - my $query_idag = "SELECT * FROM tagente_datos_inc WHERE id_agente_modulo = $id_agente_modulo"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag->execute; - if ($s_idag->rows == 0) { - # Does not exists entry in tagente_datos_inc yet - $no_existe = 1; - } else { - my @data_row = $s_idag->fetchrow_array(); - if (is_numeric($data_row[2])){ - $data_anterior = $data_row[2]; - } - if (is_numeric($data_row[3])){ - $timestamp_anterior = $data_row[3]; - } - $diferencia = $m_data - $data_anterior; - $timestamp_diferencia = $m_utimestamp - $timestamp_anterior; - # get seconds between last data and this data - if (($timestamp_diferencia > 0) && ($diferencia > 0)) { - $diferencia = $diferencia / $timestamp_diferencia; - } - if ($diferencia < 0 ){ - $need_reset = 1; - } - } - $s_idag -> finish(); - } - - # Update of tagente_datos_inx (AUX TABLE) - if ($no_existe == 1){ - my $query = "INSERT INTO tagente_datos_inc (id_agente_modulo,datos, utimestamp) VALUES ($id_agente_modulo, '$m_data', $m_utimestamp)"; - $dbh->do($query); - } else { - # Data exists previously - if ($diferencia != 0) { - my $query2 = "UPDATE tagente_datos_inc SET utimestamp = $m_utimestamp, datos = '$m_data' WHERE id_agente_modulo = $id_agente_modulo"; - $dbh->do($query2); - } - } - - if ($diferencia >= 0) { - $new_data = $diferencia; - } - - # Update of tagente_datos and tagente_estado ? (only where there is a difference (or reset)) - if ($no_existe == 0){ - if (pandora_writedata ($pa_config, $m_timestamp, $agent_name, $module_type, $m_name, $new_data, $a_max, $a_min, $a_desc, $dbh, \$bUpdateDatos) != -1){ - # Inc status is always 100 (N/A) - pandora_writestate ($pa_config, $agent_name, $module_type, $m_name, $new_data, $dbh, $bUpdateDatos); - } - } - } else { - logger ($pa_config, "(data_inc) Invalid data received from $agent_name, module $m_name", 2); - } -} - - -########################################################################## -## SUB module_generic_data (param_1, param_2,param_3, param_4) -## Process generated data form alfanumeric data module acquire -########################################################################## -## param_1 : XML name -## paran_2 : Timestamp -## param_3 : Agent name -## param_4 : Module type - -sub module_generic_data_string (%$$$$$) { - my $pa_config = $_[0]; - my $datos = $_[1]; - my $m_timestamp = $_[2]; - my $agent_name = $_[3]; - my $module_type = $_[4]; - my $dbh = $_[5]; - my $bUpdateDatos = 0; # added, patch submitted by Dassing - # Read Structure - my $m_name = $datos->{name}->[0]; - my $m_data = $datos->{data}->[0]; - my $a_desc = $datos->{description}->[0]; - my $a_max = $datos->{max}->[0]; - my $a_min = $datos->{min}->[0]; - if (ref($m_data) eq "HASH") { - $m_data = XMLout($m_data, RootName=>undef); - } - if (ref($a_max) eq "HASH") { - $a_max = ""; - } - if (ref($a_min) eq "HASH") { - $a_min = ""; - } - if (pandora_writedata($pa_config, $m_timestamp, $agent_name, $module_type, $m_name, $m_data, $a_max, $a_min, $a_desc, $dbh, \$bUpdateDatos) != -1){ - # String type has no state (100 = N/A) - pandora_writestate ($pa_config, $agent_name, $module_type, $m_name, $m_data, $dbh, $bUpdateDatos); - } -} - - -########################################################################## -## SUB pandora_writedata (pa_config, timestamp,nombre_agente,tipo_modulo, -# nombre_modulo, datos, max, min, descripcion, dbh, update) -## Insert data in main table: tagente_datos - -########################################################################## - -# Optimizations: -# Pass id_agent, and id_agent_module as parameters -# Separate detection of existance of tagente_modulo before calling this function -# Pass Entire hash reference with tagente_modulo record - -sub pandora_writedata (%$$$$$$$$$$){ - my $pa_config = $_[0]; - my $timestamp = $_[1]; - my $nombre_agente = $_[2]; - my $tipo_modulo = $_[3]; - my $nombre_modulo = $_[4]; - my $datos = $_[5]; - my $max = $_[6]; - my $min = $_[7]; - my $descripcion = $_[8]; - my $dbh = $_[9]; - my $Ref_bUpdateDatos = $_[10]; - - my @data; - - if (!defined($max)){ - $max = "0"; - } - if (!defined($min)){ - $min = "0"; - } - - # Obtenemos los identificadores - my $id_agente = dame_agente_id ($pa_config, $nombre_agente,$dbh); - - # Export module data if necessary - export_module_data ($id_agente, $nombre_agente, $nombre_modulo, $tipo_modulo, $datos, $timestamp, $dbh); - - # Check if exists module and agent_module reference in DB, - # if not, and learn mode activated, insert module in DB - if ($id_agente eq "-1"){ - return -1; - } - - my $id_modulo = dame_modulo_id($pa_config, $tipo_modulo,$dbh); - if (($id_modulo == 3) || ($id_modulo == 17) || ($id_modulo == 10) || ($id_modulo == 23)){ - if ($datos eq "") { - return -1; - } - } - - my $id_agente_modulo = dame_agente_modulo_id($pa_config, $id_agente, $id_modulo, $nombre_modulo,$dbh); - # Pandora 1.3. Now uses integer to store timestamp in datatables - # much more faster to do comparations... - my $utimestamp; # integer version of timestamp - $utimestamp = &UnixDate($timestamp,"%s"); # convert from human to integer - if (! defined($utimestamp)){ # If problems getting timestamp data - $utimestamp = &UnixDate("today","%s"); - } - my $needscreate = 0; - - # take max and min values for this id_agente_module - if ($id_agente_modulo != -1){ # ID AgenteModulo does exists - my $agent_module_row = get_db_free_row ("SELECT * FROM tagente_modulo WHERE id_agente_modulo = $id_agente_modulo", $dbh); - - # Postprocess and get MAX/MIN only for numeric moduletypes - - if (($id_modulo != 3) && ($id_modulo != 17) && ($id_modulo != 10) && ($id_modulo != 23)){ - $max = $agent_module_row->{'max'}; - $min = $agent_module_row->{'min'}; - - # Postprocess - if ((defined($agent_module_row->{'post_process'})) && ($agent_module_row->{'post_process'} != 0) && (is_numeric($agent_module_row->{'post_process'}))){ - $datos = $datos * $agent_module_row->{'post_process'}; - } - } else { - $max = ""; - $min = ""; - } - - # history_data detection. If this module don't have history, exit from here now with returncode 0 - - if ($agent_module_row->{'history_data'} == 0){ - if ( defined $Ref_bUpdateDatos ) { - $$Ref_bUpdateDatos = 1; # TODO: Make check of status change here. - } - return 0; - } - - } else { # Id AgenteModulo DOESNT exist, it could need to be created... - if (dame_learnagente($pa_config, $id_agente, $dbh) eq "1" ){ - # Try to write a module and agent_module definition for that datablock - logger( $pa_config, "Pandora_insertdata will create module (learnmode) for agent $nombre_agente",6); - $id_agente_modulo = crea_agente_modulo ($pa_config, $nombre_agente, $tipo_modulo, $nombre_modulo, $max, $min, $descripcion, $dbh); - $needscreate = 1; # Really needs to be created - } else { - logger( $pa_config, "VERBOSE: pandora_insertdata cannot find module definition ($nombre_modulo / $tipo_modulo )for agent $nombre_agente - Use LEARN MODE for autocreate.", 3); - return -1; - } - } # Module exists or has been created - - # Check old value for this data in tagente_data - # if old value nonequal to new value, needs update - my $query; - my $needsupdate =0; - - $query = "SELECT * FROM tagente_estado WHERE id_agente_modulo = $id_agente_modulo"; - my $sql_oldvalue = $dbh->prepare($query); - $sql_oldvalue->execute; - @data = $sql_oldvalue->fetchrow_array(); - $sql_oldvalue = $dbh->prepare($query); - $sql_oldvalue->execute; - if ($sql_oldvalue->rows != 0) { - @data = $sql_oldvalue->fetchrow_array(); - #$data[2] contains data - # Transform data (numeric types only) - if ($tipo_modulo =~ /string/){ - $datos = $datos; # No change - } else { # Numeric change to real - $datos =~ s/\,/\./g; # replace "," by "." - $data[2] =~ s/\,/\./g; # replace "," by "." - $datos = sprintf("%.2f", $datos); - if (is_numeric($data[2])){ - $data[2] = sprintf("%.2f", $data[2]); - } - if (is_numeric($datos)){ - $datos = sprintf("%.2f", $datos); - } - # Two decimal float. We cannot store more - # to change this, you need to change mysql structure - } - - # Detect changes between stored data and adquired data. - if ($data[2] ne $datos){ - $needsupdate = 1; - } else { - # Data in DB is the same, but could be older (more than 1 - # day ). Should check this against last_try field, who is - # updated only when new data is stored or each 24 hours - my $fecha_datos = $data[7]; # last_try - my $fecha_mysql = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $fecha_actual = ParseDate( $fecha_mysql ); - my $fecha_flag; - my $err; - my $fecha_limite = DateCalc($fecha_actual,"- 1 days",\$err); - $fecha_flag = Date_Cmp ($fecha_limite, $fecha_datos); - if ($fecha_flag >= 0) { # write data, - logger( $pa_config, "Too old data stored (>24Hr). Updating data for $nombre_modulo",5); - $needsupdate = 1; - } - } - } else { - $needsupdate = 1; # There aren't data - logger( $pa_config, "Updating data for $nombre_modulo, because there are not data in DB ",10); - } - - $sql_oldvalue->finish(); - if (($needscreate == 1) || ($needsupdate == 1)){ - my $outlimit = 0; - # Patch submitted by Dassing. - if ( defined $Ref_bUpdateDatos ) { - $$Ref_bUpdateDatos = 1; # true - } - if ($tipo_modulo =~ /string/) { # String module types - $datos = $dbh->quote($datos); - $timestamp = $dbh->quote($timestamp); - # Parse data entry for adecuate SQL representation. - $query = "INSERT INTO tagente_datos_string (id_agente_modulo, datos, utimestamp) VALUES ($id_agente_modulo, $datos, $utimestamp)"; - } elsif (is_numeric($datos)){ - if ($max != $min) { - if (int($datos) > $max) { - $datos = $max; - $outlimit=1; - logger($pa_config,"DEBUG: MAX Value reached ($max) for agent $nombre_agente / $nombre_modulo",6); - } - if (int($datos) < $min) { - $datos = $min; - $outlimit = 1; - logger($pa_config, "DEBUG: MIN Value reached ($min) for agent $nombre_agente / $nombre_modulo",6); - } - } - $datos = $dbh->quote($datos); - $timestamp = $dbh->quote($timestamp); - # Parse data entry for adecuate SQL representation. - $query = "INSERT INTO tagente_datos (id_agente_modulo, datos, utimestamp) VALUES ($id_agente_modulo, $datos, $utimestamp)"; - } - # If data is out of limits, do not insert into database - if ($outlimit == 0){ - logger($pa_config, "DEBUG: pandora_insertdata Calculado id_agente_modulo a $id_agente_modulo",6); - logger($pa_config, "DEBUG: pandora_insertdata SQL : $query",10); - $dbh->do($query); # Makes insertion in database - } - } - return 0; -} - -########################################################################## -## SUB pandora_serverkeepalive (pa_config, status, dbh) -## Update server status -########################################################################## -sub pandora_serverkeepaliver (%$$) { - my $pa_config= $_[0]; - my $opmode = $_[1]; # 0 dataserver, 1 network server, 2 snmp console - # 3 recon srv, 4 plugin srv, 5 prediction srv - # 6 WMI server - my $dbh = $_[2]; - my $version_data; - my $pandorasuffix; - my @data; - my $err; - - # First of all, update our keepalive - pandora_updateserver ($pa_config, $pa_config->{'servername'}, 1, $opmode, $dbh); - - my $temp = $pa_config->{"keepalive"} - $pa_config->{"server_threshold"}; - - if ($temp <= 0){ - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - $temp = $pa_config->{"keepalive_orig"} * 2; # Down if keepalive x 2 seconds unknown - my $fecha_limite = DateCalc($timestamp,"- $temp seconds",\$err); - $fecha_limite = &UnixDate($fecha_limite,"%Y-%m-%d %H:%M:%S"); - - my $query_idag = "SELECT * FROM tserver WHERE status = 1 AND keepalive < '$fecha_limite'"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows != 0) { - while (@data = $s_idag->fetchrow_array()){ - if ($data[3] != 0){ # only if it's currently not down - # Update server data - $version_data = $pa_config->{"version"}." (P) ".$pa_config->{"build"}; - my $sql_update = "UPDATE tserver SET status = 0, version = '".$version_data."' WHERE id_server = $data[0]"; - $dbh->do($sql_update); - - pandora_event ($pa_config, "Server ".$data[1]." going Down", 0, 0, 4, 0, 0, "system", $dbh); - logger( $pa_config, "Server ".$data[1]." going Down ", 1); - } - } - } - $s_idag->finish(); - $pa_config->{"keepalive"} = $pa_config->{"keepalive_orig"}; - } else { - $pa_config->{"keepalive"} = $pa_config->{"keepalive"} - $pa_config->{"server_threshold"}; - } -} - -########################################################################## -## SUB pandora_planned_downtime (pa_config, dbh) -## Update planned downtimes. -########################################################################## -sub pandora_planned_downtime (%$) { - my $pa_config= $_[0]; - my $dbh = $_[1]; - - my $data_ref; - my $data_ref2; - my $query_handle; - my $query_handle2; - my $query_sql; - - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp; # integer version of timestamp - $utimestamp = &UnixDate($timestamp,"%s"); # convert from - - # Activate a planned downtime: Set agents as disabled for Planned Downtime - - $query_sql = "SELECT * FROM tplanned_downtime WHERE executed = 0 AND date_from <= $utimestamp AND date_to >= $utimestamp"; - - $query_handle = $dbh->prepare($query_sql); - $query_handle ->execute; - if ($query_handle->rows != 0) { - while ($data_ref = $query_handle->fetchrow_hashref()){ - # Raise event in system to notify planned downtime has started. - $dbh->do("UPDATE tplanned_downtime SET executed=1 WHERE id = ".$data_ref->{'id'}); - pandora_event ($pa_config, "Server ".$pa_config->{'servername'}." started planned downtime: ".$data_ref->{'description'}, 0, 0, 1, 0, 0, "system", $dbh); - $query_sql = "SELECT * FROM tplanned_downtime_agents WHERE id_downtime = ".$data_ref->{'id'}; - $query_handle2 = $dbh->prepare($query_sql); - $query_handle2 ->execute; - if ($query_handle2->rows != 0) { - while ($data_ref2 = $query_handle2->fetchrow_hashref()){ - $dbh->do("UPDATE tagente SET disabled=1 WHERE id_agente = ".$data_ref2->{'id_agent'}); - } - } - $query_handle2->finish(); - } - } - $query_handle->finish(); - - - # Deactivate a planned downtime: Set agents as disabled for Planned Downtime - - $query_sql = "SELECT * FROM tplanned_downtime WHERE executed = 1 AND date_to <= $utimestamp"; - $query_handle = $dbh->prepare($query_sql); - $query_handle ->execute; - if ($query_handle->rows != 0) { - while ($data_ref = $query_handle->fetchrow_hashref()){ - # Raise event in system to notify planned downtime has started. - $dbh->do("UPDATE tplanned_downtime SET executed=0 WHERE id = ".$data_ref->{'id'}); - pandora_event ($pa_config, "Server ".$pa_config->{'servername'}." stopped planned downtime: ".$data_ref->{'description'}, 0, 0, 1, 0, 0, "system", $dbh); - $query_sql = "SELECT * FROM tplanned_downtime_agents WHERE id_downtime = ".$data_ref->{'id'}; - $query_handle2 = $dbh->prepare($query_sql); - $query_handle2 ->execute; - if ($query_handle2->rows != 0) { - while ($data_ref2 = $query_handle2->fetchrow_hashref()){ - $dbh->do("UPDATE tagente SET disabled=0 WHERE id_agente = ".$data_ref2->{'id_agent'}); - } - } - $query_handle2->finish(); - } - } - $query_handle->finish(); -} - - -########################################################################## -## SUB pandora_updateserver (pa_config, status, dbh) -## Update server status -########################################################################## -sub pandora_updateserver (%$$$) { - my $pa_config= $_[0]; - my $servername = $_[1]; - my $status = $_[2]; - my $opmode = $_[3]; # 0 dataserver, 1 network server, 2 snmp console, 3 recon - # 4 plugin, 5 prediction, 6 wmi - my $dbh = $_[4]; - - - my $sql_update; - my $sql_update_post; - my $pandorasuffix; - my $version_data; - - if ($opmode == 0){ - $pandorasuffix = "_Data"; - } elsif ($opmode == 1){ - $pandorasuffix = "_Net"; - } elsif ($opmode == 2){ - $pandorasuffix = "_SNMP"; - } elsif ($opmode == 3){ - $pandorasuffix = "_Recon"; - } elsif ($opmode == 4){ - $pandorasuffix = "_Plugin"; - } elsif ($opmode == 5){ - $pandorasuffix = "_Prediction"; - } elsif ($opmode == 6){ - $pandorasuffix = "_WMI"; - } elsif ($opmode == 7){ - $pandorasuffix = "_Export"; - } elsif ($opmode == 8){ - $pandorasuffix = "_Inventory"; - } else { - logger ($pa_config, "Error: received a unknown server type. Aborting startup.",0); - print (" [ERROR] Received a unknown server type. Aborting startup \n\n"); - exit; - } - - $sql_update_post = ""; - - my $id_server = dame_server_id($pa_config, $servername.$pandorasuffix, $dbh); - if ($id_server == -1){ - # Must create a server entry - $version_data = $pa_config->{"version"}." (P) ".$pa_config->{"build"}; - my $sql_server = "INSERT INTO tserver (name,description,version) VALUES ('$servername".$pandorasuffix."','Autocreated at startup','$version_data')"; - $dbh->do($sql_server); - $id_server = dame_server_id($pa_config, $pa_config->{'servername'}.$pandorasuffix, $dbh); - } - my @data; - my $query_idag = "SELECT * FROM tserver WHERE id_server = $id_server"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows != 0) { - if (@data = $s_idag->fetchrow_array()){ - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - # Update server data - $version_data = $pa_config->{"version"}." (P) ".$pa_config->{"build"}; - - # Some fields of tserver should be updated ONLY when server is going up - if ($data[3] == 0){ # If down, update to get up the server - logger( $pa_config, "Server ".$data[1]." going UP ",1); - $sql_update_post = ", laststart = '$timestamp', version = '$version_data'"; - } - - if ($opmode == 0){ - $sql_update = "data_server = 1"; - } elsif ($opmode == 1){ - $sql_update = "network_server = 1"; - } elsif ($opmode == 2) { - $sql_update = "snmp_server = 1"; - } elsif ($opmode == 3) { - $sql_update = "recon_server = 1"; - } elsif ($opmode == 4) { - $sql_update = "plugin_server = 1"; - } elsif ($opmode == 5) { - $sql_update = "prediction_server = 1"; - } elsif ($opmode == 6) { - $sql_update = "wmi_server = 1"; - } elsif ($opmode == 7) { - $sql_update = "export_server = 1"; - } elsif ($opmode == 8) { - $sql_update = "inventory_server = 1"; - } - - $sql_update = "UPDATE tserver SET $sql_update $sql_update_post , status = $status, keepalive = '$timestamp', master = $pa_config->{'pandora_master'} WHERE id_server = $id_server"; - - $dbh->do($sql_update); - } - $s_idag->finish(); - } -} - -########################################################################## -## SUB pandora_lastagentcontact (pa_config, timestamp,nombre_agente,os_data, agent_version,interval,dbh) -## Update last contact field in Agent Table -########################################################################## - -sub pandora_lastagentcontact (%$$$$$$) { - my $pa_config= $_[0]; - my $timestamp = $_[1]; - my $time_now = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $nombre_agente = $_[2]; - my $os_data = $_[3]; - my $agent_version = $_[4]; - my $interval = $_[5]; - my $dbh = $_[6]; - - my $id_agente = dame_agente_id($pa_config, $nombre_agente,$dbh); - pandora_accessupdate ($pa_config, $id_agente, $dbh); - my $query = ""; - if ($interval == -1){ # no update for interval field (some old agents doest support it) - $query = "UPDATE tagente SET agent_version = '$agent_version', ultimo_contacto_remoto = '$timestamp', ultimo_contacto = '$time_now', os_version = '$os_data' WHERE id_agente = $id_agente"; - } else { - $query = "UPDATE tagente SET intervalo = $interval, agent_version = '$agent_version', ultimo_contacto_remoto = '$timestamp', ultimo_contacto = '$time_now', os_version = '$os_data' WHERE id_agente = $id_agente"; - } - logger( $pa_config, "pandora_lastagentcontact: Updating Agent last contact data for $nombre_agente",6); - logger( $pa_config, "pandora_lastagentcontact: SQL Query: ".$query, 10); - db_do ($query, $dbh); - -} - -########################################################################## -## SUB update_keepalive_module -## -## Updates keepalive module from one agent. This only should be called -## when processing an agent, only one time, and not to be used on network -## or other agentless modules -########################################################################## - -sub update_keepalive_module (%$) { - my $pa_config= $_[0]; - my $id_agent = $_[1]; - my $agent_name = $_[2]; - my $dbh = $_[3]; - - # Update keepalive module - # if present, if there is more than one (nosense), only updates first one! - my $id_agent_module = get_db_free_field ("SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = $id_agent AND id_tipo_modulo = 100", $dbh); - if ($id_agent_module ne -1){ - my $module_typename = "keep_alive"; - my $module_name = get_db_free_field ("SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = $id_agent_module", $dbh); - pandora_writestate ($pa_config, $agent_name, $module_typename, $module_name, 1, $dbh, 1); - } -} - -########################################################################## -## SUB pandora_incident (pa_config, dbh, title, text, priority, status, origin, id_group -## Write in internal incident management system -########################################################################## - -sub pandora_create_incident (%$$$$$$$) { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $title = $_[2]; - my $text = $_[3]; - my $priority = $_[4]; - my $status = $_[5]; - my $origin = $_[6]; - my $id_group = $_[7]; - my $sql = "INSERT INTO tincidencia (inicio, titulo, descripcion, origen, estado, prioridad, id_grupo) VALUES (NOW(), '$title', '$text', '$origin', $status, $priority, $id_group)"; - $dbh->do($sql); -} - - -########################################################################## -## SUB pandora_audit (pa_config, escription, name, action, pandora_dbcfg_hash) -## Write in internal audit system an entry. -########################################################################## -sub pandora_audit (%$$$$) { - my $pa_config = $_[0]; - my $desc = $_[1]; - my $name = $_[2]; - my $action = $_[3]; - my $dbh = $_[4]; - my $local_dbh =0; - - # In startup audit, DBH not passed - if (! defined($dbh)){ - $local_dbh = 1; - $dbh = DBI->connect("DBI:mysql:$pa_config->{'dbname'}:$pa_config->{'dbhost'}:3306", $pa_config->{'dbuser'}, $pa_config->{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); - } - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp; # integer version of timestamp - $utimestamp = &UnixDate($timestamp,"%s"); # convert from human to integer - - my $query = "insert into tsesion (ID_usuario, IP_origen, accion, fecha, descripcion, utimestamp) values ('SYSTEM','".$name."','".$action."','".$timestamp."','".$desc."', $utimestamp)"; - eval { # Check for problems in Database, if cannot audit, break execution - $dbh->do($query); - }; - if ($@){ - logger ($pa_config,"FATAL: pandora_audit() cannot connect with database",0); - logger ($pa_config,"FATAL: Error code $@", 0); - } - if ($local_dbh == 1){ - $dbh->disconnect(); - } -} - -########################################################################## -## SUB dame_agente_id (nombre_agente) -## Return agent ID, use "nombre_agente" as name of agent. -########################################################################## -sub dame_agente_id (%$$) { - my $pa_config = $_[0]; - my $agent_name = $_[1]; - my $dbh = $_[2]; - - if ( (defined($agent_name)) && ($agent_name ne "") ){ - my $id_agente; - my @data; - $agent_name = sqlWrap ($agent_name); - # Calculate agent ID using select by its name - my $query_idag = "SELECT id_agente FROM tagente WHERE nombre = $agent_name OR direccion = $agent_name"; # Fixed 080108 by anon (used on snmpconsole...). - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger ($pa_config, "ERROR dame_agente_id(): Cannot find agent called $agent_name. Returning -1", 5); - logger ($pa_config, "ERROR: SQL Query is $query_idag ",10); - $id_agente = -1; - } else { - @data = $s_idag->fetchrow_array(); - $id_agente = $data[0]; - } - $s_idag->finish(); - return $id_agente; - } else { - return -1; - } -} - -########################################################################## -## SUB dame_server_id (pa_config, servername, dbh) -## Return serverID, using "nane" as name of server -########################################################################## -sub dame_server_id (%$$) { - my $pa_config = $_[0]; - my $name = $_[1]; - my $dbh = $_[2]; - - my $id_server; - my @data; - - # Get serverid - my $query_idag = "SELECT * FROM tserver WHERE name = '$name' "; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - - if ($s_idag->rows == 0) { - logger ($pa_config, "ERROR dame_server_id(): Cannot find server called $name. Returning -1", 5); - logger ($pa_config, "ERROR: SQL Query is $query_idag ",10); - $data[0]=-1; - } else { - @data = $s_idag->fetchrow_array(); - } - - $id_server = $data[0]; - $s_idag->finish(); - return $id_server; -} - -########################################################################## -## SUB give_networkserver_status (id_server) -## Return NETWORK server status given its id -########################################################################## - -sub give_networkserver_status (%$$) { - my $pa_config = $_[0]; - my $id_server = $_[1]; - my $dbh = $_[2]; - - my $status; - my @data; - my $query_idag = "select * from tserver where id_server = $id_server and network_server = 1"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - $status = -1; - } else { - @data = $s_idag->fetchrow_array(); - $status = $data[3]; - } - $s_idag->finish(); - return $status; -} - -########################################################################## -## SUB dame_grupo_agente (id_agente) -## Return id_group of an agent given its id -########################################################################## - -sub dame_grupo_agente (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; - - my $id_grupo; - my @data; - # Calculate agent using select by its id - my $query_idag = "SELECT id_grupo FROM tagente WHERE id_agente = $id_agente"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger ($pa_config, "ERROR dame_grupo_agente(): Cannot find agent with id $id_agente", 5); - logger ($pa_config, "ERROR: SQL Query is $query_idag ",10); - } else { @data = $s_idag->fetchrow_array(); } - $id_grupo = $data[0]; - $s_idag->finish(); - return $id_grupo; -} - -########################################################################## -## SUB dame_comando_alerta (id_alerta) -## Return agent ID, use "nombre_agente" as name of agent. -########################################################################## -sub dame_comando_alerta (%$$) { - my $pa_config = $_[0]; - my $id_alerta = $_[1]; - my $dbh = $_[2]; - - my @data; - # Calculate agent ID using select by its name - my $query_idag = "select * from talerta where id_alerta = $id_alerta"; - my $s_idag = $dbh->prepare($query_idag); - my $comando = ""; - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger ($pa_config, "ERROR dame_comando_alerta(): Cannot find alert $id_alerta", 5); - logger ($pa_config, "ERROR: SQL Query is $query_idag ", 10); - } else { - @data = $s_idag->fetchrow_array(); - $comando = $data[2]; - } - $s_idag->finish(); - return $comando; -} - - ########################################################################## ## SUB dame_agente_nombre (id_agente) ## Return agent name, given "id_agente" ########################################################################## -sub dame_agente_nombre (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; - - my $nombre_agente; - my @data; - # Calculate agent ID using select by its name - my $query_idag = "SELECT nombre FROM tagente WHERE id_agente = '$id_agente'"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_agente_nombre(): Cannot find agent with id $id_agente",4); - logger($pa_config, "ERROR: SQL Query is $query_idag ",10); - } else { - @data = $s_idag->fetchrow_array(); - } - $nombre_agente = $data[0]; - $s_idag->finish(); - return $nombre_agente; -} - -########################################################################## -## SUB give_group_disabled (pa_config, id_group, dbh) -## Return disabled field from tgrupo table given a id_grupo -########################################################################## -sub give_group_disabled (%$$) { - my $pa_config = $_[0]; - my $id_group = $_[1]; - my $dbh = $_[2]; - - my $disabled = 0; - my @data; - my $query_idag = "SELECT disabled FROM tgrupo WHERE id_grupo = '$id_group'"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR give_group_disabled(): Cannot find group id $id_group",2); - logger($pa_config, "ERROR: SQL Query is $query_idag ",10); - } else { - @data = $s_idag->fetchrow_array(); - $disabled = $data[0]; - } - $s_idag->finish(); - return $disabled; -} - -########################################################################## -## SUB dame_modulo_id (nombre_modulo) -## Return module ID, given "nombre_modulo" as module name -########################################################################## -sub dame_modulo_id (%$$) { - my $pa_config = $_[0]; - my $nombre_modulo = $_[1]; - my $dbh = $_[2]; - - my $id_modulo; my @data; - # Calculate agent ID using select by its name - my $query_idag = "select * from ttipo_modulo where nombre = '$nombre_modulo'"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_modulo_id(): Cannot find module called $nombre_modulo ",1); - logger($pa_config, "ERROR: SQL Query is $query_idag ",2); - $id_modulo = 0; - } else { - @data = $s_idag->fetchrow_array(); - $id_modulo = $data[0]; - } - $s_idag->finish(); - return $id_modulo; -} - -########################################################################## -## SUB dame_agente_modulo_id (id_agente, id_tipomodulo, nombre) -## Return agente_modulo ID, from tabla tagente_modulo, -## given id_agente, id_tipomodulo and name -########################################################################## -sub dame_agente_modulo_id (%$$$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $id_tipomodulo = $_[2]; - my $name = $_[3]; - my $dbh = $_[4]; - my $id_agentemodulo; - my @data; - - # Sanity checks - if (!defined($name)){ - return -1; - } - if (!defined($id_agente) || ($id_agente < 0)){ - return -1; - } - - # Calculate agent ID using select by its name - my $query_idag = "select * from tagente_modulo where id_agente = '$id_agente' and id_tipo_modulo = '$id_tipomodulo' and nombre = '$name'"; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger ($pa_config, "ERROR dame_agente_modulo_id(): Cannot find a module called $name", 5); - logger ($pa_config, "ERROR: SQL Query is $query_idag ", 10); - $id_agentemodulo = -1; - } else { - @data = $s_idag->fetchrow_array(); - $id_agentemodulo = $data[0]; - } - $s_idag->finish(); - return $id_agentemodulo; -} - -########################################################################## -## SUB dame_nombreagente_agentemodulo (id_agente_modulo) -## Return agent name diven id_agente_modulo -########################################################################## -sub dame_nombreagente_agentemodulo (%$$) { - my $pa_config = $_[0]; - my $id_agentemodulo = $_[1]; - my $dbh = $_[2]; - - my $id_agente; my @data; - # Calculate agent ID using select by its name - my $query_idag = "SELECT id_agente FROM tagente_modulo WHERE id_agente_modulo = ".$id_agentemodulo; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_nombreagente_agentemodulo(): Cannot find id_agente_modulo $id_agentemodulo",3); - logger($pa_config, "ERROR: SQL Query is $query_idag ",10); - $id_agente = -1; - } else { - @data = $s_idag->fetchrow_array(); - $id_agente= $data[0]; - } - $s_idag->finish(); - my $nombre_agente = dame_agente_nombre ($pa_config, $id_agente, $dbh); - return $nombre_agente; -} - -########################################################################## -## SUB dame_nombretipomodulo_idtipomodulo (id_tipo_modulo) -## Return name of moduletype given id_tipo_modulo -########################################################################## -sub dame_nombretipomodulo_idagentemodulo (%$$) { - my $pa_config = $_[0]; - my $id_tipomodulo = $_[1]; - my $dbh = $_[2]; - my @data; - # Calculate agent ID using select by its name - my $query_idag = "select * from ttipo_modulo where id_tipo = ".$id_tipomodulo; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger( $pa_config, "ERROR dame_nombreagente_agentemodulo(): Cannot find module type with ID $id_tipomodulo",1); - logger( $pa_config, "ERROR: SQL Query is $query_idag ",2); - } else { @data = $s_idag->fetchrow_array(); } - my $tipo = $data[1]; - $s_idag->finish(); - return $tipo; -} - -########################################################################## -## SUB dame_learnagente (id_agente) -## Return 1 if agent is in learn mode, 0 if not -########################################################################## -sub dame_learnagente (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; - my @data; - - # Calculate agent ID using select by its name - my $query = "select * from tagente where id_agente = ".$id_agente; - my $s_idag = $dbh->prepare($query); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger( $pa_config, "ERROR dame_learnagente(): Cannot find agente $id_agente",2); - logger( $pa_config, "ERROR: SQL Query is $query ",2); - return 0; - } else { - @data = $s_idag->fetchrow_array(); - my $learn= $data[6]; - $s_idag->finish(); - return $learn; - } -} - - -########################################################################## -## SUB dame_id_tipo_modulo (id_agente_modulo) -## Return id_tipo of module with id_agente_modulo -########################################################################## -sub dame_id_tipo_modulo (%$$) { - my $pa_config = $_[0]; - my $id_agente_modulo = $_[1]; - my $dbh = $_[2]; - my $tipo; my @data; - # Calculate agent ID using select by its name - my $query_idag = "SELECT * FROM tagente_modulo WHERE id_agente_modulo = ".$id_agente_modulo; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_id_tipo_modulo(): Cannot find id_agente_modulo $id_agente_modulo", 4); - logger($pa_config, "ERROR: SQL Query is $query_idag ", 10); - $tipo = "-1"; - } else { - @data = $s_idag->fetchrow_array(); - $tipo= $data[2]; - } - $s_idag->finish(); - return $tipo; -} -########################################################################## -## SUB give_network_component_profile_name ($pa_config, $dbh, $task_ncprofile) -## Return network component profile name, given it's id -########################################################################## -sub give_network_component_profile_name (%$$) { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $id_np = $_[2]; +sub get_agent_name ($$) { + my ($dbh, $agent_id) = @_; - my $tipo; my @data; - my $query_idag = "SELECT * FROM tnetwork_profile WHERE id_np = ".$id_np; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR give_network_component_profile_name(): Cannot find network profile $id_np",1); - logger($pa_config, "ERROR: SQL Query is $query_idag ",2); - $tipo = 0; - } else { @data = $s_idag->fetchrow_array(); } - $tipo = $data[1]; - $s_idag->finish(); - return $tipo; + return get_db_value ($dbh, "SELECT nombre FROM tagente WHERE id_agente = ?", $agent_id); } ########################################################################## -## SUB dame_intervalo (id_agente) -## Return interval for id_agente +## Returns true if the given group is disabled, false otherwise. ########################################################################## -sub dame_intervalo (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; +sub is_group_disabled ($$) { + my ($dbh, $group_id) = @_; - my $tipo = 0; - my @data; - - if (!defined($id_agente)){ - return 0; - } - - # Calculate agent ID using select by its name - my $query_idag = "select * from tagente where id_agente = ".$id_agente; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_intervalo(): Cannot find agente $id_agente",1); - logger($pa_config, "ERROR: SQL Query is $query_idag ",2); - $tipo = 0; - } else { - @data = $s_idag->fetchrow_array(); - $tipo= $data[7]; - } - $s_idag->finish(); - return $tipo; + return get_db_value ($dbh, "SELECT disabled FROM tgrupo WHERE id_grupo = ?", $group_id); } ########################################################################## -## SUB dame_desactivado (id_agente) -## Return disabled = 1 if disabled, 0 if not disabled +## Return module ID given the module name. ########################################################################## -sub dame_desactivado (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; - my $desactivado; +sub get_module_id ($$) { + my ($dbh, $module_name) = @_; - my $tipo; my @data; - # Calculate agent ID using select by its name - my $query_idag = "select * from tagente where id_agente = ".$id_agente; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_desactivado(): Cannot find agente $id_agente",4); - logger($pa_config, "ERROR: SQL Query is $query_idag ",10); - $desactivado = -1; - } else { - @data = $s_idag->fetchrow_array(); - $desactivado= $data[12]; + my $rc = get_db_value ($dbh, "SELECT id_tipo FROM ttipo_modulo WHERE nombre = ?", $module_name); + return defined ($rc) ? $rc : -1; +} + +########################################################################## +## Return a network component's profile name given it's ID. +########################################################################## +sub get_nc_profile_name ($$) { + my ($dbh, $nc_id) = @_; + + return get_db_value ($dbh, "SELECT * FROM tnetwork_profile WHERE id_np = ?", $nc_id); +} + +########################################################################## +## Get a single column returned by an SQL query as a hash reference. +########################################################################## +sub get_db_value ($$;@) { + my ($dbh, $query, @values) = @_; + + # Cache statements + my $sth = $dbh->prepare_cached($query); + $sth->execute(@values); + + # No results + if ($sth->rows == 0) { + $sth->finish(); + return undef; + } + + my $row = $sth->fetchrow_arrayref(); + $sth->finish(); + return $row->[0]; +} + +########################################################################## +## Get a single row returned by an SQL query as a hash reference. Returns +## -1 on error. +########################################################################## +sub get_db_single_row ($$;@) { + my ($dbh, $query, @values) = @_; + + # Cache statements + my $sth = $dbh->prepare_cached($query); + + $sth->execute(@values); + + # No results + if ($sth->rows == 0) { + $sth->finish(); + return undef; + } + + my $row = $sth->fetchrow_hashref(); + $sth->finish(); + return $row; +} + +########################################################################## +## Get all rows returned by an SQL query as a hash reference array. +########################################################################## +sub get_db_rows ($$;@) { + my ($dbh, $query, @values) = @_; + my @rows; + + # Cache statements + my $sth = $dbh->prepare_cached($query); + + $sth->execute(@values); + + # Save returned rows + while (my $row = $sth->fetchrow_hashref()) { + push (@rows, $row); } - $s_idag->finish(); - return $desactivado; + $sth->finish(); + return @rows; } ########################################################################## -## SUB dame_ultimo_contacto (id_agente) -## Return last_contact for id_agente +## SQL insert. Returns the ID of the inserted row. ########################################################################## -sub dame_ultimo_contacto (%$$) { - my $pa_config = $_[0]; - my $id_agente = $_[1]; - my $dbh = $_[2]; +sub db_insert ($$;@) { + my ($dbh, $query, @values) = @_; - my $tipo; my @data; - # Calculate agent ID using select by its name - my $query_idag = "select * from tagente where id_agente = ".$id_agente; - my $s_idag = $dbh->prepare($query_idag); - $s_idag ->execute; - if ($s_idag->rows == 0) { - logger($pa_config, "ERROR dame_ultimo_contacto(): Cannot find agente $id_agente", 2); - logger($pa_config, "ERROR: SQL Query is $query_idag ", 10); - } else { @data = $s_idag->fetchrow_array(); } - $tipo= $data[5]; - $s_idag->finish(); - return $tipo; -} - -########################################################################## -## SUB crea_agente_modulo(nombre_agente, nombre_tipo_modulo, nombre_modulo) -## create an entry in tagente_modulo, return id of created tagente_modulo -########################################################################## -sub crea_agente_modulo (%$$$$$$$) { - my $pa_config = $_[0]; - my $nombre_agente = $_[1]; - my $tipo_modulo = $_[2]; - my $nombre_modulo = $_[3]; - my $max = $_[4]; - my $min = $_[5]; - my $descripcion = $_[6]; - my $dbh = $_[7]; - - # Sanity checks - if (!defined($nombre_modulo)){ - logger($pa_config, "ERROR crea_agente_modulo(): Undefined module name", 2); - return -1; - } - - my $modulo_id = dame_modulo_id ($pa_config, $tipo_modulo, $dbh); - my $agente_id = dame_agente_id ($pa_config, $nombre_agente, $dbh); - if (!defined($agente_id) || ($agente_id < 0)){ - return -1; - } - if ((!defined($max)) || ($max eq "")){ - $max = 0; - } - if ((!defined($min)) || ($min eq "")){ - $min = 0; - } - if ((!defined($descripcion)) || ($descripcion eq "")){ - $descripcion = "N/A"; - } - $descripcion = sqlWrap ($descripcion. "(*)" ); - $max = sqlWrap ($max); - $min = sqlWrap ($min); - $nombre_modulo = sqlWrap ($nombre_modulo); - - my $query = "INSERT INTO tagente_modulo (id_agente,id_tipo_modulo,nombre,max,min,descripcion, id_modulo) VALUES ($agente_id, $modulo_id, $nombre_modulo, $max, $min, $descripcion, 1)"; - if (($max eq "") and ($min eq "")) { - $query = "INSERT INTO tagente_modulo (id_agente,id_tipo_modulo,nombre,descripcion, id_modulo) VALUES ($agente_id, $modulo_id, $nombre_modulo, $descripcion, 1)"; - } elsif ($min eq "") { - $query = "INSERT INTO tagente_modulo (id_agente,id_tipo_modulo,nombre,max,descripcion, id_modulo) VALUES ($agente_id, $modulo_id, $nombre_modulo, $max, $descripcion, 1)"; - } elsif ($min eq "") { - $query = "INSERT INTO tagente_modulo (id_agente,id_tipo_modulo,nombre,min,descripcion, id_modulo) VALUES ($agente_id, $modulo_id, $nombre_modulo, $min, $descripcion, 1)"; - } - $dbh->do($query); + $dbh->do($query, undef, @values); return $dbh->{'mysql_insertid'}; } -# --------------------------------------------------------------- -# Generic access to a field ($field) given a table -# give_db_value (field_name_to_be_returned, table, field_search, condition_value, dbh) -# --------------------------------------------------------------- -sub get_db_value ($$$$$) { - my $field = $_[0]; - my $table = $_[1]; - my $field_search = $_[2]; - my $condition_value= $_[3]; - my $dbh = $_[4]; - - my $query = "SELECT $field FROM $table WHERE $field_search = '$condition_value' "; - my $s_idag = $dbh->prepare($query); - $s_idag ->execute; - if ($s_idag->rows != 0) { - my @data = $s_idag->fetchrow_array(); - my $result = $data[0]; - $s_idag->finish(); - return $result; - } - return -1; -} - -# --------------------------------------------------------------- -# Free SQL sentence. Return first field on exit -# --------------------------------------------------------------- - -sub get_db_free_field ($$) { - my $condition = $_[0]; - my $dbh = $_[1]; - my $s_idag = $dbh->prepare($condition); - $s_idag ->execute; - if ($s_idag->rows != 0) { - my @data = $s_idag->fetchrow_array(); - my $result = $data[0]; - $s_idag->finish(); - return $result; - } - return -1; -} - - - -# --------------------------------------------------------------- -# Free SQL sentence. Return entire hash in row -# --------------------------------------------------------------- - -sub get_db_free_row ($$) { - my $condition = $_[0]; - my $dbh = $_[1]; - my $rowref; - - my $query = $condition; - my $s_idag = $dbh->prepare($query); - $s_idag ->execute; - if ($s_idag->rows != 0) { - $rowref = $s_idag->fetchrow_hashref; - $s_idag->finish(); - return $rowref; - } - return -1; -} - -# --------------------------------------------------------------- -# Free SQL sentence. Return all rows as a hash array. -# --------------------------------------------------------------- - -sub get_db_all_rows ($$) { - my $condition = $_[0]; - my $dbh = $_[1]; - my @result; - - my $s_idag = $dbh->prepare($condition); - if (!$s_idag->execute) { - return @result; - } - - if ($s_idag->rows == 0) { - return @result; - } - - while (my $row = $s_idag->fetchrow_hashref()) { - push (@result, $row); - } - $s_idag->finish(); - return @result; -} - -# --------------------------------------------------------------- -# Insert SQL sentence. Returns ID of row inserted -# --------------------------------------------------------------- - -sub db_insert ($$) { - my $query= $_[0]; - my $dbh = $_[1]; - - $dbh->do($query); - return $dbh->{'mysql_insertid'}; -} - -# --------------------------------------------------------------- -# Generic SQL sentence. -# --------------------------------------------------------------- - -sub db_do ($$) { - my $query= $_[0]; - my $dbh = $_[1]; - - $dbh->do($query); -} - ########################################################################## -# SUB pandora_create_agent (pa_config, dbh, target_ip, target_ip_id, -# id_group, network_server_assigned, name, id_os) -# Create agent, and associate address to agent in taddress_agent table. -# it returns created id_agent. +## Generic SQL sentence. ########################################################################## -sub pandora_create_agent { - my $pa_config = $_[0]; - my $dbh = $_[1]; - my $target_ip = $_[2]; - my $target_ip_id = $_[3]; - my $id_group = $_[4]; - my $id_server= $_[5]; - my $name = $_[6]; - my $id_parent = $_[7]; - my $id_os = $_[8]; +sub db_do ($$;@) { + my ($dbh, $query, @values) = @_; - my $prediction; - my $wmi; - my $plugin; - - if ((!is_numeric($id_server)) || ($id_server == 0)){ - $id_server = get_db_free_field ("SELECT id_server FROM tserver WHERE network_server = 1 AND master = 1 LIMIT 1", $dbh); - } - - $prediction = get_db_free_field ("SELECT id_server FROM tserver WHERE prediction_server = 1 AND master = 1 LIMIT 1", $dbh); - $wmi = get_db_free_field ("SELECT id_server FROM tserver WHERE wmi_server = 1 AND master = 1 LIMIT 1", $dbh); - $plugin = get_db_free_field ("SELECT id_server FROM tserver WHERE plugin_server = 1 AND master = 1 LIMIT 1", $dbh); - - if ($wmi < 0){ - $wmi = 0; - } - - if ($plugin < 0){ - $plugin = 0; - } - - if ($prediction < 0){ - $prediction = 0; - } - - if ($id_server < 0){ - $id_server = 0; - } - - my $server = $pa_config->{'servername'}.$pa_config->{"servermode"}; - logger ($pa_config,"$server: Creating agent $name $target_ip ", 1); - - my $query_sql2 = "INSERT INTO tagente (nombre, direccion, comentarios, id_grupo, id_os, id_network_server, intervalo, id_parent, modo, id_prediction_server, id_wmi_server, id_plugin_server) VALUES ('$name', '$target_ip', 'Created by $server', $id_group, $id_os, $id_server, 300, $id_parent, 1, $prediction, $wmi, $plugin)"; - - $dbh->do ($query_sql2); - - my $lastid = $dbh->{'mysql_insertid'}; - - pandora_event ($pa_config, "Agent '$name' created by ".$pa_config->{'servername'}.$pa_config->{"servermode"}, $pa_config->{'autocreate_group'}, $lastid, 2, 0, 0, 'new_agent', $dbh); - - if ($target_ip_id > 0){ - my $query_sql3 = "INSERT INTO taddress_agent (id_a, id_agent) values ($target_ip_id, $lastid)"; - $dbh->do($query_sql3); - } - return $lastid; -} - -########################################################################## -## SUB pandora_event -## Write in internal audit system an entry. -## Params: config_hash, event_title, group, agent_id, severity, id_alertam -## id_agentmodule, event_type (from a set, as string), db_handle -########################################################################## - -sub pandora_event (%$$$$$$$$) { - my $pa_config = $_[0]; - my $evento = $_[1]; - my $id_grupo = $_[2]; - my $id_agente = $_[3]; - my $severity = $_[4]; # new in 2.0 - my $id_alert_am = $_[5]; # new in 2.0 - my $id_agentmodule = $_[6]; # new in 2.0 - my $event_type = $_[7]; # new in 2.0 - my $dbh = $_[8]; - my $timestamp = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $utimestamp; # integer version of timestamp - - $utimestamp = &UnixDate($timestamp,"%s"); # convert from human to integer - $evento = $dbh->quote($evento); - $event_type = $dbh->quote($event_type); - $timestamp = $dbh->quote($timestamp); - my $query = "INSERT INTO tevento (id_agente, id_grupo, evento, timestamp, estado, utimestamp, event_type, id_agentmodule, id_alert_am, criticity) VALUES ($id_agente, $id_grupo, $evento, $timestamp, 0, $utimestamp, $event_type, $id_agentmodule, $id_alert_am, $severity)"; - $dbh->do($query); -} - -########################################################################## -## SUB export_module_data () -## ($id_agent, $module, $data, $timestamp, $dbh) -## Process module data according to the module type. -########################################################################## - -sub export_module_data { - my $id_agent = $_[0]; - my $agent_name = $_[1]; - my $module_name = $_[2]; - my $module_type = $_[3]; - my $data = $_[4]; - my $timestamp = $_[5]; - my $dbh = $_[6]; - - my $tagente_modulo = get_db_free_row ("SELECT id_export, id_agente_modulo - FROM tagente_modulo WHERE id_agente = " . $id_agent . - " AND nombre = '" . $module_name . "'", $dbh); - if ($tagente_modulo eq '-1') { - return; - } - - my $id_export = $tagente_modulo->{'id_export'}; - my $id_agente_modulo = $tagente_modulo->{'id_agente_modulo'}; - if ($id_export < 1) { - return; - } - - $dbh->do("INSERT INTO tserver_export_data (`id_export_server`, `agent_name` , - `module_name`, `module_type`, `data`, `timestamp`) - VALUES ($id_export, '$agent_name', '$module_name', '$module_type', - '$data', '$timestamp')"); -} - - -########################################################################## -# SUB update_on_error (pa_config, id_agent_module, dbh ) -# Modules who cannot connect or something go bad, update last_execution_try field -########################################################################## -sub update_on_error { - my $pa_config = $_[0]; - my $id_agent_module = $_[1]; - my $dbh = $_[2]; - - my $utimestamp = &UnixDate("today","%s"); - - # Modules who cannot connect or something go bad, update last_execution_try field - logger ($pa_config, "Cannot obtain Module from IdAgentModule $id_agent_module", 3); - db_do ("UPDATE tagente_estado - SET current_interval = 300, last_execution_try = $utimestamp - WHERE id_agente_modulo = $id_agent_module", $dbh); -; - -} - -########################################################################## -## SUB calcula_alerta_snmp($source,$oid,$custom_value,$timestamp); -## Given an SNMP Trap received with this data, execute Alert or not -########################################################################## - -sub calcula_alerta_snmp { - # Parameters passed as arguments - my $pa_config = $_[0]; - my $trap_agente = $_[1]; - my $trap_oid = $_[2]; - my $trap_oid_text = $_[3]; - my $trap_custom_value = $_[4]; - my $timestamp = $_[5]; - my $dbh = $_[6]; - my $alert_fired = 0; - - my $s_idag = $dbh->prepare("SELECT * FROM talert_snmp"); - $s_idag ->execute; - my @data; - # Read all alerts and apply to this incoming trap - if ($s_idag->rows != 0) { - while (@data = $s_idag->fetchrow_array()) { - $alert_fired = 0; - my $id_as = $data[0]; - my $id_alert = $data[1]; - my $field1 = $data[2]; - my $field2 = $data[3]; - my $field3 = $data[4]; - my $description = $data[5]; - my $alert_type = $data[6]; - my $agent = $data[7]; - my $custom_oid = $data[8]; - my $oid = $data[9]; - my $time_threshold = $data[10]; - my $times_fired = $data[11]; - my $last_fired = $data[12]; # The real fired alarms - my $max_alerts = $data[13]; - my $min_alerts = $data[14]; # The real triggered alarms (not really fired, only triggered) - my $internal_counter = $data[15]; - my $alert_priority = $data[16]; - - my $alert_data = ""; - - if ($alert_type == 0){ # type 0 is OID only - if ( $trap_oid =~ m/$oid/i || $trap_oid_text =~ m/$oid/i){ - $alert_fired = 1; - $alert_data = "SNMP/OID:".$oid; - logger ($pa_config,"SNMP Alert debug (OID) MATCHED",10); - } - } elsif ($alert_type == 1){ # type 1 is custom value - logger ($pa_config,"SNMP Alert debug (Custom) $custom_oid / $trap_custom_value",10); - if ( $trap_custom_value =~ m/$custom_oid/i ){ - $alert_fired = 1; - $alert_data = "SNMP/VALUE:".$custom_oid; - logger ($pa_config,"SNMP Alert debug (Custom) MATCHED",10); - } - } else { # type 2 is agent IP - if ($trap_agente =~ m/$agent/i ){ - $alert_fired = 1; - $alert_data = "SNMP/SOURCE:".$agent; - logger ($pa_config,"SNMP Alert debug (SOURCE) MATCHED",10); - } - } - - if ($alert_fired == 1){ # Exists condition to fire alarm. - # Verify if under time_threshold - my $fecha_ultima_alerta = ParseDate($last_fired); - my $fecha_actual = ParseDate( $timestamp ); - my $ahora_mysql = &UnixDate("today","%Y-%m-%d %H:%M:%S"); # If we need to update MYSQL last_fired will use $ahora_mysql - my $err; my $flag; - my $fecha_limite = DateCalc($fecha_ultima_alerta,"+ $time_threshold seconds",\$err); - # verify if upper min alerts - # Verify if under min alerts - $flag = Date_Cmp($fecha_actual,$fecha_limite); - if ( $flag >= 0 ) { # Out limits !, reset $times_fired, but do not write to - # database until a real alarm was fired - $times_fired = 0; - $internal_counter=0; - logger ($pa_config,"SNMP Alarm out of timethreshold limits",10); - } - # We are between limits marked by time_threshold or running a new time-alarm-interval - # Caution: MIN Limit is related to triggered (in time-threshold limit) alerts - # but MAX limit is related to executed alerts, not only triggered. Because an alarm to be - # executed could be triggered X (min value) times to be executed. - if (($internal_counter+1 >= $min_alerts) && ($times_fired+1 <= $max_alerts)){ - # The new alert is between last valid time + threshold and between max/min limit to alerts in this gap of time. - $times_fired++; - $internal_counter++; - logger($pa_config,"Executing SNMP Trap alert for $agent - $alert_data",2); - - # Create a hash for passing to execute_alert - my %data_alert = ( - 'name' => '', - 'id_agent_module' => 0, - 'id_template_module' => 0, - 'field1' => $field1, - 'field2' => $field2, - 'field3' => $field3, - 'description' => $description, - 'times_fired' => $times_fired, - 'time_threshold' => 0, - 'id_alert_action' => $id_alert, - 'priority' => $alert_priority, - ); - - # Execute alert - execute_alert ($pa_config, \%data_alert, 0, 0, $agent, $trap_agente, 1, 0, $dbh); - - # Now update the new value for times_fired, alert_fired, internal_counter and last_fired for this alert. - my $query_idag2 = "update talert_snmp set times_fired = $times_fired, last_fired = '$ahora_mysql', internal_counter = $internal_counter where id_as = $id_as "; - $dbh->do($query_idag2); - - # Now find record for trap and update "fired" status... - # Due DBI doesnt return ID of a new inserted item, we now need to find ourselves - # this is a crap :( - - my $query_idag3 = "update ttrap set alerted = 1, priority = $alert_priority where timestamp = '$timestamp' and source = '$trap_agente'"; - $dbh->do($query_idag3); - - } else { # Alert is in valid timegap but has too many alerts or too many little - $internal_counter++; - if ($internal_counter < $min_alerts){ - # Now update the new value for times_fired & last_fired if we are below min limit for triggering this alert - my $query_idag = "update talert_snmp set internal_counter = $internal_counter, times_fired = $times_fired, last_fired = '$ahora_mysql' where id_as = $id_as "; - $dbh->do($query_idag); - logger ($pa_config, "SNMP Alarm not fired because is below min limit",8); - } else { # Too many alerts fired (upper limit) - my $query_idag = "update talert_snmp set times_fired=$times_fired, internal_counter = $internal_counter where id_as = $id_as "; - $dbh->do($query_idag); - logger ($pa_config, "SNMP Alarm not fired because is above max limit",8); - } - } - } - } # While - } # if - $s_idag->finish(); + $dbh->do($query, undef, @values); } # End of function declaration @@ -2600,4 +215,3 @@ sub calcula_alerta_snmp { 1; __END__ - # Look updated servers and take down non updated servers diff --git a/pandora_server/lib/PandoraFMS/DataServer.pm b/pandora_server/lib/PandoraFMS/DataServer.pm new file mode 100644 index 0000000000..c432388aac --- /dev/null +++ b/pandora_server/lib/PandoraFMS/DataServer.pm @@ -0,0 +1,246 @@ +package PandoraFMS::DataServer; +########################################################################## +# Pandora FMS Data Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use Time::Local; +use XML::Simple; + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = Thread::Semaphore->new; +my $TaskSem :shared = Thread::Semaphore->new (0); + +######################################################################################## +# Data Server class constructor. +######################################################################################## +sub new ($$;$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'dataserver'} == 1; + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 0, \&PandoraFMS::DataServer::data_producer, \&PandoraFMS::DataServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS Data Server. \n"; + $self->setNumThreads ($pa_config->{'dataserver_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + my @tasks; + + # Read all files in the incoming directory + opendir (DIR, $pa_config->{'incomingdir'}) + || die "[FATAL] Cannot open Incoming data directory at " . $pa_config->{'incomingdir'} . ": $!"; + + while (defined (my $file_name = readdir(DIR))) { + + # For backward compatibility + if ($file_name =~ /^.*\.checksum$/) { + unlink("$pa_config->{'incomingdir'}/$file_name"); + next; + } + + # Data files have the extension .data + next if ($file_name !~ /^.*\.data$/); + + push (@tasks, $file_name); + } + + closedir(DIR); + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $task) = @_; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my $file_name = $pa_config->{'incomingdir'}; + + # Fix path + $file_name .= "/" unless (substr ($file_name, -1, 1) eq '/'); + $file_name .= $task; + + # Try to parse the XML 3 times + my $xml_data; + + for (1..3) { + eval { + threads->yield; + $xml_data = XMLin ($file_name, forcearray => 'module'); + }; + + # Invalid XML + if ($@) { + print "$@\n"; + sleep (60); + next; + } + + unlink ($file_name); + process_xml_data ($self->getConfig (), $xml_data, $self->getDBH ()); + return; + } + + rename($file_name, $file_name . '_BADXML'); + pandora_event ($pa_config, "Unable to process XML data file ($file_name)", 0, 0, 0, 0, 0, 'error', $dbh); +} + +############################################################################### +# Process XML data coming from an agent. +############################################################################### +sub process_xml_data { + my ($pa_config, $data, $dbh) = @_; + + my ($agent_name, $agent_version, $timestamp, $interval, $os_version) = + ($data->{'agent_name'}, $data->{'version'}, $data->{'timestamp'}, + $data->{'interval'}, $data->{'os_version'}); + + # Unknown agent! + if (! defined ($agent_name) || $agent_name eq '') { + logger($pa_config, 'ERROR: Received data from an unknown agent', 2); + return; + } + + # Check some variables + $interval = 300 unless defined ($interval); + $os_version = 'N/A' if (! defined ($os_version) || $os_version eq ''); + + # Get agent id + my $agent_id = get_agent_id ($dbh, $agent_name); + if ($agent_id < 1) { + if ($pa_config->{'autocreate'} == 0) { + logger($pa_config, "ERROR: There is no agent defined with name $agent_name", 3); + return; + } + + # Create the agent + my $os = pandora_get_os ($data->{'os'}); + $agent_id = pandora_create_agent ($pa_config, $pa_config->{'servername'}, $agent_name, '', 0, $pa_config->{'autocreate_group'}, 0, 0, $os, $dbh); + return unless defined ($agent_id); + } + + pandora_update_agent ($pa_config, $timestamp, $agent_id, $os_version, $agent_version, $interval, $dbh); + pandora_module_keep_alive ($pa_config, $agent_id, $agent_name, $dbh); + + # Process modules + foreach my $module_data (@{$data->{'module'}}) { + + # Unnamed module + next unless (defined ($module_data->{'name'}->[0])); + + my $module_type = $module_data->{'type'}->[0]; + my $module_name = $module_data->{'name'}->[0]; + + # Single data + if (! defined ($module_data->{'datalist'})) { + my $data_timestamp = (defined ($module_data->{'timestamp'})) ? $module_data->{'timestamp'}->[0] : $timestamp; + process_module_data ($pa_config, $module_data, $agent_name, $module_name, $module_type, $data_timestamp, $dbh); + next; + } + + # Data list + foreach my $list (@{$module_data->{'datalist'}}) { + + # Empty list + next unless defined ($list->{'data'}); + + foreach my $data (@{$list->{'data'}}) { + + # No value + next unless defined ($data->{'value'}); + + $module_data->{'data'} = $data->{'value'}; + my $data_timestamp = (defined ($data->{'timestamp'})) ? $data->{'timestamp'} : $timestamp; + process_module_data ($pa_config, $module_data, $agent_name, $module_name, + $module_type, $data_timestamp, $dbh); + } + } + } +} + +########################################################################## +# Process module data, creating module if necessary. +########################################################################## +sub process_module_data ($$$$$$$) { + my ($pa_config, $data, $agent_name, $module_name, $module_type, $timestamp, $dbh) = @_; + + my $agent = get_db_single_row ($dbh, 'SELECT * FROM tagente WHERE nombre = ?', $agent_name); + return unless defined ($agent); + + my $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente = ? AND nombre = ?', $agent->{'id_agente'}, $module_name); + if (! defined ($module)) { + my $module_id = get_module_id ($dbh, $module_type); + return if ($module_id == -1 && $pa_config->{'autocreate'} == 0); + + my ($min, $max, $description) = (0, 0, ''); + $max = $data->{'max'}->[0] if (defined ($data->{'max'})); + $min = $data->{'min'}->[0] if (defined ($data->{'min'})); + $description = $data->{'description'}->[0] if (defined ($data->{'description'})); + pandora_create_module ($agent->{'id_agente'}, $module_id, $module_name, + $max, $min, $description, $dbh); + $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente = ? AND nombre = ?', $agent->{'id_agente'}, $module_name); + return unless defined $module; + } + + if ($timestamp =~ /(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/ || + $timestamp =~ /(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/) { + my $utimestamp = timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900); + pandora_process_module ($pa_config, $data->{'data'}->[0], $agent, $module, $module_type, $timestamp, $utimestamp, $dbh); + } +} + + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/NetworkServer.pm b/pandora_server/lib/PandoraFMS/NetworkServer.pm new file mode 100644 index 0000000000..76d8528ab1 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/NetworkServer.pm @@ -0,0 +1,476 @@ +package PandoraFMS::NetworkServer; +########################################################################## +# Pandora FMS Network Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use IO::Socket::INET; +use Net::Ping; +use POSIX qw(strftime); + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = new Thread::Semaphore; +my $TaskSem :shared = new Thread::Semaphore (0); +my $ICMPLock :shared; + +######################################################################################## +# Network Server class constructor. +######################################################################################## +sub new ($$$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'networkserver'} == 1; + + if (! -e $config->{'snmpget'}) { + print ' [E] ' . $config->{'snmpget'} . " needed by Pandora FMS Network Server not found.\n\n"; + return undef; + } + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 1, \&PandoraFMS::NetworkServer::data_producer, \&PandoraFMS::NetworkServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS Network Server. \n"; + $self->setNumThreads ($pa_config->{'network_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + my @rows; + + if ($pa_config->{'pandora_master'} == 0) { + @rows = get_db_rows ($dbh, 'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado + WHERE server_name = ? + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.id_tipo_modulo > 4 + AND tagente_modulo.id_tipo_modulo < 19 + AND tagente_modulo.disabled = 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC ', $pa_config->{'servername'}); + } else { + @rows = get_db_rows ($dbh, 'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, tagente_estado.last_execution_try, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado + WHERE ((server_name = ?) OR (server_name = ANY(SELECT server_name FROM tserver WHERE status = 0))) + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.disabled = 0 + AND tagente_modulo.id_tipo_modulo > 4 + AND tagente_modulo.id_tipo_modulo < 19 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC', $pa_config->{'servername'}); + } + + foreach my $row (@rows) { + + # Reset forced execution flag + if ($row->{'flag'} == 1) { + db_do ($dbh, 'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?', $row->{'id_agente_modulo'}); + } + + push (@tasks, $row->{'id_agente_modulo'}); + } + + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $task) = @_; + + exec_network_module ($self->getConfig (), $task, $self->getDBH ()); +} + +############################################################################## +# pandora_ping_icmp (config, destination, timeout) +# Do a ICMP scan, return 1 if alive, 0 if not +############################################################################## +sub pandora_ping_icmp { + my $pa_config = $_[0]; + my $dest = $_[1]; + my $l_timeout = $_[2]; + # temporal vars. + my $result = 0; + my $result2 = 0; + my $temp; + + if ($pa_config->{'icmp_checks'} eq ""){ + $pa_config->{'icmp_checks'} = 1; + } + + # Make more than a single ping (as defined in icmp_checks + for ($temp =0; $temp < $pa_config->{'icmp_checks'}; $temp++){ + my $p; + # Some hosts don't accept ICMP with too small payload. Use 16 Bytes min + { + lock $ICMPLock; + $p = Net::Ping->new("icmp", $l_timeout, 32); + $result = $p->ping($dest); + } + + if (defined ($result)){ + $p->close(); + if ($result == 1){ + $result2 = 1; + $temp = $pa_config->{'icmp_checks'}; # Exit for + } + } + undef ($p); + } + return $result2; +} + +############################################################################## +# pandora_ping_latency (destination, timeout, data, result) - Do a ICMP latency check +############################################################################## +sub pandora_ping_latency { + my $dest = $_[0]; + my $l_timeout = $_[1]; + my $module_data = $_[2]; + my $module_result = $_[3]; + my $icmp_return; + my $icmp_reply; + my $icmp_ip; + my $nm; + + # Locking for use ICMP call safety + { + lock $ICMPLock; + $nm = Net::Ping->new("icmp", $l_timeout, 32); + $nm->hires(); + ($icmp_return, $icmp_reply, $icmp_ip) = $nm->ping ($dest,$l_timeout); + } + if ($icmp_return) { + $$module_data = $icmp_reply * 1000; # milliseconds + $$module_result = 0; # Successful + } else { + $$module_result = 1; # Error. + $$module_data = 0; + } + $nm->close(); + undef($nm); +} + +########################################################################## +# SUB pandora_query_tcp (pa_config, tcp_port. ip_target, result, data, tcp_send, +# tcp_rcv, id_tipo_module, dbh) +# Makes a call to TCP modules to get a value. +########################################################################## +sub pandora_query_tcp (%$$$$$$$) { + my $pa_config = $_[0]; + my $tcp_port = $_[1]; + my $ip_target = $_[2]; + my $module_result = $_[3]; + my $module_data = $_[4]; + my $tcp_send = $_[5]; + my $tcp_rcv = $_[6]; + my $id_tipo_modulo = $_[7]; + + my $counter; + for ($counter =0; $counter < $pa_config->{'tcp_checks'}; $counter++){ + my $temp; my $temp2; + my $tam; + my $handle=IO::Socket::INET->new( + Proto=>"tcp", + PeerAddr=>$ip_target, + Timeout=>$pa_config->{'tcp_timeout'}, + PeerPort=>$tcp_port, + Blocking=>0 ); # Non blocking !!, very important ! + + if (defined ($handle)){ + # Multi request patch, submitted by Glen Eustace (new zealand) + my @tcp_send = split( /\|/, $tcp_send ); + my @tcp_rcv = split( /\|/, $tcp_rcv ); + +next_pair: + $tcp_send = shift( @tcp_send ); + $tcp_rcv = shift( @tcp_rcv ); + + if ((defined ($tcp_send)) && ($tcp_send ne "")){ # its Expected to sending data ? + # Send data + $handle->autoflush(1); + $tcp_send =~ s/\^M/\r\n/g; + # Replace Carriage rerturn and line feed + $handle->send($tcp_send); + } + # we expect to receive data ? (non proc types) + if ((defined ($tcp_rcv)) && (($tcp_rcv ne "") || ($id_tipo_modulo == 10) || ($id_tipo_modulo ==8) || ($id_tipo_modulo == 11))) { + # Receive data, non-blocking !!!! (VERY IMPORTANT!) + $temp2 = ""; + for ($tam=0; $tam<($pa_config->{'tcp_timeout'}); $tam++){ + $handle->recv($temp,16000,0x40); + $temp2 = $temp2.$temp; + if ($temp ne ""){ + $tam++; # If doesnt receive data, increase counter + } + sleep(1); + } + if ($id_tipo_modulo == 9){ # only for TCP Proc + if ($temp2 =~ /$tcp_rcv/i){ # String match ! + if ( @tcp_send ) { # still more pairs + goto next_pair; + } + $$module_data = 1; + $$module_result = 0; + $counter = $pa_config->{'tcp_checks'}; + } else { + $$module_data = 0; + $$module_result = 0; + $counter = $pa_config->{'tcp_checks'}; + } + } elsif ($id_tipo_modulo == 10 ){ # TCP String (no int conversion)! + $$module_data = $temp2; + $$module_result =0; + } else { # TCP Data numeric (inc or data) + if ($temp2 ne ""){ + if ($temp2 =~ /[A-Za-z\.\,\-\/\\\(\)\[\]]/){ + $$module_result = 1; + $$module_data = 0; # invalid data + $counter = $pa_config->{'tcp_checks'}; + } else { + $$module_data = int($temp2); + $$module_result = 0; # Successful + $counter = $pa_config->{'tcp_checks'}; + } + } else { + $$module_result = 1; + $$module_data = 0; # invalid data + $counter = $pa_config->{'tcp_checks'}; + } + } + } else { # No expected data to receive, if connected and tcp_proc type successful + if ($id_tipo_modulo == 9){ # TCP Proc + $$module_result = 0; + $$module_data = 1; + $counter = $pa_config->{'tcp_checks'}; + } + } + $handle->close(); + undef ($handle); + } else { # Cannot connect (open sock failed) + $$module_result = 1; # Fail + if ($id_tipo_modulo == 9){ # TCP Proc + $$module_result = 0; + $$module_data = 0; # Failed, but data exists + $counter = $pa_config->{'tcp_checks'}; + } + } + } +} + +########################################################################## +# SUB pandora_query_snmp (pa_config, oid, community, target, version, error, dbh) +# Makes a call to SNMP modules to get a value, +########################################################################## +sub pandora_query_snmp (%$$$$$) { + my $pa_config = $_[0]; + my $snmp_oid = $_[1]; + my $snmp_community =$_[2]; + my $snmp_target = $_[3]; + my $snmp_version = $_[4]; + # $_[5] contains error var. + + if ($snmp_version ne '1' && $snmp_version ne '2' + && $snmp_version ne '2c' && $snmp_version ne '3') { + $snmp_version = '1'; + } + + my $snmp_timeout = $pa_config->{"snmp_timeout"}; + my $snmp_retries = $pa_config->{'snmp_checks'}; + + # TODO: Alternative version if command is not available or user select a new switch + # with "use internal SNMP" option. At this moment, due to locks() external snmp is much faster + + $_[5] = 0; + my $snmpget_cmd = $pa_config->{"snmpget"}; + my $output = `$snmpget_cmd -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -c $snmp_community $snmp_target $snmp_oid`; + if ($output eq ""){ + $_[5] = 1; + } + + return $output; +} + +########################################################################## +# SUB exec_network_module (paconfig, id_agente_modulo, dbh ) +# Execute network module task +########################################################################## +sub exec_network_module { + my $pa_config = $_[0]; + my $id_agente_modulo = $_[1]; + my $dbh = $_[2]; + # Init variables + + my @sql_data; + if ((!defined($id_agente_modulo)) || ($id_agente_modulo eq "")){ + return 0; + } + my $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?', $id_agente_modulo); + if ($module == -1) { + logger ($pa_config,"[ERROR] Processing data for invalid module", 0); + return 0; + } + + my $error = "1"; + my $query_sql2; + my $temp=0; my $tam; my $temp2; + my $module_result = 1; # Fail by default + my $module_data = 0; + my $id_agente = $module->{'id_agente'}; + my $agent_name = get_agent_name ($dbh, $id_agente); + my $id_tipo_modulo = $module->{'id_tipo_modulo'}; + my $ip_target = $module->{'ip_target'}; + my $snmp_oid = $module->{'snmp_oid'}; + my $snmp_community = $module->{'snmp_community'}; + my $tcp_port = $module->{'tcp_port'}; + my $tcp_send = $module->{'tcp_send'}; + my $tcp_rcv = $module->{'tcp_rcv'}; + + if ((defined($ip_target)) && ($ip_target)) { + + # ICMP Modules + # ------------ + if ($id_tipo_modulo == 6){ # ICMP (Connectivity only: Boolean) + $temp = pandora_ping_icmp ($pa_config, $ip_target, $pa_config->{'networktimeout'}); + if ($temp == 1 ){ + $module_result = 0; # Successful + $module_data = 1; + } else { + $module_result = 0; # If cannot connect, its down. + $module_data = 0; + } + } elsif ($id_tipo_modulo == 7){ # ICMP (data for latency in ms) + # This module only could be executed if executed as root + if ($> == 0){ + pandora_ping_latency ($ip_target, $pa_config->{"networktimeout"}, \$module_data, \$module_result); + } else { + $module_result = 0; # Done but, with zero value + $module_data = 0; # This should don't happen + } + # SNMP Modules (Proc=18, inc, data, string) + # ------------ + } elsif (($id_tipo_modulo == 15) || ($id_tipo_modulo == 18) || ($id_tipo_modulo == 16) || ($id_tipo_modulo == 17)) { # SNMP module + if ((defined($snmp_oid)) && ($snmp_oid ne "") && (defined($snmp_community)) && ($snmp_community ne "")) { # Port check + $temp2 = pandora_query_snmp ($pa_config, $snmp_oid, $snmp_community, $ip_target, $tcp_send, $error); + } else { + $error = 1 + } + if ($error == 0) { # A correct SNMP Query + $module_result = 0; + # SNMP_DATA_PROC + if ($id_tipo_modulo == 18){ #snmp_data_proc + # RFC1213-MIB where it says that: SYNTAX INTEGER { up(1), down(2), testing(3), + # unknown(4), dormant(5), notPresent(6), lowerLayerDown(7) } + if ($temp2 != 1){ # up state is 1, down state in SNMP is 2 .... + $temp2 = 0; + } + $module_data = $temp2; + } + # SNMP_DATA and SNMP_DATA_INC + elsif (($id_tipo_modulo == 15) || ($id_tipo_modulo == 16) ){ + if (!is_numeric($temp2)){ + $module_result = 1; + } else { + $module_data = $temp2; + } + } else { # String SNMP + $module_data = $temp2; + } + } else { # Failed SNMP-GET + $module_data = 0; + if ($id_tipo_modulo == 18){ # snmp_proc + # Feature from 10Feb08. If snmp_proc_deadresponse = 1 and cannot contact by an error + # this is a fail monitor + if ($pa_config->{"snmp_proc_deadresponse"} eq "1"){ + $module_result = 0; + } else { + $module_result = 1; + } + } else { + $module_result = 1; # No data, cannot connect + } + } + # TCP Module + # ---------- + } elsif (($id_tipo_modulo == 8) || ($id_tipo_modulo == 9) || ($id_tipo_modulo == 10) || ($id_tipo_modulo == 11)) { # TCP Module + if ((defined($tcp_port)) && ($tcp_port < 65536) && ($tcp_port > 0)) { # Port check + pandora_query_tcp ($pa_config, $tcp_port, $ip_target, \$module_result, \$module_data, $tcp_send, $tcp_rcv, $id_tipo_modulo); + } else { + # Invalid port, get no check + $module_result = 1; + } + } + } + + # Write data section + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Is everything goes ok + if ($module_result == 0) { + + pandora_process_module ($pa_config, $module_data, '', $module, '', $timestamp, $utimestamp, $dbh); + + # Update agent last contact using Pandora version as agent version + pandora_update_agent ($pa_config, $timestamp, $id_agente, $pa_config->{'servername'}.'_Net', $pa_config->{'version'}, -1, $dbh); + + } else { + # Modules who cannot connect or something go bad, update last_execution_try field + pandora_update_module_on_error ($pa_config, $id_agente_modulo, $dbh); + } +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/PluginServer.pm b/pandora_server/lib/PandoraFMS/PluginServer.pm new file mode 100644 index 0000000000..1f4191622f --- /dev/null +++ b/pandora_server/lib/PandoraFMS/PluginServer.pm @@ -0,0 +1,182 @@ +package PandoraFMS::PluginServer; +########################################################################## +# Pandora FMS Plugin Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use POSIX qw(strftime); +use HTML::Entities; + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = Thread::Semaphore->new; +my $TaskSem :shared = Thread::Semaphore->new (0); + +######################################################################################## +# Plugin Server class constructor. +######################################################################################## +sub new ($$;$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'pluginserver'} == 1; + + # Check for pandora_exec + if (system($config->{'plugin_exec'} . ' > /dev/null 2>&1') != 256) { + print " [E] pandora_exec not found.\n\n"; + return undef; + } + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 4, \&PandoraFMS::PluginServer::data_producer, \&PandoraFMS::PluginServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS Plugin Server. \n"; + $self->setNumThreads ($pa_config->{'plugin_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + my @rows; + + if ($pa_config->{'pandora_master'} != 1) { + @rows = get_db_rows ($dbh, 'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado + WHERE server_name = ? + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.id_plugin != 0 + AND tagente_modulo.disabled = 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC', $pa_config->{'servername'}); + } else { + @rows = get_db_rows ($dbh, 'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado + WHERE ((server_name = ?) OR (server_name = ANY(SELECT server_name FROM tserver WHERE status = 0))) + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.disabled = 0 + AND tagente_modulo.id_plugin != 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC', $pa_config->{'servername'}); + } + + foreach my $row (@rows) { + + # Reset forced execution flag + if ($row->{'flag'} == 1) { + db_do ($dbh, 'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?', $row->{'id_agente_modulo'}); + } + + push (@tasks, $row->{'id_agente_modulo'}); + } + + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $module_id) = @_; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + # Retrieve module data + my $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?', $module_id); + return unless defined $module; + + # Retrieve plugin data + my $plugin = get_db_single_row ($dbh, 'SELECT * FROM tplugin WHERE id = ?', $module->{'id_plugin'}); + return unless defined $plugin; + + # Use the smallest timeout + my $timeout = ($plugin->{'max_timeout'} < $pa_config->{'plugin_timeout'}) ? + $plugin->{'max_timeout'} : $pa_config->{'plugin_timeout'}; + + # Build command to execute + my $command = $plugin->{'execute'}; + if ($plugin->{'net_dst_opt'} ne ''){ + $command .= ' ' . $plugin->{'net_dst_opt'} . ' ' . $module->{'ip_target'}; + } + if ($plugin->{'net_port_opt'} ne '') { + $command .= ' ' . $plugin->{'net_port_opt'} . ' ' . $module->{'tcp_port'}; + } + if ($plugin->{'user_opt'} ne '') { + $command .= ' ' . $plugin->{'user_opt'} . ' ' . $module->{'plugin_user'}; + } + if ($plugin->{'pass_opt'} ne '') { + $command .= ' ' . $plugin->{'pass_opt'} . ' ' . $module->{'plugin_pass'}; + } + + # Extra parameter + if ($module->{'plugin_parameter'} ne '') { + $command .= ' ' . $module->{'plugin_parameter'}; + } + + $command = decode_entities($command); + logger ($pa_config, "Executing AM # $module_id plugin command '$command'", 9); + + # Execute command + $command = $pa_config->{'plugin_exec'} . ' ' . $timeout . ' ' . $command; + my $module_data = `$command`; + if (! defined $module_data || $module_data eq '') { + pandora_update_module_on_error ($pa_config, $module_id, $dbh); + return; + } + + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + pandora_process_module ($pa_config, $module_data, '', $module, '', $timestamp, $utimestamp, $dbh); + pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, $pa_config->{'servername'}.'_Plugin', $pa_config->{'version'}, -1, $dbh); +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/PredictionServer.pm b/pandora_server/lib/PandoraFMS/PredictionServer.pm new file mode 100644 index 0000000000..75f4bbf751 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/PredictionServer.pm @@ -0,0 +1,242 @@ +package PandoraFMS::PredictionServer; +########################################################################## +# Pandora FMS Prediction Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use IO::Socket::INET; +use Net::Ping; +use POSIX qw(strftime); + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = Thread::Semaphore->new; +my $TaskSem :shared = Thread::Semaphore->new (0); + +######################################################################################## +# Prediction Server class constructor. +######################################################################################## +sub new ($$;$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'predictionserver'} == 1; + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 5, \&PandoraFMS::PredictionServer::data_producer, \&PandoraFMS::PredictionServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS Prediction Server. \n"; + $self->setNumThreads ($pa_config->{'prediction_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + my @rows; + + if ($pa_config->{'pandora_master'} != 1) { + @rows = get_db_rows ($dbh, 'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag + FROM tagente, tagente_modulo, tagente_estado + WHERE server_name = ? + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.prediction_module != 0 + AND tagente_modulo.disabled = 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 + OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) + ORDER BY last_execution_try ASC ', $pa_config->{'servername'}); + } else { + @rows = get_db_rows ($dbh, 'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag + FROM tagente, tagente_modulo, tagente_estado + WHERE ((server_name = ?) OR (server_name = ANY(SELECT server_name FROM tserver WHERE status = 0))) + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.disabled = 0 + AND tagente_modulo.prediction_module != 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()) + ORDER BY last_execution_try ASC', $pa_config->{'servername'}); + } + + foreach my $row (@rows) { + + # Reset forced execution flag + if ($row->{'flag'} == 1) { + db_do ($dbh, 'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?', $row->{'id_agente_modulo'}); + } + + push (@tasks, $row->{'id_agente_modulo'}); + } + + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $task) = @_; + + exec_prediction_module ($self->getConfig (), $task, $self->getDBH ()); +} + +########################################################################## +# Execute prediction module. +########################################################################## +sub exec_prediction_module { + my ($pa_config, $id_am, $dbh) = @_; + + # Get a full hash for agent_module record reference ($agent_module) + my $agent_module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?', $id_am); + return unless defined $agent_module; + + # Get a full hash for target agent_module record reference ($target_module) + my $target_module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?', $agent_module->{'prediction_module'}); + return unless defined $target_module; + + # Prediction mode explanation + # + # 0 is for target type of generic_proc. It compares latest data with current data. Needs to get + # data on a "middle" interval, so if interval is 300, get data to compare with 150 before + # and 150 in the future. If current data is ABOVE or BELOW average +- typical_deviation + # this is a BAD value (0), if not is ok (1) and written in target module as is. + # more interval configured for this module, more "margin" has to compare data. + # + # 1 is for target type of generic_data. It get's data in the future, using the interval given in + # module. It gets average from current timestamp to INTERVAL in the future and gets average + # value. Typical deviation is not used here. + + # 0 proc, 1 data + my $prediction_mode = ($agent_module->{'id_tipo_modulo'} == 2) ? 0 : 1; + + # Initialize another global sub variables. + my $module_data = 0; # 0 data for default + + # Get current timestamp + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + # Get different data from each week one month ago (4 values) + # $agent_module->{'module_interval'} uses a margin of interval to get average data from the past + my @week_data; + my @week_utimestamp; + + for (my $i=0; $i<4; $i++){ + $week_utimestamp[$i] = $utimestamp - (84600*7*($i+1)); + # Adjust for proc prediction + if ($prediction_mode == 0) { + $week_utimestamp[$i] = $week_utimestamp[$i] - ($agent_module->{'module_interval'} / 2); + } + } + + # Let's calculate statistical average using past data + # n = total of real data values + my ($n, $average, $temp1) = (0, 0, 0); + for (my $i=0; $i < 4; $i++){ + my ($first_data, $last_data, $average_interval); + my $sum_data = 0; + + $temp1 = $week_utimestamp[$i] + $agent_module->{'module_interval'}; + # Get data for week $i in the past + $average_interval = get_db_value ($dbh, 'SELECT AVG(datos) FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? AND utimestamp < ?', $target_module->{'id_agente_modulo'}, $week_utimestamp[$i], $temp1); + + # Need to get data outside interval because no data. + if ($average_interval == 0){ + $last_data = get_db_value ($dbh, 'SELECT datos FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? LIMIT 1', $target_module->{'id_agente_modulo'}, $week_utimestamp[$i]); + $sum_data++ if ($last_data != 0); + + $first_data = get_db_value ($dbh, 'SELECT datos FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp < ? LIMIT 1', $target_module->{'id_agente_modulo'}, $temp1); + $sum_data++ if ($first_data != 0); + + $week_data[$i] = (($last_data + $first_data) / $sum_data); + } else { + $week_data[$i] = $average_interval; + } + + # It's possible that one of the week_data[i] values was not valid (NULL) + # so recheck it and relay on n=0 for "no data" values set to 0 in result + # Calculate total ammount of valida data for each data sample + if ((is_numeric($week_data[$i])) && ($week_data[$i] > 0)){ + $n++; + # Average SUM + $average = $average + $week_data[$i]; + } + } + + # Real average value + $average = ($n > 0) ? ($average / $n) : 0; + + # (PROC) Compare with current data + if ($prediction_mode == 0){ + # Calculate typical deviation + my $typical_deviation = 0; + for (my $i=0; $i< $n; $i++){ + if ((is_numeric($week_data[$i])) && ($week_data[$i] > 0)) { + $typical_deviation = $typical_deviation + (($week_data[$i] - $average)**2); + } + } + $typical_deviation = sqrt ($typical_deviation / ($n-1)); + + my $current_value = get_db_value ($dbh, 'SELECT datos FROM tagente_estado WHERE id_agente_modulo = ?', $target_module->{'id_agente_modulo'}); + if ( ($current_value > ($average - $typical_deviation)) && ($current_value < ($average + $typical_deviation)) ){ + $module_data = 1; # OK !! + } else { + $module_data = 0; # Out of predictions + } + } else { + # Prediction based on data + $module_data = $average; + } + + pandora_process_module ($pa_config, $module_data, '', $agent_module, '', $timestamp, $utimestamp, $dbh); + pandora_update_agent ($pa_config, $timestamp, $agent_module->{'id_agente'}, $pa_config->{'servername'}.'_Prediction', $pa_config->{'version'}, -1, $dbh); +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm new file mode 100644 index 0000000000..7d224436de --- /dev/null +++ b/pandora_server/lib/PandoraFMS/ProducerConsumerServer.pm @@ -0,0 +1,166 @@ +package PandoraFMS::ProducerConsumerServer; +########################################################################## +# Pandora FMS generic server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::Server; + +# inherits from PandoraFMS::Server +our @ISA = qw(PandoraFMS::Server); + +######################################################################################## +# ProducerConsumerServer class constructor. +######################################################################################## +sub new ($$$$$;$) { + my ($class, $config, $server_type, $producer, + $consumer, $dbh) = @_; + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, $server_type, $dbh); + + # Set producer/consumer functions + $self->{'_producer'} = $producer; + $self->{'_consumer'} = $consumer; + + bless $self, $class; + return $self; +} + +######################################################################################## +# Get producer function. +######################################################################################## +sub getProducer ($) { + my $self = shift; + + return $self->{'_producer'}; +} + +######################################################################################## +# Get consumer function. +######################################################################################## +sub getConsumer ($) { + my $self = shift; + + return $self->{'_consumer'}; +} + +############################################################################### +# Run. +############################################################################### +sub run ($$$$$) { + my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; + + # Launch consumer threads + for (1..$self->getNumThreads ()) { + my $thr = threads->create (\&PandoraFMS::ProducerConsumerServer::data_consumer, $self, + $task_queue, $pending_tasks, $sem, $task_sem); + return unless defined ($thr); + $self->addThread ($thr->tid ()); + } + + # Launch producer thread + my $thr = threads->create (\&PandoraFMS::ProducerConsumerServer::data_producer, $self, + $task_queue, $pending_tasks, $sem, $task_sem); + return unless defined ($thr); + $self->addThread ($thr->tid ()); +} + +############################################################################### +# Queue pending tasks. +############################################################################### +sub data_producer ($$$$$) { + my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; + my $pa_config = $self->getConfig (); + + # Connect to the DB + my $dbh = db_connect ('mysql', $pa_config->{'dbname'}, $pa_config->{'dbhost'}, 3306, + $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); + $self->setDBH ($dbh); + + while (1) { + + # Get pending tasks + my @tasks = &{$self->{'_producer'}}($self); + + # Update queue size for statistics + $self->setQueueSize ($#tasks); + + foreach my $task (@tasks) { + $sem->down; + + if (defined $pending_tasks->{$task}) { + $sem->up; + next; + } + + # Queue task and signal consumers + $pending_tasks->{$task} = 0; + push (@{$task_queue}, $task); + $task_sem->up; + + $sem->up; + } + + threads->yield; + sleep ($pa_config->{'server_threshold'}); + } +} + +############################################################################### +# Execute pending tasks. +############################################################################### +sub data_consumer ($$$$$) { + my ($self, $task_queue, $pending_tasks, $sem, $task_sem) = @_; + my $pa_config = $self->getConfig (); + + # Connect to the DB + my $dbh = db_connect ('mysql', $pa_config->{'dbname'}, $pa_config->{'dbhost'}, 3306, + $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); + $self->setDBH ($dbh); + + while (1) { + + # Wait for data + $task_sem->down; + + $sem->down; + my $task = pop (@{$task_queue}); + $sem->up; + + # Execute task + &{$self->{'_consumer'}}($self, $task); + + # Update task status + $sem->down; + delete ($pending_tasks->{$task}); + $sem->up; + + threads->yield; + } +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/ReconServer.pm b/pandora_server/lib/PandoraFMS/ReconServer.pm new file mode 100644 index 0000000000..3edf638091 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/ReconServer.pm @@ -0,0 +1,382 @@ +package PandoraFMS::ReconServer; +########################################################################## +# Pandora FMS Recon Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use IO::Socket::INET; +use Net::Ping; +use NetAddr::IP; +use POSIX qw(strftime); + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = Thread::Semaphore->new; +my $TaskSem :shared = Thread::Semaphore->new (0); +my $ICMPLock :shared; +my $TracerouteAvailable = (eval 'use Net::Traceroute::PurePerl; 1') ? 1 : 0; + +######################################################################################## +# Recon Server class constructor. +######################################################################################## +sub new ($$$$$$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'reconserver'} == 1; + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 3, \&PandoraFMS::ReconServer::data_producer, \&PandoraFMS::ReconServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS Recon Server. \n"; + $self->setNumThreads ($pa_config->{'recon_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + + my $server_id = get_server_id ($dbh, $pa_config->{'servername'}, $self->getServerType ()); + return @tasks unless defined ($server_id); + + my @rows = get_db_rows ($dbh, 'SELECT * FROM trecon_task + WHERE id_recon_server = ? + AND (status = 1 OR (utimestamp + interval_sweep) < UNIX_TIMESTAMP())', $server_id); + foreach my $row (@rows) { + + # Update task status + update_recon_task ($pa_config, $dbh, $row->{'id_rt'}, 0); + + push (@tasks, $row->{'id_rt'}); + } + + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $task_id) = @_; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + # Get recon task data + my $task = get_db_single_row ($dbh, 'SELECT * FROM trecon_task WHERE id_rt = ?', $task_id); + return -1 unless defined ($task); + + # Get a NetAddr::IP object for the target network + my $net_addr = new NetAddr::IP ($task->{'subnet'}); + if (! defined ($net_addr)) { + logger ($pa_config, 'Invalid network ' . $task->{'subnet'} . ' for task ' . $task->{'name'}, 2); + update_recon_task ($pa_config, $dbh, $task_id, -1); + return -1; + } + + # Scan the network for hosts + my ($total_hosts, $hosts_found, $addr_found) = ($net_addr->num, 0, ''); + for (my $i = 1, $net_addr++; $net_addr < $net_addr->broadcast; $i++, $net_addr++) { + + my $addr = (split(/\//, $net_addr))[0]; + update_recon_task ($pa_config, $dbh, $task_id, $i / ($total_hosts / 100)); + + # Does the host already exist? + next if (addr_exists ($dbh, $addr) == 1); + + # Is the host alive? (thanks to Evi for the TCP scans) + my $alive = 0; + if (icmp_scan ($addr, $pa_config->{'networktimeout'}) == 1) { + $alive = 1; + #Check for Remote Desktop & VNC (Desktop & Server machines) + } elsif (tcp_scan ($addr, $pa_config->{'networktimeout'}, 3389) == 1 || + tcp_scan ($addr, $pa_config->{'networktimeout'}, 5900) == 1) { + $alive = 1; + #Check for management ports 10000 = Webmin, 161 = SNMP (Most embedded devices) + } elsif (tcp_scan ($addr, $pa_config->{'networktimeout'}, 10000) == 1 || + tcp_scan ($addr, $pa_config->{'networktimeout'}, 161) == 1) { + $alive = 1; + #Check for SSH & Mail (Servers and Unix machines) + } elsif (tcp_scan ($addr, $pa_config->{'networktimeout'}, 22) == 1 || + tcp_scan ($addr, $pa_config->{'networktimeout'}, 25) == 1) { + $alive = 1; + #Check for WWW & MySQL (Webservers and systems in a DMZ) + } elsif (tcp_scan ($addr, $pa_config->{'networktimeout'}, 80) == 1 || + tcp_scan ($addr, $pa_config->{'networktimeout'}, 3306) == 1) { + $alive = 1; + } + + next unless ($alive == 1); + + $hosts_found++; + $addr_found .= $addr . " "; + + # Guess the OS + my $id_os = guess_os ($pa_config, $addr); + + # Resolve the address + my $host_name = gethostbyaddr(inet_aton($addr), AF_INET); + $host_name = $addr unless defined ($host_name); + + # Get the parent host + my $parent_id = get_host_parent ($pa_config, $addr, $dbh); + + # Add the new address + my $addr_id = add_address ($dbh, $addr); + + # Crate a new agent + my $agent_id = pandora_create_agent ($pa_config, $pa_config->{'servername'} . '_Recon', + $host_name, $addr, $addr_id, + $task->{'id_group'}, '', $parent_id, $task->{'id_os'}, $dbh); + + # Crate network profile modules for the agent + create_network_profile_modules ($pa_config, $dbh, $agent_id, $task->{'id_network_profile'}, $addr); + + # Generate an event + pandora_event ($pa_config, "[RECON] New host [$host_name] detected on network [" . $task->{'subnet'} . ']', + $task->{'id_group'}, $agent_id, 2, 0, 0, 'recon_host_detected', $dbh); + } + + # Create an incident + if ($hosts_found > 0 && $task->{'create_incident'} == 1){ + my $text = "At " . strftime ("%Y-%m-%d %H:%M:%S", localtime()) . " ($hosts_found) new hosts were detected by Pandora FMS Recon Server running on [" . $pa_config->{'servername'} . "_Recon]. This incident has been automatically created following instructions for this recon task [" . $task->{'id_group'} . "].\n\n"; + if ($task->{'id_network_profile'} > 0) { + $text .= "Aditionally, and following instruction for this task, agent(s) has been created, with modules assigned to network component profile [" . get_nc_profile_name ($dbh, $task->{'id_network_profile'}) . "]. Please check this agent as soon as possible to verify it."; + } + $text .= "\n\nThis is the list of IP addresses found: \n\n$addr_found"; + pandora_create_incident ($pa_config, $dbh, "[RECON] New hosts detected", $text, 0, 0, 'Pandora FMS Recon Server', $task->{'id_group'}); + } + + # Mark recon task as done + update_recon_task ($pa_config, $dbh, $task_id, -1); +} + +############################################################################## +# ICMP scan the given host. Returns 1 if successful, 0 otherwise. +############################################################################## +sub icmp_scan ($$) { + my ($host, $timeout) = @_; + + # Ping the host + my $ping; + { + lock $ICMPLock; + $ping = Net::Ping->new (); + } + + # Host is alive + if ($ping->ping($host)){ + $ping->close(); + return 1; + } + + return 0; +} + +############################################################################## +# TCP scan the given host/port. Returns 1 if successful, 0 otherwise. +############################################################################## +sub tcp_scan ($$$) { + my ($host, $timeout, $port) = $@; + my $rc = 0; + + eval { + local $SIG{'ALRM'} = sub { return 0; }; + alarm ($timeout); + my $handle=IO::Socket::INET->new( + Proto => 'tcp', + PeerAddr => $host, + PeerPort => $port); + $rc = 1 if ($handle); + alarm (0); + }; + + return 0 if ($@); + return $rc; +} + +########################################################################## +# Guess OS using xprobe2. +########################################################################## +sub guess_os { + my ($pa_config, $host) = @_; + + my $xprobe2 = $pa_config->{'xprobe2'}; + + # Other OS + return 10 if (! -e $xprobe2); + + # Execute xprobe2 + my $output = ''; + eval { + $output = `$xprobe2 $host 2> /dev/null | grep "Running OS" 2> /dev/null | head -1 2> /dev/null`; + }; + + # Check for errors + return 10 if ($@); + + return pandora_get_os ($output); +} + +########################################################################## +# Return 1 if the given IP address already exists in the DB, 0 if not. +########################################################################## +sub addr_exists ($$) { + my ($dbh, $ip_address) = @_; + + my @addresses = get_db_rows ($dbh, 'SELECT * FROM taddress WHERE ip = ?', $ip_address); + + # Address not found + return 0 if ($#addresses < 0); + + return 1; +} + + +########################################################################## +# Return the ID of the agent with the given IP. +########################################################################## +sub get_agent_from_addr ($$$) { + my ($pa_config, $dbh, $ip_address) = @_; + + return 0 if (! defined ($ip_address) || $ip_address eq ''); + + my $agent_id = get_db_value ($dbh, 'SELECT id_agent FROM taddress, taddress_agent WHERE taddress_agent.id_a = taddress.id_a AND ip = ?', $ip_address); + return (defined ($agent_id)) ? $agent_id : -1; +} + +########################################################################## +# Update recon task status. +########################################################################## +sub update_recon_task { + my ($pa_config, $dbh, $id_task, $status) = @_; + + db_do ($dbh, 'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?', time (), $status, $id_task); +} + +########################################################################## +# Add the given address to taddress. +########################################################################## +sub add_address ($$) { + my ($dbh, $ip_address) = @_; + + return db_insert ($dbh, 'INSERT INTO taddress (ip) VALUES (?)', $ip_address); +} + +########################################################################## +# Create network profile modules for the given agent. +########################################################################## +sub create_network_profile_modules { + my ($pa_config, $dbh, $agent_id, $np_id, $addr) = @_; + + return unless ($np_id > 0); + + # Get network components associated to the network profile + my @np_components = get_db_rows ($dbh, 'SELECT * FROM tnetwork_profile_component WHERE id_np = ?', $np_id); + + foreach my $np_component (@np_components) { + + # Get network component data + my $component = get_db_single_row ($dbh, 'SELECT * FROM tnetwork_component wHERE id_nc = ?', $np_component->{'id_nc'}); + next unless defined ($component); + + # Create the module + my $module_id = db_insert ($dbh, 'INSERT INTO tagente_modulo (id_agente, id_tipo_modulo, descripcion, nombre, max, min, module_interval, tcp_port, tcp_send, tcp_rcv, snmp_community, snmp_oid, ip_target, id_module_group, flag, disabled, plugin_user, plugin_pass, plugin_parameter, max_timeout, id_modulo ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?, ?, ?, ?)', + $agent_id, $component->{'type'}, $component->{'description'}, $component->{'name'}, $component->{'max'}, $component->{'min'}, $component->{'interval'}, $component->{'tcp_port'}, $component->{'tcp_send'}, $component->{'tcp_rcv'}, $component->{'snmp_community'}, + $component->{'snmp_oid'}, $addr, $component->{'id_module_group'}, $component->{'plugin_user'}, $component->{'plugin_pass'}, $component->{'plugin_parameter'}, $component->{'max_timeout'}, $component->{'id_modulo'}); + + logger($pa_config, 'Recon Server: Creating module ' . $component->{'name'} . " for agent $addr", 3); + + # An entry in tagente_estado is necessary for the module to work + db_insert ($dbh, 'INSERT INTO tagente_estado (id_agente_modulo, datos, timestamp, estado, id_agente, last_try, utimestamp, current_interval, running_by) + VALUES (?, \'\', \'0000-00-00 00:00:00\', 1, ?, \'0000-00-00 00:00:00\', 0, ?, 0)', + $module_id, $agent_id, $component->{'module_interval'}); + + } +} + +########################################################################## +# Returns the ID of the parent of the given host if available. +########################################################################## +sub get_host_parent ($$){ + my ($pa_config, $host, $dbh) = @_; + + # Traceroute not available + return 0 unless ($TracerouteAvailable != 0); + + my $tr = Net::Traceroute::PurePerl->new ( + backend => 'PurePerl', + host => $host, + debug => 0, + max_ttl => 15, + query_timeout => $pa_config->{'networktimeout'}, + packetlen => 40, + protocol => 'udp', # udp or icmp + ); + + my $success = 0; + + # Call traceroute + eval { + local $SIG{'ALRM'} = sub { return 0; }; + alarm($pa_config->{'networktimeout'}); + $success = $tr->traceroute(); + alarm(0); + }; + + # Error or timeout + return 0 if ($@); + + # Traceroute was not successful + return 0 if ($tr->hops < 2 || $success == 0); + + my $parent_addr = $tr->hop_query_host($tr->hops - 1, 0); + return get_agent_from_addr ($pa_config, $dbh, $parent_addr); +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/SNMPServer.pm b/pandora_server/lib/PandoraFMS/SNMPServer.pm new file mode 100644 index 0000000000..1494307659 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/SNMPServer.pm @@ -0,0 +1,178 @@ +package PandoraFMS::SNMPServer; +########################################################################## +# Pandora FMS SNMP Console. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use Time::Local; +use XML::Simple; + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::Server; + +# Load enterprise module +enterprise_load (); + +# Inherits from PandoraFMS::Server +our @ISA = qw(PandoraFMS::Server); + +######################################################################################## +# SNMP Server class constructor. +######################################################################################## +sub new ($$;$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'snmpconsole'} == 1; + + # Start snmptrapd + if (system ($config->{'snmp_trapd'} .' -t -On -n -a -Lf ' . $config->{'snmp_logfile'} . ' -p /var/run/pandora_snmptrapd.pid -F %4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]%a[**]%N[**]%w[**]%W[**]%q[**]%v\n 2>/dev/null') != 0) { + print " [E] Could not start snmptrapd.\n\n"; + return undef; + } + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 2, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + + print " [*] Starting Pandora FMS SNMP Console. \n"; + $self->SUPER::run (\&PandoraFMS::SNMPServer::pandora_snmptrapd); +} + +########################################################################## +# Process SNMP log file. +########################################################################## +sub pandora_snmptrapd { + my $self = shift; + my $pa_config = $self->getConfig (); + + # Connect to the DB + my $dbh = db_connect ('mysql', $pa_config->{'dbname'}, $pa_config->{'dbhost'}, + 3306, $pa_config->{'dbuser'}, $pa_config->{'dbpass'}); + $self->setDBH ($dbh); + + # Wait for the SNMP log file to be available + my $log_file = $pa_config->{'snmp_logfile'}; + sleep ($pa_config->{'server_threshold'}) while (! -e $log_file); + open (SNMPLOGFILE, $log_file) or return; + + # Process index file, if available + my ($idx_file, $last_line, $last_size) = ($log_file . '.index', 0, 0); + if (-e $idx_file) { + open (INDEXFILE, $idx_file) or return; + my $idx_data = ; + close INDEXFILE; + ($last_line, $last_size) = split(/\s+/, $idx_data); + } + + my $log_size = (stat ($log_file))[7]; + + # New SNMP log file found + if ($log_size < $last_size) { + unlink ($idx_file); + ($last_line, $last_size) = (0, 0); + } + + # Skip already processed lines + readline SNMPLOGFILE for (1..$last_line); + + my $trap2agent = enterprise_hook('snmp_get_trap2agent', [$dbh]); + + # Main loop + while (1) { + while (my $line = ) { + $last_line++; + $last_size = (stat ($log_file))[7]; + + # Update index file + open INDEXFILE, '>' . $idx_file; + print INDEXFILE $last_line . ' ' . $last_size; + close INDEXFILE; + + # Skip Headers + next if ($line =~ m/NET-SNMP/); + + # Unknown data + next if ($line !~ m/\[\*\*\]/); + + my ($date, $time, $source, $oid, + $type, $type_desc, $value, $data) = split(/\[\*\*\]/, $line); + + my $timestamp = $date . ' ' . $time; + $value = limpia_cadena ($value); + + my ($custom_oid, $custom_type, $custom_value) = ('', '', $type_desc); + + # Custom OID + if ($type == 6) { + + #String data + next if ($data =~ m/STRING/) && ($data !~ m/([0-9\.]*)\s\=\s([A-Za-z0-9]*)\:\s\"(.+)\"/); + + next if ($data !~ m/([0-9\.]*)\s\=\s([A-Za-z0-9]*)\:\s(.+)/); + + ($custom_oid, $custom_type, $custom_value) = ($1, $2, $3); + $custom_value = limpia_cadena ($custom_value); + } + + # Insert the trap into the DB + if (! defined(enterprise_hook ('snmp_insert_trap', [$pa_config, $source, $oid, $type, $value, $custom_oid, $custom_value, $custom_type, $timestamp, $dbh]))) { + db_insert ($dbh, 'INSERT INTO ttrap (timestamp, source, oid, type, value, oid_custom, value_custom, type_custom) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + $timestamp, $source, $oid, $type, $value, $custom_oid, $custom_value, $custom_type); + logger ($pa_config, "Received SNMP Trap from $source", 4); + + # Evaluate alerts for this trap + pandora_evaluate_snmp_alerts ($pa_config, $source, $oid, $oid, $custom_oid . ' ' . $custom_value, $timestamp, $dbh); + } + + enterprise_hook ('snmp_trap2agent', [$trap2agent, $pa_config, $source, $oid, $value, $custom_oid, $custom_value, $timestamp, $dbh]); + } + + sleep ($pa_config->{'server_threshold'}); + } +} + +######################################################################################## +# Stop the server, killing snmptrapd before. +######################################################################################## +sub stop () { + my $self = shift; + + system ('kill -9 `cat /var/run/pandora_snmptrapd.pid 2> /dev/null`'); + unlink ('/var/run/pandora_snmptrapd.pid'); + + $self->SUPER::stop (); +} + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/Server.pm b/pandora_server/lib/PandoraFMS/Server.pm new file mode 100644 index 0000000000..a3963f06a1 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/Server.pm @@ -0,0 +1,230 @@ +package PandoraFMS::Server; +########################################################################## +# Pandora FMS generic server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; + +use PandoraFMS::Core; + +# defined in PandoraFMS::Core.pm +our @ServerSuffixes; + +######################################################################################## +# Server class constructor. +######################################################################################## +sub new ($$$;$) { + my $class = shift; + my $self = { + _pa_config => shift, + _server_type => shift, + _dbh => shift, + _num_threads => 1, + _threads => [], + _queue_size => 0, + }; + + # Thread kill signal handler + #$SIG{'KILL'} = sub { + # threads->exit() if threads->can('exit'); + # exit(); + #}; + + bless $self, $class; + return $self; +} + +######################################################################################## +# Run. +######################################################################################## +sub run ($$) { + my ($self, $func) = @_; + + for (1..$self->{'_num_threads'}) { + my $thr = threads->create (\&{$func}, $self); + return unless defined ($thr); + push (@{$self->{'_threads'}}, $thr->tid ()); + } +} + +######################################################################################## +# Set the number of server threads. +######################################################################################## +sub setNumThreads ($$) { + my ($self, $num_threads) = @_; + + $self->{'_num_threads'} = $num_threads; +} + +######################################################################################## +# Set the actual server queue size (used for statistics). +######################################################################################## +sub setQueueSize ($$) { + my ($self, $size) = @_; + + $self->{'_queue_size'} = $size; +} + +######################################################################################## +# Get the number of server threads. +######################################################################################## +sub getNumThreads ($$) { + my $self = shift; + + return $self->{'_num_threads'}; +} + +######################################################################################## +# Get consumer function. +######################################################################################## +sub getConsumer ($) { + my $self = shift; + + return $self->{'_consumer'}; +} + +######################################################################################## +# Set DB handler. +######################################################################################## +sub setDBH ($$) { + my ($self, $dbh) = @_; + + $self->{'_dbh'} = $dbh; +} + +######################################################################################## +# Get DB handler. +######################################################################################## +sub getDBH ($) { + my $self = shift; + + return $self->{'_dbh'}; +} + +######################################################################################## +# Get config. +######################################################################################## +sub getConfig ($) { + my $self = shift; + + return $self->{'_pa_config'}; +} + +######################################################################################## +# Get server type. +######################################################################################## +sub getServerType ($) { + my $self = shift; + + return $self->{'_server_type'}; +} + +######################################################################################## +# Add a thread to the server thread list. +######################################################################################## +sub addThread ($$) { + my ($self, $tid) = @_; + push (@{$self->{'_threads'}}, $tid); +} + +######################################################################################## +# Returns 1 if all server threads are running, 0 otherwise. +######################################################################################## +sub checkThreads ($) { + my $self = shift; + + foreach my $tid (@{$self->{'_threads'}}) { + my $thr = threads->object ($tid); + return 0 unless defined ($thr); + #return 0 unless $thr->is_running (); + } + + return 1; +} + +######################################################################################## +# Generate a 'going up' event. +######################################################################################## +sub upEvent ($) { + my $self = shift; + + return unless defined ($self->{'_dbh'}); + pandora_event ($self->{'_pa_config'}, $self->{'_pa_config'}->{'servername'} . + $ServerTypes[$self->{'_server_type'}] . ' going UP', + 0, 0, 3, 0, 0, 'system', $self->{'_dbh'}); +} + +######################################################################################## +# Generate a 'going down' event. +######################################################################################## +sub downEvent ($) { + my $self = shift; + + return unless defined ($self->{'_dbh'}); + pandora_event ($self->{'_pa_config'}, $self->{'_pa_config'}->{'servername'} . + $ServerTypes[$self->{'_server_type'}] . ' going DOWN', + 0, 0, 4, 0, 0, 'system', $self->{'_dbh'}); +} + +######################################################################################## +# Update server status. +######################################################################################## +sub update ($) { + my $self = shift; + + eval { + pandora_update_server ($self->{'_pa_config'}, $self->{'_dbh'}, $self->{'_pa_config'}->{'servername'}, + 1, $self->{'_server_type'}, $self->{'_num_threads'}, $self->{'_queue_size'}); + }; +} + +######################################################################################## +# Stop the server, killing all server threads. +######################################################################################## +sub stop ($) { + my $self = shift; + + # Update server status + pandora_update_server ($self->{'_pa_config'}, $self->{'_dbh'}, $self->{'_pa_config'}->{'servername'}, + 0, $self->{'_server_type'}); + + # Generate an event + $self->downEvent (); + + # Kill server threads + foreach my $tid (@{$self->{'_threads'}}) { + my $thr = threads->object($tid); + next unless defined ($thr); + + # A kill method might not be available + #if ($thr->can('kill')) { + # $thr->kill('KILL')->detach(); + #} else { + $thr->detach(); + #} + } +} + +# End of function declaration +# End of defined Code + +1; +__END__ diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index da38d83707..c629dc8af6 100644 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -20,8 +20,7 @@ package PandoraFMS::Tools; use warnings; use Time::Local; -use Date::Manip; # Needed to manipulate DateTime formats of input, output and compare -use POSIX qw(setsid); +use POSIX qw(setsid strftime); use Mail::Sendmail; # New in 2.0. Used to sendmail internally, without external scripts require Exporter; @@ -43,6 +42,7 @@ our @EXPORT = qw( pandora_trash_ascii enterprise_hook enterprise_load + print_message ); ########################################################################## @@ -257,7 +257,7 @@ sub logger { $datos = "[V".$verbose_level."] ".$datos; } - my $time_now = &UnixDate("today","%Y/%m/%d %H:%M:%S"); + my $time_now = strftime ("%Y-%m-%d %H:%M:%S", localtime()); if (-e $fichero){ my $filesize = (stat($fichero))[7]; if ( $filesize > $pa_config->{'max_log_size'}) { @@ -321,25 +321,21 @@ sub float_equal { } ########################################################################## -# sub enterprise_load () # Tries to load the PandoraEnterprise module. Must be called once before # enterprise_hook (). ########################################################################## sub enterprise_load () { - eval { - require PandoraFMS::Enterprise; - }; + eval 'use PandoraFMS::Enterprise;'; + return 0 if ($@); + return 1; } - ########################################################################## -# sub enterprise_hook ($function_name, \@arguments) # Tries to call a PandoraEnterprise function. Returns undef if unsuccessful. ########################################################################## sub enterprise_hook ($$) { - my $func = $_[0]; - my @args = @{$_[1]}; - my $output; + my $func = shift; + my @args = @{shift ()}; # Temporarily disable strict refs no strict 'refs'; @@ -348,14 +344,22 @@ sub enterprise_hook ($$) { $func = 'PandoraFMS::Enterprise::' . $func; # Try to call the function - $output = eval { &$func (@args); }; + my $output = eval { &$func (@args); }; # Check for errors - if ($@) { - return undef; - } - else { - return $output; + return undef if ($@); + + return $output; +} + +########################################################################## +# Prints a message to STDOUT at the given log level. +########################################################################## +sub print_message ($$$) { + my ($pa_config, $message, $log_level) = @_; + + if ($pa_config->{'verbosity'} > $log_level){ + print STDOUT $message . "\n"; } } diff --git a/pandora_server/lib/PandoraFMS/WMIServer.pm b/pandora_server/lib/PandoraFMS/WMIServer.pm new file mode 100644 index 0000000000..b9019b91e2 --- /dev/null +++ b/pandora_server/lib/PandoraFMS/WMIServer.pm @@ -0,0 +1,195 @@ +package PandoraFMS::WMIServer; +########################################################################## +# Pandora FMS WMI Server. +# Pandora FMS. the Flexible Monitoring System. http://www.pandorafms.org +########################################################################## +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; version 2 +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +########################################################################## + +use strict; +use warnings; + +use threads; +use threads::shared; +use Thread::Semaphore; + +use POSIX qw(strftime); +use HTML::Entities; + +use PandoraFMS::Tools; +use PandoraFMS::DB; +use PandoraFMS::Core; +use PandoraFMS::ProducerConsumerServer; + +# Inherits from PandoraFMS::ProducerConsumerServer +our @ISA = qw(PandoraFMS::ProducerConsumerServer); + +# Global variables +my @TaskQueue :shared; +my %PendingTasks :shared; +my $Sem :shared = Thread::Semaphore->new; +my $TaskSem :shared = Thread::Semaphore->new (0); + +######################################################################################## +# NetworkServer class constructor. +######################################################################################## +sub new ($$;$) { + my ($class, $config, $dbh) = @_; + + return undef unless $config->{'wmiserver'} == 1; + + # Check for a WMI client + if (system ($config->{'wmi_client'} . ' > /dev/null 2>&1') != 256) { + print ' [E] ' . $config->{'wmi_client'} . " not found. Pandora FMS WMI Server needs a DCOM/WMI client.\n\n"; + return undef; + } + + # Call the constructor of the parent class + my $self = $class->SUPER::new($config, 6, \&PandoraFMS::WMIServer::data_producer, \&PandoraFMS::WMIServer::data_consumer, $dbh); + + bless $self, $class; + return $self; +} + +############################################################################### +# Run. +############################################################################### +sub run ($) { + my $self = shift; + my $pa_config = $self->getConfig (); + + print " [*] Starting Pandora FMS WMI Server. \n"; + $self->setNumThreads ($pa_config->{'wmi_threads'}); + $self->SUPER::run (\@TaskQueue, \%PendingTasks, $Sem, $TaskSem); +} + +############################################################################### +# Data producer. +############################################################################### +sub data_producer ($) { + my $self = shift; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my @tasks; + my @rows; + + if ($pa_config->{'pandora_master'} != 1) { + @rows = get_db_rows ($dbh, 'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado + WHERE server_name = ? + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.id_modulo = 6 + AND tagente_modulo.disabled = 0 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() + OR tagente_modulo.flag = 1) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC', $pa_config->{'servername'}); + } else { + @rows = get_db_rows ($dbh, 'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, UNIX_TIMESTAMP() - tagente_estado.current_interval - tagente_estado.last_execution_try AS time_left + FROM tagente, tagente_modulo, tagente_estado, tserver + WHERE ((server_name = ?) OR (server_name = ANY(SELECT server_name FROM tserver WHERE status = 0))) + AND tagente_modulo.id_agente = tagente.id_agente + AND tagente.disabled = 0 + AND tagente_modulo.disabled = 0 + AND tagente_modulo.id_modulo = 6 + AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo + AND ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() OR tagente_modulo.flag = 1 ) + ORDER BY tagente_modulo.flag DESC, time_left DESC, last_execution_try ASC', $pa_config->{'servername'}); + } + + foreach my $row (@rows) { + + # Reset forced execution flag + if ($row->{'flag'} == 1) { + db_do ($dbh, 'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?', $row->{'id_agente_modulo'}); + } + + push (@tasks, $row->{'id_agente_modulo'}); + } + + return @tasks; +} + +############################################################################### +# Data consumer. +############################################################################### +sub data_consumer ($$) { + my ($self, $module_id) = @_; + my ($pa_config, $dbh) = ($self->getConfig (), $self->getDBH ()); + + my $module = get_db_single_row ($dbh, 'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?', $module_id); + return unless defined $module; + + # Build command to execute + my $wmi_command = $pa_config->{'wmi_client'} . ' -U "' . $module->{'plugin_user'} . '"%"' . $module->{'plugin_pass'} . '"'; + + # Use a custom namespace + my $namespace = $module->{'tcp_send'}; + if ($namespace ne '') { + $namespace =~ s/\"/\'/g; + $wmi_command .= ' --namespace="' . $namespace . '"'; + } + + # WMI query + my $wmi_query = decode_entities($module->{'snmp_oid'}); + $wmi_query =~ s/\"/\'/g; + + $wmi_command .= ' //' . $module->{'ip_target'} . ' "' . $wmi_query . '"'; + logger ($pa_config, "Executing AM # $module_id WMI command '$wmi_command'", 9); + + # Execute command + my $module_data = `$wmi_command`; + if (! defined ($module_data)) { + pandora_update_module_on_error ($pa_config, $module_id, $dbh); + return; + } + + # Parse command output. Example: + # CLASS: Win32_Processor + # DeviceID|LoadPercentage + # CPU0|2 + my @output = split("\n", $module_data); + if ($#output < 2) { + pandora_update_module_on_error ($pa_config, $module_id, $dbh); + return; + } + + # Check for errors + if ($output[0] =~ /ERROR/) { + pandora_update_module_on_error ($pa_config, $module_id, $dbh); + return; + } + + # Get the first row (line 3) + my @row = split(/\|/, $output[2]); + + # Get the specified column + $module_data = $row[$module->{'tcp_port'}]; + + # Regexp + if ($module->{'snmp_community'} ne ''){ + my $filter = $module->{'snmp_community'}; + $module_data = ($module_data =~ /$filter/) ? 1 : 0; + } + + my $utimestamp = time (); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + + pandora_process_module ($pa_config, $module_data, '', $module, '', $timestamp, $utimestamp, $dbh); + pandora_update_agent ($pa_config, $timestamp, $module->{'id_agente'}, $pa_config->{'servername'} . '_WMI', $pa_config->{'version'}, -1, $dbh); +} + +1; +__END__ diff --git a/pandora_server/pandora_ctl b/pandora_server/pandora_ctl deleted file mode 100755 index 100b5ce770..0000000000 --- a/pandora_server/pandora_ctl +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -# Global Startup / Control script for Pandora FMS -# Written by A. Delgado, contributed to Pandora FMS project -# (c) A.Delgado 2008 - -if [ $# -eq 0 ] -then - -echo -e "\nPandora FMS 2.0 Control script - http://pandorafms.org -Usage: pandora start/stop [server] [server] ... - -Servers : pandora_server pandora_network pandora_recon pandora_snmpconsole \n pandora_prediction pandora_plugin pandora_wmi \n" -exit 0 -fi - - -ACCION=$1 -SERVIDOR2=$2 -SERVIDOR3=$3 -SERVIDOR4=$4 -SERVIDOR5=$5 -SERVIDOR6=$6 -SERVIDOR7=$7 -SERVIDOR8=$8 - -if [ $# -eq 1 ] -then - /etc/init.d/pandora_server $ACCION - /etc/init.d/pandora_network $ACCION - /etc/init.d/pandora_recon $ACCION - /etc/init.d/pandora_snmpconsole $ACCION - /etc/init.d/pandora_prediction $ACCION - /etc/init.d/pandora_plugin $ACCION - /etc/init.d/pandora_wmi $ACCION -else - [ ! -z $SERVIDOR2 ] && /etc/init.d/$SERVIDOR2 $ACCION && echo -e "\n$ACCION server $SERVIDOR2\n" - [ ! -z $SERVIDOR3 ] && /etc/init.d/$SERVIDOR3 $ACCION && echo -e "\n$ACCION server $SERVIDOR3\n" - [ ! -z $SERVIDOR4 ] && /etc/init.d/$SERVIDOR4 $ACCION && echo -e "\n$ACCION server $SERVIDOR4\n" - [ ! -z $SERVIDOR5 ] && /etc/init.d/$SERVIDOR5 $ACCION && echo -e "\n$ACCION server $SERVIDOR5\n" - [ ! -z $SERVIDOR6 ] && /etc/init.d/$SERVIDOR6 $ACCION && echo -e "\n$ACCION server $SERVIDOR6\n" - [ ! -z $SERVIDOR7 ] && /etc/init.d/$SERVIDOR7 $ACCION && echo -e "\n$ACCION server $SERVIDOR7\n" - [ ! -z $SERVIDOR8 ] && /etc/init.d/$SERVIDOR8 $ACCION && echo -e "\n$ACCION server $SERVIDOR8\n" -fi diff --git a/pandora_server/pandora_network b/pandora_server/pandora_network deleted file mode 100755 index 1c6cf6aa8a..0000000000 --- a/pandora_server/pandora_network +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# Pandora FMS Network Server, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.1 Build 081030 -# http://www.pandorafms.com - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_network.pid -PANDORA_DAEMON=/usr/local/bin/pandora_network - -# Main script - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f $PANDORA_DAEMON ] -then - echo "Pandora FMS Network Server not found, please check setup and read manual" - exit -fi - -case "$1" in - start) - OLD_PATH="`pwd`" - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z $PANDORA_PID ] - then - echo "Pandora FMS Network Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." - exit 1 - else - rm -f $PANDORA_PID - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z "$PANDORA_PID" ] - then - echo "Pandora Network Server is now running with PID $PANDORA_PID" - else - echo "Cannot start Pandora FMS Network Server. Aborted." - fi - cd "$OLD_PATH" - ;; - stop) - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ -z $PANDORA_PID ] - then - echo "Pandora FMS Network Server is not running, cannot stop it." - exit 1 - else - echo "Stopping Pandora FMS Network Server" - kill $PANDORA_PID > /dev/null 2>&1 - rm -f $PANDORA_PID - fi - ;; - force-reload|restart) - $0 stop - sleep 2 - $0 start - ;; - *) - echo "Usage: pandora_network {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/pandora_package_installer b/pandora_server/pandora_package_installer index 6b49b75f84..cf63f1d8e2 100755 --- a/pandora_server/pandora_package_installer +++ b/pandora_server/pandora_package_installer @@ -45,12 +45,6 @@ install () { cp pandora_* /etc/init.d rm /etc/init.d/pandora_*_installer ln -s /etc/init.d/pandora_server /etc/rc2.d/S90pandora_server 2> /dev/null - ln -s /etc/init.d/pandora_recon /etc/rc2.d/S90pandora_recon 2> /dev/null - ln -s /etc/init.d/pandora_network /etc/rc2.d/S90pandora_network 2> /dev/null - ln -s /etc/init.d/pandora_snmpconsole /etc/rc2.d/S90pandora_snmpconsole 2> /dev/null - ln -s /etc/init.d/pandora_plugin /etc/rc2.d/S90pandora_plugin 2> /dev/null - ln -s /etc/init.d/pandora_prediction /etc/rc2.d/S90pandora_prediction 2> /dev/null - ln -s /etc/init.d/pandora_wmi /etc/rc2.d/S90pandora_wmi 2> /dev/null fi mkdir /usr/share/pandora 2> /dev/null @@ -84,29 +78,10 @@ uninstall () { rm -Rf /etc/pandora/pandora_server.conf 2> /dev/null rm -Rf /etc/init.d/pandora_server 2> /dev/null - rm -Rf /etc/init.d/pandora_network 2> /dev/null - rm -Rf /etc/init.d/pandora_recon 2> /dev/null - rm -Rf /etc/init.d/pandora_snmpconsole 2> /dev/null - rm -Rf /etc/init.d/pandora_prediction 2> /dev/null - rm -Rf /etc/init.d/pandora_wmi 2> /dev/null - rm -Rf /etc/init.d/pandora_plugin 2> /dev/null rm -Rf /etc/rc2.d/S90pandora_server 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_recon 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_network 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_snmpconsole 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_wmi 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_prediction 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_plugin 2> /dev/null rm -Rf /usr/bin/pandora_exec 2> /dev/null - rm -Rf /usr/bin/pandora_wmi 2> /dev/null - rm -Rf /usr/bin/pandora_server 2> /dev/null - rm -Rf /usr/bin/pandora_snmpconsole 2> /dev/null - rm -Rf /usr/bin/pandora_recon 2> /dev/null - rm -Rf /usr/bin/pandora_network 2> /dev/null - rm -Rf /usr/bin/pandora_prediction 2> /dev/null - rm -Rf /usr/bin/pandora_plugin 2> /dev/null rm -Rf /usr/share/pandora rm -Rf /etc/cron.daily/pandora_purge_db diff --git a/pandora_server/pandora_plugin b/pandora_server/pandora_plugin deleted file mode 100755 index 5b73a49543..0000000000 --- a/pandora_server/pandora_plugin +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Pandora FMS Plugin Server, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.1 Build 081030 -# http://www.pandorafms.com - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_plugin.pid -PANDORA_DAEMON=/usr/local/bin/pandora_plugin - -# Main script - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS Plugin Server cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f $PANDORA_DAEMON ] -then - echo "Pandora FMS Plugin Server not found, please check setup and read manual" - exit -fi - -case "$1" in - start) - OLD_PATH="`pwd`" - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z $PANDORA_PID ] - then - echo "Pandora FMS Plugin Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." - exit 1 - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z "$PANDORA_PID" ] - then - echo "Pandora FMS Plugin Server is now running with PID $PANDORA_PID" - else - echo "Cannot start Pandora FMS Plugin Server. Aborted." - fi - cd "$OLD_PATH" - ;; - stop) - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ -z $PANDORA_PID ] - then - echo "Pandora FMS Plugin Server is not running, cannot stop it." - exit 1 - else - echo "Stopping Pandora FMS Plugin Server" - kill $PANDORA_PID > /dev/null 2>&1 - rm -f $PANDORA_PID - fi - ;; - force-reload|restart) - $0 stop - sleep 2 - $0 start - ;; - *) - echo "Usage: pandora_plugin {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/pandora_prediction b/pandora_server/pandora_prediction deleted file mode 100755 index 22eb9c6d27..0000000000 --- a/pandora_server/pandora_prediction +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# Pandora FMS Prediction Server, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.1 Build 081030 -# http://www.pandorafms.com - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_prediction.pid -PANDORA_DAEMON=/usr/local/bin/pandora_prediction - -# Main script - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f $PANDORA_DAEMON ] -then - echo "Pandora FMS Prediction Server not found, please check setup and read manual" - exit -fi - -case "$1" in - start) - OLD_PATH="`pwd`" - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z $PANDORA_PID ] - then - echo "Pandora FMS Prediction Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." - exit 1 - else - rm -f $PANDORA_PID - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z "$PANDORA_PID" ] - then - echo "Pandora FMS Prediction Server is now running with PID $PANDORA_PID" - else - echo "Cannot start Pandora FMS Prediction Server. Aborted." - fi - cd "$OLD_PATH" - ;; - stop) - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ -z $PANDORA_PID ] - then - echo "Pandora FMS Prediction Server is not running, cannot stop it." - exit 1 - else - echo "Stopping Pandora FMS Prediction Server" - kill $PANDORA_PID > /dev/null 2>&1 - rm -f $PANDORA_PID - fi - ;; - force-reload|restart) - $0 stop - sleep 2 - $0 start - ;; - *) - echo "Usage: pandora_prediction {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/pandora_recon b/pandora_server/pandora_recon deleted file mode 100755 index b9f4d00dbe..0000000000 --- a/pandora_server/pandora_recon +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Pandora FMS Recon Server, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.1 Build 081030 -# http://www.pandorafms.com - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_recon.pid -PANDORA_DAEMON=/usr/local/bin/pandora_recon - -# Main script - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f $PANDORA_DAEMON ] -then - echo "Pandora FMS Recon Server not found, please check setup and read manual" - exit -fi - -case "$1" in - start) - OLD_PATH="`pwd`" - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z $PANDORA_PID ] - then - echo "Pandora FMS Recon Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." - exit 1 - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z "$PANDORA_PID" ] - then - echo "Pandora Recon Server is now running with PID $PANDORA_PID" - else - echo "Cannot start Pandora FMS Recon Server. Aborted." - fi - cd "$OLD_PATH" - ;; - stop) - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ -z $PANDORA_PID ] - then - echo "Pandora FMS Recon Server is not running, cannot stop it." - exit 1 - else - echo "Stopping Pandora FMS Recon Server" - kill $PANDORA_PID > /dev/null 2>&1 - rm -f $PANDORA_PID - fi - ;; - force-reload|restart) - $0 stop - sleep 2 - $0 start - ;; - *) - echo "Usage: pandora_recon {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/pandora_server b/pandora_server/pandora_server index 58090a129e..8d325d8ad2 100755 --- a/pandora_server/pandora_server +++ b/pandora_server/pandora_server @@ -1,6 +1,6 @@ #!/bin/bash -# Pandora FMS Data Server, startup script +# Pandora FMS Server, startup script # Copyright (c) 2006-2008 Sancho Lerena, # Linux Version (generic) # v2.1 Build 081030 @@ -9,7 +9,7 @@ # Configurable path and filenames PANDORA_HOME="/etc/pandora/pandora_server.conf" PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_dataserver.pid +PANDORA_PID=$PANDORA_PID_PATH/pandora_server.pid PANDORA_DAEMON=/usr/local/bin/pandora_server # Main script @@ -22,7 +22,7 @@ fi if [ ! -f $PANDORA_DAEMON ] then - echo "Pandora FMS Data Server not found, please check setup and read manual" + echo "Pandora FMS Server not found, please check setup and read manual" exit fi @@ -32,7 +32,7 @@ case "$1" in PANDORA_PID=$(pidof -x $PANDORA_DAEMON) if [ ! -z $PANDORA_PID ] then - echo "Pandora FMS Data Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." + echo "Pandora FMS Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." exit 1 else rm -f $PANDORA_PID @@ -44,9 +44,9 @@ case "$1" in PANDORA_PID=$(pidof -x $PANDORA_DAEMON) if [ ! -z "$PANDORA_PID" ] then - echo "Pandora Data Server is now running with PID $PANDORA_PID" + echo "Pandora Server is now running with PID $PANDORA_PID" else - echo "Cannot start Pandora FMS Data Server. Aborted." + echo "Cannot start Pandora FMS Server. Aborted." fi cd "$OLD_PATH" ;; @@ -54,10 +54,10 @@ case "$1" in PANDORA_PID=$(pidof -x $PANDORA_DAEMON) if [ -z $PANDORA_PID ] then - echo "Pandora FMS Data Server is not running, cannot stop it." + echo "Pandora FMS Server is not running, cannot stop it." exit 1 else - echo "Stopping Pandora FMS Data Server" + echo "Stopping Pandora FMS Server" kill $PANDORA_PID > /dev/null 2>&1 rm -f $PANDORA_PID fi diff --git a/pandora_server/pandora_server_installer b/pandora_server/pandora_server_installer index 60cd8b3898..7997284871 100755 --- a/pandora_server/pandora_server_installer +++ b/pandora_server/pandora_server_installer @@ -67,31 +67,12 @@ else cp pandora_* /etc/init.d/ rm /etc/init.d/pandora_server_installer ln -s /etc/init.d/pandora_server /etc/rc2.d/S90pandora_server - ln -s /etc/init.d/pandora_recon /etc/rc2.d/S90pandora_recon - ln -s /etc/init.d/pandora_network /etc/rc2.d/S90pandora_network - ln -s /etc/init.d/pandora_snmpconsole /etc/rc2.d/S90pandora_snmpconsole - ln -s /etc/init.d/pandora_plugin /etc/rc2.d/S90pandora_plugin - ln -s /etc/init.d/pandora_prediction /etc/rc2.d/S90pandora_prediction - ln -s /etc/init.d/pandora_wmi /etc/rc2.d/S90pandora_wmi fi if [ -e /usr/local/bin/pandora_server ] then ln -s /usr/local/bin/pandora_server /usr/bin/pandora_server - ln -s /usr/local/bin/pandora_snmpconsole /usr/bin/pandora_snmpconsole - ln -s /usr/local/bin/pandora_recon /usr/bin/pandora_recon - ln -s /usr/local/bin/pandora_network /usr/bin/pandora_network - ln -s /usr/local/bin/pandora_prediction /usr/bin/pandora_prediction - ln -s /usr/local/bin/pandora_plugin /usr/bin/pandora_plugin - ln -s /usr/local/bin/pandora_wmi /usr/bin/pandora_wmi - else ln -s /usr/bin/pandora_server /usr/local/bin/pandora_server - ln -s /usr/bin/pandora_network /usr/local/bin/pandora_network - ln -s /usr/bin/pandora_recon /usr/local/bin/pandora_recon - ln -s /usr/bin/pandora_plugin /usr/local/bin/pandora_plugin - ln -s /usr/bin/pandora_prediction /usr/local/bin/pandora_prediction - ln -s /usr/bin/pandora_snmpconsole /usr/local/bin/pandora_snmpconsole - ln -s /usr/bin/pandora_wmi /usr/local/bin/pandora_wmi fi mkdir /usr/share/pandora @@ -126,36 +107,12 @@ uninstall () { rm -Rf /var/log/pandora/ 2> /dev/null rm -Rf /etc/pandora/pandora_server.conf 2> /dev/null rm -Rf /etc/init.d/pandora_server 2> /dev/null - rm -Rf /etc/init.d/pandora_network 2> /dev/null - rm -Rf /etc/init.d/pandora_recon 2> /dev/null - rm -Rf /etc/init.d/pandora_snmpconsole 2> /dev/null - rm -Rf /etc/init.d/pandora_prediction 2> /dev/null - rm -Rf /etc/init.d/pandora_wmi 2> /dev/null - rm -Rf /etc/init.d/pandora_plugin 2> /dev/null rm -Rf /etc/rc2.d/S90pandora_server 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_recon 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_network 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_snmpconsole 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_wmi 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_prediction 2> /dev/null - rm -Rf /etc/rc2.d/S90pandora_plugin 2> /dev/null rm -Rf /usr/local/bin/pandora_server 2> /dev/null - rm -Rf /usr/local/bin/pandora_snmpconsole 2> /dev/null - rm -Rf /usr/local/bin/pandora_recon 2> /dev/null - rm -Rf /usr/local/bin/pandora_network 2> /dev/null - rm -Rf /usr/local/bin/pandora_wmi 2> /dev/null - rm -Rf /usr/local/bin/pandora_prediction 2> /dev/null - rm -Rf /usr/local/bin/pandora_plugin 2> /dev/null - rm -Rf /usr/bin/pandora_wmi 2> /dev/null rm -Rf /usr/bin/pandora_server 2> /dev/null - rm -Rf /usr/bin/pandora_snmpconsole 2> /dev/null - rm -Rf /usr/bin/pandora_recon 2> /dev/null - rm -Rf /usr/bin/pandora_network 2> /dev/null - rm -Rf /usr/bin/pandora_prediction 2> /dev/null - rm -Rf /usr/bin/pandora_plugin 2> /dev/null rm -Rf /usr/share/pandora rm -Rf /etc/cron.daily/pandora_purge_db diff --git a/pandora_server/pandora_snmpconsole b/pandora_server/pandora_snmpconsole deleted file mode 100755 index f4db5aecbd..0000000000 --- a/pandora_server/pandora_snmpconsole +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/bash - -# Pandora FMS SNMP Console, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.0 Build 080903 -# http://www.pandorafms.com - -# Compatible with NetSNMP 5.1 or higher - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run/" -PANDORA_PID=$PANDORA_PID_PATH/pandora_snmp.pid -PANDORA_DAEMON=/usr/local/bin/pandora_snmpconsole - -DAEMON_LOG="/var/log/pandora/pandora_snmptrap.log" -DAEMON_PID="$PANDORA_PID_PATH/pandora_snmptrapd.pid" -DAEMON_PATH=/usr/sbin/snmptrapd - -# Dont touch this call unless you know are doing. -# For different versions of NetSNMP Trap daemon, it's possible you need to change some options -# Please refer NetSNMP documentation in that case. -DAEMON_OPTIONS="-t -On -n -a -Lf $DAEMON_LOG -p $DAEMON_PID -F %4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]%a[**]%N[**]%w[**]%W[**]%q[**]%v\n" -DAEMON="$DAEMON_PATH $DAEMON_OPTIONS" - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f "$DAEMON_PATH" ] -then - echo "NetSNMP snmptrapd not found at $DAEMON_PATH, please check setup and read manual" - exit -fi - -case "$1" in - start) - if [ -f $DAEMON_PID ] - then - CHECK_PID=`cat $DAEMON_PID` - CHECK_PID_RESULT=`ps aux | grep -v grep | grep "$CHECK_PID" | grep "snmp" | wc -l` - if [ $CHECK_PID_RESULT == 1 ] - then - echo "NetSNMP Trap daemon is currently running on this machine with PID ($CHECK_PID). Aborting now..." - exit - fi - fi - - SNMP_WARNING=`ps aux | grep "$DAEMON_PATH" | grep -v grep | wc -l` - if [ $SNMP_WARNING == 1 ] - then - echo "WARNING: Seems to be already running a SNMP trap daemon on this system not associated with Pandora FMS" - echo "Check manually if their output logfile is been used for Pandora FMS and it's format is correct" - fi - - # Launch SNMP TRAP Daemon - $DAEMON - - sleep 1 - if [ ! -f $DAEMON_LOG ] - then - echo "Problem with NetSNMP Trap daemon (Logfile $DAEMON_LOG not found!). Aborting" - exit - fi - - if [ -f $DAEMON_PID ] - then - CHECK_PID=`cat $DAEMON_PID` - CHECK_PID_RESULT=`ps aux | grep -v grep | grep "$CHECK_PID" | grep "snmp" | wc -l` - if [ $CHECK_PID_RESULT != 1 ] - then - echo "Problem starting NetSNMP Trap daemon on this machine (PID File not correct). Aborting now..." - exit - fi - else - echo "Problem starting NetSNMP Trap daemon on this machine (Cannot get PID File). Aborting now..." - exit - fi - - RESULT="`cat $DAEMON_LOG | grep 'errno 13' 2> /dev/null`" - if [ -z "$RESULT" ] - then - echo "NetSNMP Trap daemon is now running with PID `cat $DAEMON_PID 2> /dev/null`" - else - echo "NetSNMP cannot open port (udp/162). Please execute as root user" - rm -f $DAEMON_PID 2> /dev/null - fi - - if [ -f $PANDORA_PID ] - then - CHECK_PID=`cat $PANDORA_PID` - CHECK_PID_RESULT=`ps aux | grep -v grep | grep "$CHECK_PID" | grep "pandora_snmpconsole" | wc -l` - if [ $CHECK_PID_RESULT == 1 ] - then - echo "Pandora FMS SNMP Server is currently running on this machine with PID ($CHECK_PID). Aborting now..." - exit - fi - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - MYPID=`ps aux | grep "$PANDORA_DAEMON" | grep -v grep | tail -1 | awk '{print $2}'` - if [ ! -z "$MYPID" ] - then - echo $MYPID > $PANDORA_PID - echo "Pandora FMS SNMP Server is now running with PID $MYPID" - else - echo "Cannot start Pandora FMS SNMP Server. Aborted" - fi - cd "$OLD_PATH" - ;; - stop) - if [ -f $DAEMON_PID ] - then - echo "Stopping NetSNMP Trap daemon" - PID_1=`cat $DAEMON_PID` - if [ ! -z "`ps -F -p $PID_1 | grep -v grep | grep 'snmptrapd'`" ] - then - kill -9 `cat $DAEMON_PID 2> /dev/null` - fi - rm -f $DAEMON_PID 2> /dev/null > /dev/null - rm -f $DAEMON_LOG 2> /dev/null > /dev/null - else - echo "NetSNMP Trap daemon is not running, cannot stop it." - fi - - if [ -f $PANDORA_PID ] - then - echo "Stopping Pandora SNMP Console" - PID_2=`cat $PANDORA_PID` - if [ ! -z "`ps -F -p $PID_2 | grep -v grep | grep 'pandora_snmpconsole'`" ] - then - kill `cat $PANDORA_PID` 2> /dev/null > /dev/null - fi - rm -f $PANDORA_PID - else - echo "Pandora FMS SNMP Console is not running, cannot stop it." - fi - ;; - force-reload|restart) - $0 stop - $0 start - ;; -*) - echo "Usage: pandora_snmpconsole {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/pandora_wmi b/pandora_server/pandora_wmi deleted file mode 100755 index 00d918afc1..0000000000 --- a/pandora_server/pandora_wmi +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Pandora FMS WMI Server, startup script -# Copyright (c) 2006-2008 Sancho Lerena, -# Linux Version (generic) -# v2.1 Build 081030 -# http://www.pandorafms.com - -# Configurable path and filenames -PANDORA_HOME="/etc/pandora/pandora_server.conf" -PANDORA_PID_PATH="/var/run" -PANDORA_PID=$PANDORA_PID_PATH/pandora_wmi.pid -PANDORA_DAEMON=/usr/local/bin/pandora_wmi - -# Main script - -if [ ! -d "$PANDORA_PID_PATH" ] -then - echo "Pandora FMS WMI Server cannot write it's PID file in $PANDORA_PID_PATH. Please create that directory" - exit -fi - -if [ ! -f $PANDORA_DAEMON ] -then - echo "Pandora FMS WMI Server not found, please check setup and read manual" - exit -fi - -case "$1" in - start) - OLD_PATH="`pwd`" - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z $PANDORA_PID ] - then - echo "Pandora FMS WMI Server is currently running on this machine with PID ($PANDORA_PID). Aborting now..." - exit 1 - fi - - $PANDORA_DAEMON $PANDORA_HOME -D - sleep 1 - - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ ! -z "$PANDORA_PID" ] - then - echo "Pandora FMS WMI Server is now running with PID $PANDORA_PID" - else - echo "Cannot start Pandora FMS WMI Server. Aborted." - fi - cd "$OLD_PATH" - ;; - stop) - PANDORA_PID=$(pidof -x $PANDORA_DAEMON) - if [ -z $PANDORA_PID ] - then - echo "Pandora FMS WMI Server is not running, cannot stop it." - exit 1 - else - echo "Stopping Pandora FMS WMI Server" - kill $PANDORA_PID > /dev/null 2>&1 - rm -f $PANDORA_PID - fi - ;; - force-reload|restart) - $0 stop - sleep 2 - $0 start - ;; - *) - echo "Usage: pandora_plugin {start|stop|restart}" - exit 1 -esac - diff --git a/pandora_server/util/pandora_dbstress.pl b/pandora_server/util/pandora_dbstress.pl index 3c0781b12f..1939eb8c21 100755 --- a/pandora_server/util/pandora_dbstress.pl +++ b/pandora_server/util/pandora_dbstress.pl @@ -17,6 +17,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ################################################################################ +use POSIX qw(strftime); + # Configure here target (AGENT_ID for Stress) my $target_module = -1; # -1 for all modules of that agent @@ -29,15 +31,14 @@ my $target_days = 30; # Includes list use strict; -use Time::Local; # DateTime basic manipulation use DBI; # DB interface with MySQL -use Date::Manip; # Date/Time manipulation use Math::Trig; # Math functions # Pandora Modules use PandoraFMS::Config; use PandoraFMS::Tools; use PandoraFMS::DB; +use PandoraFMS::Core; ################################################################################ ################################################################################ @@ -55,7 +56,7 @@ my %pa_config; pandora_init(\%pa_config,"Pandora DB Stress tool"); # Read config file for Global variables -pandora_loadconfig (\%pa_config,0); #Start like a data server +pandora_load_config (\%pa_config,0); #Start like a data server # open database, only ONCE. We pass reference to DBI handler ($dbh) to all subprocess my $dbh = DBI->connect("DBI:mysql:$pa_config{'dbname'}:$pa_config{'dbhost'}:3306",$pa_config{'dbuser'}, $pa_config{'dbpass'}, { RaiseError => 1, AutoCommit => 1 }); @@ -79,11 +80,10 @@ if ($target_agent ne -1){ my $s_idag = $dbh->prepare($query_idag); $s_idag ->execute; -my @data; if ($s_idag->rows != 0) { - while (@data = $s_idag->fetchrow_array()) { + while (my $module = $s_idag->fetchrow_hashref()) { # Fill this module with data ! - process_module (\%pa_config, $data[0], $target_interval, $data[4], $target_days, $data[2], $data[1], $dbh); + process_module (\%pa_config, $module, $target_interval, $target_days, $dbh); } } $s_idag->finish(); @@ -99,35 +99,31 @@ print " [*] All work done\n\n"; # (random) or periodic curve (cuve) values. ############################################################################## -sub process_module(){ - my $pa_config = $_[0]; - my $id_agentemodulo = $_[1]; - my $target_interval = $_[2]; - my $target_name = $_[3]; - my $target_days = $_[4]; - my $target_type = $_[5]; - my $target_agent = $_[6]; - my $dbh = $_[7]; +sub process_module($$$$$){ + my ($pa_config, $module, $target_interval, $target_days, $dbh) = @_; + + my $id_agentemodulo = $module->{'id_agente_modulo'}; + my $target_name = $module->{'nombre'}; + my $target_agent = $module->{'id_agente'}; my $factor; - $target_type = dame_nombretipomodulo_idagentemodulo ($pa_config, $target_type, $dbh); + my $valor = 0; # value storage for data generation my $a; # loopcounter my $b; # counter print " [*] Processing module $target_name \n"; - my $agent_name = dame_agente_nombre ($pa_config, $target_agent, $dbh); + my $agent_name = get_agent_name ($dbh, $target_agent); my $err; # not used # Init start time to now - target_days - my $fecha_actual = &UnixDate("today","%Y-%m-%d %H:%M:%S"); - my $m_timestamp = DateCalc($fecha_actual,"- $target_days days",\$err); - my $mysql_date; - my $bUpdateDatos; + my $utimestamp = time () - (86400 * $target_days); + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); + # Calculate how many iterations need to fill data range # $target_days*min*sec / $target_interval my $iterations = ($target_days * 24 * 60 * 60) / $target_interval; - print " [D] ID_AgenteMoludo $id_agentemodulo Interval $target_interval ModuleName $target_name Days $target_days Type $target_type Agent $target_agent \n"; + print " [D] ID_AgenteMoludo $id_agentemodulo Interval $target_interval ModuleName $target_name Days $target_days Agent $target_agent \n"; open (LOG,">> pandora_dbstress.log"); # Generate MATH/Curve data for beautiful Drawings @@ -141,18 +137,19 @@ sub process_module(){ if ($b > 180){ $b =0; } - $m_timestamp = DateCalc($m_timestamp,"+ $target_interval seconds",\$err); - $mysql_date = &UnixDate($m_timestamp,"%Y-%m-%d %H:%M:%S"); + $utimestamp += $target_interval; + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); $valor = $valor * $b * 10; $valor = sprintf("%.2f", $valor); - $valor =~ s/\,/\./g; + $valor =~ s/\,/\./g; if (($a % 20) == 0) { print "\r -> ".int($a / ($iterations / 100))."% generated for ($target_name) "; } - pandora_lastagentcontact($pa_config, $mysql_date, $agent_name, "none","1.2", $target_interval, $dbh); + pandora_update_agent($pa_config, $timestamp, $target_agent, "none","1.2", $target_interval, $dbh); # print LOG $mysql_date, $target_name, $valor, "\n"; - pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); - pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); + pandora_process_module ($pa_config, $valor, '', $module, '', '', $utimestamp, $dbh); + #pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); + #pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); } } @@ -163,15 +160,16 @@ sub process_module(){ $valor = rand(15) + rand(15) + rand(15) + rand(15) + rand(15) + rand(15); $valor = sprintf("%.2f", $valor); $valor =~ s/\,/\./g; - $m_timestamp = DateCalc($m_timestamp,"+ $target_interval seconds",\$err); - $mysql_date = &UnixDate($m_timestamp,"%Y-%m-%d %H:%M:%S"); + $utimestamp += $target_interval; + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); if ($a % 20 == 0) { print "\r -> ".int($a / ($iterations / 100))."% generated for ($target_name) "; } - pandora_lastagentcontact($pa_config, $mysql_date, $agent_name, "none","1.2", $target_interval, $dbh); + pandora_update_agent($pa_config, $timestamp, $target_agent, "none","1.2", $target_interval, $dbh); #print LOG $mysql_date, $target_name, $valor, "\n"; - pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); - pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); + pandora_process_module ($pa_config, $valor, '', $module, '', '', $utimestamp, $dbh); + #pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); + #pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); } } @@ -185,15 +183,16 @@ sub process_module(){ } else { $valor = 0; } - $m_timestamp = DateCalc($m_timestamp,"+ $target_interval seconds",\$err); - $mysql_date = &UnixDate($m_timestamp,"%Y-%m-%d %H:%M:%S"); + $utimestamp += $target_interval; + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); if ($a % 20 eq 0) { print "\r -> ".int($a / ($iterations / 100))."% generated for ($target_name) "; } - pandora_lastagentcontact($pa_config, $mysql_date, $agent_name, "none","1.2", $target_interval, $dbh); + pandora_update_agent($pa_config, $timestamp, $target_agent, "none","1.2", $target_interval, $dbh); #print LOG $mysql_date, $target_name, $valor, "\n"; - pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); - pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,$valor,$dbh,$bUpdateDatos); + pandora_process_module ($pa_config, $valor, '', $module, '', '', $utimestamp, $dbh); + #pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); + #pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,$valor,$dbh,$bUpdateDatos); } } @@ -202,15 +201,16 @@ sub process_module(){ if ( $target_name =~ /text/i ){ for ($a=1;$a<$iterations;$a++){ $valor = pandora_trash_ascii (rand(100)+50); - $m_timestamp = DateCalc($m_timestamp,"+ $target_interval seconds",\$err); - $mysql_date = &UnixDate($m_timestamp,"%Y-%m-%d %H:%M:%S"); + $utimestamp += $target_interval; + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime($utimestamp)); if ($a % 20 eq 0) { print "\r -> ".int($a / ($iterations / 100))."% generated for ($target_name) "; } - pandora_lastagentcontact($pa_config, $mysql_date, $agent_name, "none","1.2", $target_interval, $dbh); + pandora_update_agent($pa_config, $timestamp, $target_agent, "none","1.2", $target_interval, $dbh); #print LOG $mysql_date, $target_name, $valor, "\n"; - pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); - pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); + pandora_process_module ($pa_config, $valor, '', $module, '', '', $utimestamp, $dbh); + #pandora_writedata($pa_config,$mysql_date,$agent_name,$target_type,$target_name,$valor,0,0,"",$dbh,\$bUpdateDatos); + #pandora_writestate ($pa_config,$agent_name,$target_type,$target_name,$valor,100,$dbh,$bUpdateDatos); } }