From daaa63e1925a8c419d2aee674c399e1e50ac5355 Mon Sep 17 00:00:00 2001 From: slerena <slerena@gmail.com> Date: Thu, 13 Mar 2008 18:33:44 +0000 Subject: [PATCH] 2008-03-13 Sancho Lerena <slerena@gmail.com> * lib/PandoraFMS/Config.pm: New pandora_startlog function. Added support to manage PID in daemon mode. Added support for quiet mode. Added prediction_threads option. * lib/PandoraFMS/Tools.pm: daemonize function now manages PID and store in a file when it forks. * lib/PandoraFMS/DB.pm: Removed some gotos. Modified generic-access DB functions and added one to manage entire row in a hash: get_db_free_row (). * bin/pandora_plugin: A lot of fixes. This code actually works fine :-) * bin/pandora_prediction: First version of usable code. Works but not seriously tested. * bin/pandora_network: Adjusted to work with new features (quiet mode) and new db schema. Some code cleanup. * util/pandora_dbstress.pl: Updated default values. * util/pandora_dbstress.README: Improved README documentation for dbstress tool. git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@747 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f --- pandora_server/ChangeLog | 22 ++ pandora_server/bin/pandora_network | 73 ++-- pandora_server/bin/pandora_plugin | 133 ++++--- pandora_server/bin/pandora_prediction | 413 ++++++++++++++++++++ pandora_server/lib/PandoraFMS/Config.pm | 165 +++++--- pandora_server/lib/PandoraFMS/DB.pm | 230 ++++++----- pandora_server/lib/PandoraFMS/Tools.pm | 41 +- pandora_server/util/pandora_dbstress.README | 30 +- pandora_server/util/pandora_dbstress.pl | 4 +- 9 files changed, 858 insertions(+), 253 deletions(-) create mode 100755 pandora_server/bin/pandora_prediction diff --git a/pandora_server/ChangeLog b/pandora_server/ChangeLog index 7ab443a1fd..3327ed392d 100644 --- a/pandora_server/ChangeLog +++ b/pandora_server/ChangeLog @@ -1,3 +1,25 @@ +2008-03-13 Sancho Lerena <slerena@gmail.com> + + * lib/PandoraFMS/Config.pm: New pandora_startlog function. Added support to manage PID + in daemon mode. Added support for quiet mode. Added prediction_threads option. + + * lib/PandoraFMS/Tools.pm: daemonize function now manages PID and store in a file when + it forks. + + * lib/PandoraFMS/DB.pm: Removed some gotos. Modified generic-access DB functions and + added one to manage entire row in a hash: get_db_free_row (). + + * bin/pandora_plugin: A lot of fixes. This code actually works fine :-) + + * bin/pandora_prediction: First version of usable code. Works but not seriously tested. + + * bin/pandora_network: Adjusted to work with new features (quiet mode) and new db + schema. Some code cleanup. + + * util/pandora_dbstress.pl: Updated default values. + + * util/pandora_dbstress.README: Improved README documentation for dbstress tool. + 2008-03-11 Manuel Arostegui <marostegui@artica.es> * pandora_server, pandora_network, pandora_recon: diff --git a/pandora_server/bin/pandora_network b/pandora_server/bin/pandora_network index 244d3b0c14..c6c49ebc7d 100755 --- a/pandora_server/bin/pandora_network +++ b/pandora_server/bin/pandora_network @@ -62,13 +62,20 @@ pandora_loadconfig (\%pa_config,1); # Audit server starting pandora_audit (\%pa_config, "Pandora FMS Network Daemon starting", "SYSTEM", "System"); -print " [*] Starting up network threads\n"; - -if ( $pa_config{"daemon"} eq "1" ) { - print " [*] Backgrounding Pandora FMS Network Server process.\n\n"; - &daemonize; +# Thread startup +if ($pa_config{"quiet"} == 0){ + print " [*] Starting up network threads\n"; } +# Daemonize and put in background +if ( $pa_config{"daemon"} eq "1" ){ + if ($pa_config{"quiet"} eq "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++){ @@ -78,9 +85,15 @@ for (my $ax=0; $ax < $pa_config{'network_threads'}; $ax++){ # Launch now the network producer thread threads->new( \&pandora_network_producer, \%pa_config); -print " [*] All threads loaded and running \n\n"; -# 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); + +# 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", @@ -122,7 +135,9 @@ sub pandora_network_consumer ($$) { my $pa_config = $_[0]; my $thread_id = $_[1]; - print " [*] Starting up Network Consumer Thread # $thread_id \n"; + if ($pa_config->{"quiet"} == 0){ + print " [*] Starting up Network Consumer Thread # $thread_id \n"; + } my $data_id_agent_module; # Create Database handler @@ -170,7 +185,10 @@ sub pandora_network_consumer ($$) { sub pandora_network_producer ($) { my $pa_config = $_[0]; - print " [*] Starting up Network Producer Thread ...\n"; + + if ($pa_config->{"quiet"} == 0){ + 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 }); @@ -192,13 +210,17 @@ sub pandora_network_producer ($) { FROM tagente, tagente_modulo, tagente_estado WHERE - id_server = $server_id + id_network_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.disabled = 0 AND - tagente_modulo.id_tipo_modulo > 4 + 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 ( @@ -215,12 +237,16 @@ sub pandora_network_producer ($) { FROM tagente, tagente_modulo, tagente_estado, tserver WHERE - ( (tagente.id_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR - (tagente.id_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_server = tserver.id_server AND tserver.status = 0) + ( (tagente.id_network_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR + (tagente.id_network_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_network_server = tserver.id_server AND tserver.status = 0) ) 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 @@ -600,10 +626,12 @@ sub exec_network_module { } } - # -------------------------------------------------------- + # 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; @@ -626,17 +654,19 @@ sub exec_network_module { } else { logger ($pa_config, "Problem with unknown module type '$tipo_modulo'", 0); - goto skipdb_execmod; + $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 { - # $module_result != 0) - + } + + if ($module_result != 0) { + if ($module_interval == 0){ - $module_interval = dame_intervalo ($pa_config, $id_agente, $dbh); - } + $module_interval = dame_intervalo ($pa_config, $id_agente, $dbh); + } + # Modules who cannot connect or something go bad, update last_execution_try field logger ($pa_config, "Cannot obtain exec Network Module $nombre from agent $agent_name", 4); my $query_act = "UPDATE tagente_estado SET current_interval = $module_interval, last_execution_try = $utimestamp WHERE id_agente_modulo = $id_agente_modulo "; @@ -644,7 +674,4 @@ sub exec_network_module { $a_idages->execute; $a_idages->finish(); } - -skipdb_execmod: - #$dbh->disconnect(); } diff --git a/pandora_server/bin/pandora_plugin b/pandora_server/bin/pandora_plugin index aa5e567e89..b8bcc51064 100755 --- a/pandora_server/bin/pandora_plugin +++ b/pandora_server/bin/pandora_plugin @@ -57,27 +57,30 @@ pandora_loadconfig (\%pa_config, 4); # Audit server starting pandora_audit (\%pa_config, "Pandora FMS Plugin server starting", "SYSTEM", "System"); -print " [*] Starting up plugin threads\n"; - -die "Aqui me quedo"; - -if ( $pa_config{"daemon"} eq "1" ) { - print " [*] Backgrounding Pandora FMS Plugin Server process.\n\n"; - &daemonize; +# 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); } -=for COMMENT BLOCK # 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); } -=cut + # Launch now the producer thread threads->new( \&pandora_plugin_producer, \%pa_config); -print " [*] All threads loaded and running \n\n"; # 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'}; @@ -88,7 +91,7 @@ my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost:3306", # Server keepalive thread running in main thread on a infinite loop while (1) { - pandora_serverkeepaliver (\%pa_config, 1, $dbh); + pandora_serverkeepaliver (\%pa_config, 4, $dbh); threads->yield; sleep ($pa_config{"server_threshold"}); } @@ -120,7 +123,9 @@ sub pandora_plugin_consumer ($$) { my $pa_config = $_[0]; my $thread_id = $_[1]; - print " [*] Starting up Plugin Consumer Thread # $thread_id \n"; + if ($pa_config->{"quiet"} == 0){ + print " [*] Starting up Plugin Consumer Thread # $thread_id \n"; + } my $data_id_agent_module; # Create Database handler @@ -139,6 +144,7 @@ sub pandora_plugin_consumer ($$) { { lock $queue_lock; $data_id_agent_module = shift(@pending_task); +#print "[CLIENT] Pop out of queue module (pending queue) $data_id_agent_module \n"; delete($pending_task_hash{$data_id_agent_module}); $current_task_hash{$data_id_agent_module}=1; } @@ -147,6 +153,7 @@ sub pandora_plugin_consumer ($$) { eval { # Call network execution process # exec_network_module ( $pa_config, $data_id_agent_module, $dbh); +print "[CLIENT] Executing module $data_id_agent_module \n"; exec_plugin_module ($pa_config, $data_id_agent_module, $dbh); }; if ($@){ @@ -158,6 +165,7 @@ sub pandora_plugin_consumer ($$) { # not been processed, but has been freed from task queue { lock $queue_lock; +#print "[CLIENT] Removing from queue (current task) module $data_id_agent_module \n"; delete($current_task_hash{$data_id_agent_module}); } $counter = 0; @@ -191,13 +199,15 @@ sub pandora_plugin_producer ($) { FROM tagente, tagente_modulo, tagente_estado WHERE - id_server = $server_id + 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.id_tipo_modulo > 4 + tagente_modulo.disabled = 0 AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo AND ( @@ -214,23 +224,28 @@ sub pandora_plugin_producer ($) { FROM tagente, tagente_modulo, tagente_estado, tserver WHERE - ( (tagente.id_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR - (tagente.id_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_server = tserver.id_server AND tserver.status = 0) + ( (tagente.id_plugin_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR + (tagente.id_plugin_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_plugin_server = tserver.id_server AND tserver.status = 0) ) AND tagente.disabled = 0 + AND + tagente_modulo.disabled = 0 AND - tagente_modulo.id_tipo_modulo > 4 + tagente_modulo.id_plugin != 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 last_execution_try ASC"; } +#print "[DEBUG] SQL is $query1 \n"; $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]; +print "[DEBUG] Procesando candidato $data_id_agente_modulo\n"; + # Skip modules already queued if ((!defined($pending_task_hash{$data_id_agente_modulo})) && (!defined($current_task_hash{$data_id_agente_modulo}))) { @@ -239,6 +254,7 @@ sub pandora_plugin_producer ($) { } # Locking scope, do not remove redundant { } { +#print "[DEBUG] Metiendo $data_id_agente_modulo en cola \n"; lock $queue_lock; push (@pending_task, $data_id_agente_modulo); $pending_task_hash {$data_id_agente_modulo}=1; @@ -246,6 +262,7 @@ sub pandora_plugin_producer ($) { } } #logger ($pa_config, "Items in Network Pending Queue: ".scalar(@pending_task), 5); +#print "[DEBUG] Items in Network Pending Queue: ".scalar(@pending_task); $exec_sql1->finish(); sleep($pa_config->{"server_threshold"}); } # Main loop @@ -263,17 +280,17 @@ sub exec_plugin_module { # Set global variables for this sub my $timeout = $pa_config->{'plugin_timeout'}; - my $agent_plugin; # hash container for tagent_plugin record + 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_plugin) + # 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_plugin = $exec_sql->fetchrow_hashref; + $agent_module = $exec_sql->fetchrow_hashref; # Get a full hash for plugin record reference ($plugin) - $query_sql = "SELECT * FROM tplugin WHERE id = ".$agent_plugin->{'id_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; @@ -284,7 +301,7 @@ sub exec_plugin_module { } # Initialize another global sub variables. - my $agent_name = dame_agente_nombre ($pa_config, $agent_plugin->{'id_agente'}, $dbh); + my $agent_name = dame_agente_nombre ($pa_config, $agent_module->{'id_agente'}, $dbh); my $module_result = 1; # Fail by default my $module_data = 0; # 0 data for default my $module_interval = 0; @@ -293,23 +310,23 @@ sub exec_plugin_module { my $exec_output = ""; my $plugin_command = $plugin->{"execute"}; if ($plugin->{'net_dst_opt'} ne ""){ - $plugin_command = $plugin_command . " ". $plugin->{'net_dst_opt'} ." ". $agent_plugin->{'ip_target'}; + $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_plugin->{'tcp_port'}; + $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_plugin->{'plugin_user'}; + $plugin_command = $plugin_command . " ". $plugin->{'user_opt'} ." ". $agent_module->{'plugin_user'}; } if ($plugin->{'pass_opt'} ne "") { - $plugin_command = $plugin_command . " ". $plugin->{'pass_opt'} ." ". $agent_plugin->{'plugin_pass'}; + $plugin_command = $plugin_command . " ". $plugin->{'pass_opt'} ." ". $agent_module->{'plugin_pass'}; } # Proccess field / optional / dynamic field - if ($agent_plugin->{'plugin_parameter'} ne "") { - $plugin_command = $plugin_command . $agent_plugin->{'plugin_parameter'}; + if ($agent_module->{'plugin_parameter'} ne "") { + $plugin_command = $plugin_command . " ". $agent_module->{'plugin_parameter'}; } - + logger ($pa_config, "Executing AM # $id_am plugin command '$plugin_command'", 9); # Final command line execution is stored at "plugin_command" eval { alarm ($timeout); @@ -317,12 +334,18 @@ sub exec_plugin_module { alarm(0); # Cancel the pending alarm if plugin call returns alive $module_result = 0; # If comes here, this is a successfull exec }; + +# print "[DEBUG] Output for $plugin_command is --$module_data-- \n"; + # Timeout if ($@ =~ /PANDORA PLUGIN SERVER TIMED OUT/) { - logger ($pa_config, "[ERROR] Plugin Task for module ".$agent_plugin->{'id_agente_modulo'}." causes a system timeout in exec", 1); - # resuming eval block... - } else { - logger ($pa_config, "[ERROR] Plugin Task for module ".$agent_plugin->{'id_agente_modulo'}." causes an unknown system error", 1); + logger ($pa_config, "[ERROR] Plugin Task for module ".$agent_module->{'id_agente_modulo'}." causes a system timeout in exec", 1); + logger ($pa_config, "Executing plugin command '$plugin_command'", 9); +print "[DEBUG] Executing plugin TIMEOUT\n"; + # General error, not timed-out + } elsif ($module_result == 1) { + logger ($pa_config, "[ERROR] Plugin Task for module ".$agent_module->{'id_agente_modulo'}." causes an unknown system error", 1); logger ($pa_config, "[ERROR] $@", 1); +print "[DEBUG] Executing plugin ERROR $@\n"; } sub timed_out { @@ -332,49 +355,63 @@ sub exec_plugin_module { # 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 if ($module_result == 0) { +print "[DEBUG] MODULERESULT = 0\n"; my %part; - $part{'name'}[0] = $agent_plugin->{'id_agent'}; + $part{'name'}[0] = $agent_module->{'nombre'}; $part{'description'}[0] = ""; $part{'data'}[0] = $module_data; - my $tipo_modulo = $agent_plugin->{'id_module_type'}; + 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 == $tipo_modulo) { + if (1 == $agent_module->{'id_tipo_modulo'}) { module_generic_data ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); } - elsif (4 == $tipo_modulo) { + elsif (4 == $agent_module->{'id_tipo_modulo'}) { module_generic_data_inc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); } - elsif (3 == $tipo_modulo) { + elsif (3 == $agent_module->{'id_tipo_modulo'}) { module_generic_data_string ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); } - elsif (2 == $tipo_modulo) { + # Generic_proc + elsif (2 == $agent_module->{'id_tipo_modulo'}) { +print "[DEBUG FINAL]: ".$agent_module->{'nombre'}." ".$module_data." ".$tipo_modulo." ".$agent_name. " ".$timestamp; module_generic_proc ($pa_config, \%part, $timestamp, $agent_name, $tipo_modulo, $dbh); } - else { + 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); - goto skipdb_execmod; +print "[DEBUG] Executing plugin UNKONWN MODULE TYPE$@\n"; + $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 { + } + + # If something went wrong in module processing... + if ($module_result != 0){ +print "[DEBUG] MODULERESULT != 0\n"; + # If module execution get a INVALID value - if ($agent_plugin->{'intervalo'} == 0){ - $module_interval = dame_intervalo ($pa_config, $agent_plugin->{'id_agente'}, $dbh); + if ($agent_module->{'intervalo'} == 0){ + $module_interval = dame_intervalo ($pa_config, $agent_module->{'id_agente'}, $dbh); } + # Modules who cannot connect or something go bad, update last_execution_try field - logger ($pa_config, "Cannot obtain exec plugin Module ".$agent_plugin->{'nombre'}." from agent $agent_name", 3); - my $query_act = "UPDATE tagente_estado SET current_interval = $module_interval, last_execution_try = $utimestamp WHERE id_agente_modulo = ".$agent_plugin->{'id_agente_modulo'}; + logger ($pa_config, "Cannot obtain exec plugin Module ".$agent_module->{'nombre'}." from agent $agent_name", 2); + my $query_act = "UPDATE tagente_estado SET current_interval = $module_interval, last_execution_try = $utimestamp WHERE id_agente_modulo = ".$agent_module->{'id_agente_modulo'}; $dbh->do($query_act); } -skipdb_execmod: + $exec_sql->finish(); #close tagent_plugin hash reference } diff --git a/pandora_server/bin/pandora_prediction b/pandora_server/bin/pandora_prediction new file mode 100755 index 0000000000..ce98a02242 --- /dev/null +++ b/pandora_server/bin/pandora_prediction @@ -0,0 +1,413 @@ +#!/usr/bin/perl +########################################################################## +# Pandora FMS Prediction Server +########################################################################## +# 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 on a received signal +######################################################################################## +sub pandora_shutdown { + logger (\%pa_config,"Pandora FMS Prediction Server Shutdown by signal ",0); + print " [*] Shutting down Pandora FMS Prediction Server (received signal)...\n"; + 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; + + 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 + if (scalar(@pending_task) > 0){ + { + lock $queue_lock; + $data_id_agent_module = shift(@pending_task); +print "[CLIENT] Pop out of queue module (pending queue) $data_id_agent_module \n"; + 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); +print "[PREDICT-CLIENT] Executing module # $data_id_agent_module \n"; + 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; +print "[CLIENT] Removing from queue (current task) module $data_id_agent_module \n"; + delete($current_task_hash{$data_id_agent_module}); + } + $counter = 0; + } else { + $counter ++; + } + } +} + +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_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() + OR + tagente_modulo.flag = 1 + ) + 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, tserver + WHERE + ( (tagente.id_prediction_server = $server_id AND tagente_modulo.id_agente = tagente.id_agente) OR + (tagente.id_prediction_server != $server_id AND tagente_modulo.id_agente = tagente.id_agente AND tagente.id_prediction_server = tserver.id_server AND tserver.status = 0) + ) 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_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() OR tagente_modulo.flag = 1 ) + ORDER BY last_execution_try ASC"; + } +# print "[DEBUG] SQL is $query1 \n"; + $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]; +print "[DEBUG] Procesando candidato $data_id_agente_modulo\n"; + + # 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 { } + { +print "[DEBUG] Metiendo $data_id_agente_modulo en cola \n"; + 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); +print "[DEBUG] Items in Network Pending Queue: ".scalar(@pending_task); + $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++){ +print "DEBUG HASH REF ".$target_module->{'id_agente_modulo'}; +print "\n"; + $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; +print "DEBUG SQL - $query_sql \n"; + $week_data[$i] = get_db_free_field ($query_sql, $dbh); + + # 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 +print "Value of n is $n \n"; + if ($n > 0){ + $average = $average / $n; + } else { + $average = 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)); + + # (PROC) Compare with current data + if ($prediction_mode == 0){ + $query_sql = 'SELECT data 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_data_inc ($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/lib/PandoraFMS/Config.pm b/pandora_server/lib/PandoraFMS/Config.pm index 371b589c2c..a2fad0d2ea 100644 --- a/pandora_server/lib/PandoraFMS/Config.pm +++ b/pandora_server/lib/PandoraFMS/Config.pm @@ -27,15 +27,18 @@ require Exporter; our @ISA = ("Exporter"); our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); -our @EXPORT = qw( pandora_help_screen - pandora_init - pandora_loadconfig ); +our @EXPORT = qw( + pandora_help_screen + pandora_init + pandora_loadconfig + pandora_startlog + ); # 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 = "2.0-dev"; -my $pandora_build="PS080226"; +my $pandora_build="PS080311"; our $VERSION = $pandora_version." ".$pandora_build; # Setup hash @@ -48,12 +51,14 @@ my %pa_config; ########################################################################## sub help_screen { - printf "\n\nSyntax: \n pandora_server < fullpathname to pandora server configuration file > [ options ] \n\n"; + printf "\nSyntax: \n\n pandora_server < fullpathname to pandora server configuration file > [ options ] \n\n"; printf "Following options are optional : \n"; - printf " -v : Verbose mode activated, give more information in logfile \n"; - printf " -d : Debug mode activated, give extensive information in logfile \n"; - printf " -D : Daemon mode (runs in backgroup)\n"; - printf " -h : This screen, show a little help screen \n"; + printf " -v : Verbose mode activated, give more information in logfile \n"; + printf " -d : Debug mode activated, give extensive information in logfile \n"; + printf " -D : Daemon mode (runs in backgroup)\n"; + printf " -P <file> : Store PID to file.\n"; + printf " -q : Quiet startup\n"; + printf " -h : This screen, show a little help screen \n"; printf " \n"; exit; } @@ -67,8 +72,8 @@ sub pandora_init { my $pa_config = $_[0]; my $init_string = $_[1]; printf "\n$init_string $pandora_version Build $pandora_build Copyright (c) 2004-2008 ArticaST\n"; - printf "This program is Free Software, licensed under the terms of GPL License v2.\n"; - printf "You can download latest versions and documentation at http://pandora.sourceforge.net. \n\n"; + printf "This program is OpenSource, licensed under the terms of GPL License version 2.\n"; + printf "You can download latest versions and documentation at http://pandora.sourceforge.net \n\n"; # Load config file from command line if ($#ARGV == -1 ){ @@ -78,6 +83,8 @@ sub pandora_init { } $pa_config->{"verbosity"}=1; # Verbose 1 by default $pa_config->{"daemon"}=0; # Daemon 0 by default + $pa_config->{'PID'}=""; # PID file not exist by default + $pa_config->{"quiet"}=0; # Daemon 0 by default # If there are not valid parameters my $parametro; @@ -90,9 +97,15 @@ sub pandora_init { elsif ($parametro =~ m/-v\z/i) { $pa_config->{"verbosity"}=5; } + elsif ($parametro =~ m/^-P\z/i) { + $pa_config->{'PID'}= clean_blank($ARGV[$ax+1]); + } elsif ($parametro =~ m/-d\z/) { $pa_config->{"verbosity"}=10; } + elsif ($parametro =~ m/-q\z/) { + $pa_config->{"quiet"}=1; + } elsif ($parametro =~ m/-D\z/) { $pa_config->{"daemon"}=1; } @@ -161,15 +174,19 @@ sub pandora_loadconfig { $pa_config->{"tcp_timeout"} = 20; # Introduced on 1.3.1 $pa_config->{"snmp_proc_deadresponse"} = 0; # Introduced on 1.3.1 10 Feb08 $pa_config->{"plugin_threads"} = 3; # Introduced on 2.0 + $pa_config->{"prediction_threads"} = 3; # Introduced on 2.0 $pa_config->{"plugin_timeout"} = 5; # Introduced on 2.0 $pa_config->{"wmi_threads"} = 3; # Introduced on 2.0 $pa_config->{"wmi_timeout"} = 5; # Introduced on 2.0 # Check for UID0 - if ($> == 0){ - printf " [W] Not all Pandora FMS components need to be executed as root\n"; - printf " please consider starting it with a non-privileged user.\n"; - } + if ($pa_config->{"quiet"} != 0){ + if ($> == 0){ + printf " [W] Not all Pandora FMS components need to be executed as root\n"; + printf " please consider starting it with a non-privileged user.\n"; + } + } + # Check for file if ( ! -e $archivo_cfg ) { printf "\n [ERROR] Cannot open configuration file at $archivo_cfg. \n"; @@ -250,6 +267,9 @@ sub pandora_loadconfig { elsif ($parametro =~ m/^dataserver\s([0-9]*)/i){ $pa_config->{'dataserver'}= clean_blank($1); } + elsif ($parametro =~ m/^networkserver\s([0-9]*)/i){ + $pa_config->{'networkserver'}= clean_blank($1); + } elsif ($parametro =~ m/^pluginserver\s([0-9]*)/i){ $pa_config->{'pluginserver'}= clean_blank($1); } @@ -316,6 +336,9 @@ sub pandora_loadconfig { elsif ($parametro =~ m/^plugin_threads\s([0-9]*)/i) { $pa_config->{'plugin_threads'}= clean_blank($1); } + elsif ($parametro =~ m/^prediction_threads\s([0-9]*)/i) { + $pa_config->{'prediction_threads'}= clean_blank($1); + } elsif ($parametro =~ m/^plugin_timeout\s([0-9]*)/i) { $pa_config->{'plugin_timeout'}= clean_blank($1); } @@ -326,7 +349,10 @@ sub pandora_loadconfig { } # end of loop for parameter # - if ( $pa_config->{"verbosity"} > 0){ + if (($pa_config->{"verbosity"} > 0) && ($pa_config->{"quiet"} == 0)){ + if ($pa_config->{"PID"} ne ""){ + print " [*] PID File is written at ".$pa_config->{'PID'}."\n"; + } print " [*] Server basepath is ".$pa_config->{'basepath'}."\n"; print " [*] Server logfile at ".$pa_config->{"logfile"}."\n"; print " [*] Server errorlogfile at ".$pa_config->{"errorlogfile"}."\n"; @@ -367,50 +393,54 @@ sub pandora_loadconfig { print " [ERROR] You must enable 'wmiserver' in setup file to run Pandora FMS WMI server. \n\n"; exit; } - if ($opmode == 0){ - print " [*] You are running Pandora FMS Data Server. \n"; - $parametro ="Pandora FMS Data Server"; - $pa_config->{"servermode"}="_Data"; - } - if ($opmode == 1){ - print " [*] You are running Pandora FMS Network Server. \n"; - $parametro ="Pandora FMS Network Server"; - $pa_config->{"servermode"}="_Net"; - } - if ($opmode == 2){ - print " [*] You are running Pandora FMS SNMP Console. \n"; - $parametro ="Pandora FMS SNMP Console"; - $pa_config->{"servermode"}="_SNMP"; - } - if ($opmode == 3){ - print " [*] You are running Pandora FMS Recon Server. \n"; - $parametro ="Pandora FMS Recon Server"; - $pa_config->{"servermode"}="_Recon"; - } - if ($opmode == 4){ - print " [*] You are running Pandora FMS Plugin Server. \n"; - $parametro ="Pandora FMS Plugin Server"; - $pa_config->{"servermode"}="_Plugin"; + + # Show some config options in startup + if ($pa_config->{"quiet"} == 0){ + if ($opmode == 0){ + print " [*] You are running Pandora FMS Data Server. \n"; + $parametro ="Pandora FMS Data Server"; + $pa_config->{"servermode"}="_Data"; + } + if ($opmode == 1){ + print " [*] You are running Pandora FMS Network Server. \n"; + $parametro ="Pandora FMS Network Server"; + $pa_config->{"servermode"}="_Net"; + } + if ($opmode == 2){ + print " [*] You are running Pandora FMS SNMP Console. \n"; + $parametro ="Pandora FMS SNMP Console"; + $pa_config->{"servermode"}="_SNMP"; + } + if ($opmode == 3){ + print " [*] You are running Pandora FMS Recon Server. \n"; + $parametro ="Pandora FMS Recon Server"; + $pa_config->{"servermode"}="_Recon"; + } + if ($opmode == 4){ + print " [*] You are running Pandora FMS Plugin Server. \n"; + $parametro ="Pandora FMS Plugin Server"; + $pa_config->{"servermode"}="_Plugin"; + } + if ($opmode == 5){ + print " [*] You are running Pandora FMS Prediction Server. \n"; + $parametro ="Pandora FMS Prediction Server"; + $pa_config->{"servermode"}="_Prediction"; + } + if ($opmode == 6){ + print " [*] You are running Pandora FMS WMI Server. \n"; + $parametro ="Pandora FMS WMI Server"; + $pa_config->{"servermode"}="_WMI"; + } + if ($pa_config->{"pandora_check"} == 1) { + print " [*] MD5 Security enabled.\n"; + } + if ($pa_config->{"pandora_master"} == 1) { + print " [*] This server is running in MASTER mode.\n"; + } } - if ($opmode == 5){ - print " [*] You are running Pandora FMS Prediction Server. \n"; - $parametro ="Pandora FMS Prediction Server"; - $pa_config->{"servermode"}="_Prediction"; - } - if ($opmode == 6){ - print " [*] You are running Pandora FMS WMI Server. \n"; - $parametro ="Pandora FMS WMI Server"; - $pa_config->{"servermode"}="_WMI"; - } - if ($pa_config->{"pandora_check"} == 1) { - print " [*] MD5 Security enabled.\n"; - } - if ($pa_config->{"pandora_master"} == 1) { - print " [*] This server is running in MASTER mode.\n"; - } logger ($pa_config, "Launching $parametro $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"); + logger ($pa_config, "Config options: $config_options", 1); my $dbh; # Check valid Database variables and update server status eval { @@ -423,15 +453,24 @@ sub pandora_loadconfig { print $@; exit; } - print " [*] Pandora FMS Server [".$pa_config->{'servername'}.$pa_config->{"servermode"}."] is running and operative \n"; + if ($pa_config->{"quiet"} == 0){ + 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); - - # Dump all errors to errorlog - open STDERR, ">>$pa_config->{'errorlogfile'}" or die " [ERROR] Pandora FMS can't write to Errorlog. Aborting : \n $!"; - my $time_now = &UnixDate("today","%Y/%m/%d %H:%M:%S"); - print STDERR "$time_now - ".$pa_config->{'servername'}." Starting Pandora FMS server. Error logging activated \n"; } + + +sub pandora_startlog ($){ + my $pa_config = $_[0]; + + # 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"; + # This redirect ANY output to errorlog. Not a good idea for real usage ! + # open STDOUT, ">>$pa_config->{'errorlogfile'}" +} # End of function declaration # End of defined Code diff --git a/pandora_server/lib/PandoraFMS/DB.pm b/pandora_server/lib/PandoraFMS/DB.pm index 1c65f3b230..b38e593c83 100644 --- a/pandora_server/lib/PandoraFMS/DB.pm +++ b/pandora_server/lib/PandoraFMS/DB.pm @@ -33,39 +33,42 @@ require Exporter; our @ISA = ("Exporter"); our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); -our @EXPORT = qw( crea_agente_modulo - 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_updateserver - pandora_serverkeepaliver - pandora_audit - pandora_event - pandora_lastagentcontact - pandora_writedata - pandora_writestate - pandora_calcula_alerta - module_generic_proc - module_generic_data - module_generic_data_inc - module_generic_data_string - execute_alert - give_network_component_profile_name - pandora_create_incident - give_db_value - ); +our @EXPORT = qw( + crea_agente_modulo + 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_updateserver + pandora_serverkeepaliver + pandora_audit + pandora_event + pandora_lastagentcontact + pandora_writedata + pandora_writestate + pandora_calcula_alerta + module_generic_proc + module_generic_data + module_generic_data_inc + module_generic_data_string + execute_alert + give_network_component_profile_name + pandora_create_incident + get_db_value + get_db_free_row + get_db_free_field + ); # Spanish translation note: # 'Crea' in spanish means 'create' @@ -374,18 +377,22 @@ sub pandora_writestate (%$$$$$$$) { # now we use only local timestamp to stamp state of modules my $pa_config = $_[0]; my $nombre_agente = $_[1]; - my $tipo_modulo = $_[2]; + my $tipo_modulo = $_[2]; # passed as string my $nombre_modulo = $_[3]; my $datos = $_[4]; # Careful: Dont pass a hash, only a single value my $estado = $_[5]; my $dbh = $_[6]; my $needs_update = $_[7]; - 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 @data; my $cambio = 0; my $id_grupo; + + # 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); @@ -395,8 +402,9 @@ sub pandora_writestate (%$$$$$$$) { my $id_agente = dame_agente_id ($pa_config, $nombre_agente, $dbh); my $id_modulo = dame_modulo_id ($pa_config, $tipo_modulo, $dbh); my $id_agente_modulo = dame_agente_modulo_id($pa_config, $id_agente, $id_modulo, $nombre_modulo, $dbh); + if (($id_agente == -1) || ($id_agente_modulo == -1)) { - goto fin_pandora_writestate; + return 0; } # Seek for agent_interval or module_interval @@ -432,38 +440,37 @@ sub pandora_writestate (%$$$$$$$) { my $query_act; # OJO que dentro de una llave solo tiene existencia en esa llave !! if ($s_idages->rows == 0) { # 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, cambio, id_agente, last_try, utimestamp, current_interval, running_by, last_execution_try) VALUES ($id_agente_modulo,$datos,'$timestamp','$estado','1',$id_agente,'$timestamp',$utimestamp, $module_interval, $id_server, $utimestamp)"; # Cuando se hace un insert, siempre hay un cambio de estado + $query_act = "INSERT INTO tagente_estado (id_agente_modulo, datos, timestamp, estado, cambio, id_agente, last_try, utimestamp, current_interval, running_by, last_execution_try) VALUES ($id_agente_modulo,$datos,'$timestamp','$estado','1',$id_agente,'$timestamp',$utimestamp, $module_interval, $id_server, $utimestamp)"; # Cuando se hace un insert, siempre hay un cambio de estado } else { # There are an entry in table already - @data = $s_idages->fetchrow_array(); - # Se supone que $data[5](estado) ( nos daria el estado ANTERIOR - # For xxxx_PROC type (boolean / monitor), create an event if state has changed - if (( $data[5] != $estado) && ( ($tipo_modulo =~/keep_alive/) || ($tipo_modulo =~ /proc/)) ) { - # Cambio de estado detectado ! - $cambio = 1; - # Este seria el momento oportuno de probar a saltar la alerta si estuviera definida - # Makes an event entry, only if previous state changes, if new state, doesnt give any alert - $id_grupo = dame_grupo_agente($pa_config, $id_agente,$dbh); - my $descripcion; - if ( $estado == 0) { - $descripcion = "Monitor ($nombre_modulo) goes up "; - } - if ( $estado == 1) { - $descripcion = "Monitor ($nombre_modulo) goes down"; - } - pandora_event ($pa_config, $descripcion, $id_grupo, $id_agente, $dbh); - } - if ($needs_update == 1) { - $query_act = "UPDATE tagente_estado SET utimestamp = $utimestamp, datos = $datos, cambio = '$cambio', timestamp = '$timestamp', estado = '$estado', id_agente = $id_agente, last_try = '$timestamp', current_interval = '$module_interval', running_by = $id_server, last_execution_try = $utimestamp WHERE id_agente_modulo = '$id_agente_modulo'"; - } else { # dont update last_try field, that it's the field - # we use to check last update time in database - $query_act = "UPDATE tagente_estado SET utimestamp = $utimestamp, datos = $datos, cambio = '$cambio', timestamp = '$timestamp', estado = '$estado', id_agente = $id_agente, current_interval = '$module_interval', running_by = $id_server, last_execution_try = $utimestamp WHERE id_agente_modulo = '$id_agente_modulo'"; - } - } + @data = $s_idages->fetchrow_array(); + # Se supone que $data[5](estado) ( nos daria el estado ANTERIOR + # For xxxx_PROC type (boolean / monitor), create an event if state has changed + if (( $data[5] != $estado) && ( ($tipo_modulo =~/keep_alive/) || ($tipo_modulo =~ /proc/)) ) { + # Cambio de estado detectado ! + $cambio = 1; + # Este seria el momento oportuno de probar a saltar la alerta si estuviera definida + # Makes an event entry, only if previous state changes, if new state, doesnt give any alert + $id_grupo = dame_grupo_agente($pa_config, $id_agente,$dbh); + my $descripcion; + if ( $estado == 0) { + $descripcion = "Monitor ($nombre_modulo) goes up "; + } + if ( $estado == 1) { + $descripcion = "Monitor ($nombre_modulo) goes down"; + } + pandora_event ($pa_config, $descripcion, $id_grupo, $id_agente, $dbh); + } + if ($needs_update == 1) { + $query_act = "UPDATE tagente_estado SET utimestamp = $utimestamp, datos = $datos, cambio = '$cambio', timestamp = '$timestamp', estado = '$estado', id_agente = $id_agente, last_try = '$timestamp', current_interval = '$module_interval', running_by = $id_server, last_execution_try = $utimestamp WHERE id_agente_modulo = $id_agente_modulo"; + } else { # dont update last_try field, that it's the field + # we use to check last update time in database + $query_act = "UPDATE tagente_estado SET utimestamp = $utimestamp, datos = $datos, cambio = '$cambio', timestamp = '$timestamp', estado = '$estado', id_agente = $id_agente, current_interval = '$module_interval', running_by = $id_server, last_execution_try = $utimestamp WHERE id_agente_modulo = $id_agente_modulo"; + } + } my $a_idages = $dbh->prepare($query_act); $a_idages->execute; $a_idages->finish(); - $s_idages->finish(); -fin_pandora_writestate: + $s_idages->finish(); } ########################################################################## @@ -528,14 +535,14 @@ sub pandora_accessupdate (%$$) { logger($pa_config,"Updating tagent_access for agent id $id_agent",9); } - # Update keepalive module (if present, if there is more than one, only updates first one!). - my $id_agent_module = give_db_free ("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 $agent_name = give_db_free ("SELECT nombre FROM tagente WHERE id_agente = $id_agent", $dbh); - my $module_typename = "keep_alive"; - my $module_name = give_db_free ("SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = $id_agent_module", $dbh); - pandora_writestate ($pa_config, $agent_name, $module_typename, $module_name, 1, 0, $dbh, 1); - } + # Update keepalive module (if present, if there is more than one, 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 $agent_name = get_db_free_field ("SELECT nombre FROM tagente WHERE id_agente = $id_agent", $dbh); + 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, 0, $dbh, 1); + } } } @@ -960,8 +967,10 @@ fin_DB_insert_datos: ## Update server status ########################################################################## sub pandora_serverkeepaliver (%$$) { - my $pa_config= $_[0]; - my $opmode = $_[1]; # 0 dataserver, 1 network server, 2 snmp console, 3 recon server + 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; @@ -1024,7 +1033,7 @@ sub pandora_updateserver (%$$$) { } elsif ($opmode == 4){ $pandorasuffix = "_Plugin"; } elsif ($opmode == 5){ - $pandorasuffix = "_IA"; + $pandorasuffix = "_Prediction"; } elsif ($opmode == 6){ $pandorasuffix = "_WMI"; } else { @@ -1091,7 +1100,7 @@ sub pandora_lastagentcontact (%$$$$$$) { my $dbh = $_[6]; my $id_agente = dame_agente_id($pa_config, $nombre_agente,$dbh); - pandora_accessupdate ($pa_config, $id_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"; @@ -1370,25 +1379,25 @@ sub give_group_disabled (%$$) { ## 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 $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 = -1; - } else { - @data = $s_idag->fetchrow_array(); - $id_modulo = $data[0]; - } - $s_idag->finish(); - return $id_modulo; + 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; } ########################################################################## @@ -1686,7 +1695,7 @@ sub crea_agente_modulo (%$$$$$$$) { # Generic access to a field ($field) given a table # give_db_value (field_name_to_be_returned, table, field_search, condition_value, dbh) # --------------------------------------------------------------- -sub give_db_value ($$$$$) { +sub get_db_value ($$$$$) { my $field = $_[0]; my $table = $_[1]; my $field_search = $_[2]; @@ -1706,9 +1715,10 @@ sub give_db_value ($$$$$) { } # --------------------------------------------------------------- -# Generic access to a field ($field) given a table +# Free SQL sentence. Return first field on exit # --------------------------------------------------------------- -sub give_db_free ($$) { + +sub get_db_free_field ($$) { my $condition = $_[0]; my $dbh = $_[1]; @@ -1724,6 +1734,28 @@ sub give_db_free ($$) { 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; +} + # End of function declaration # End of defined Code diff --git a/pandora_server/lib/PandoraFMS/Tools.pm b/pandora_server/lib/PandoraFMS/Tools.pm index c514e79280..28411b0f3c 100644 --- a/pandora_server/lib/PandoraFMS/Tools.pm +++ b/pandora_server/lib/PandoraFMS/Tools.pm @@ -27,15 +27,16 @@ require Exporter; our @ISA = ("Exporter"); our %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); -our @EXPORT = qw( daemonize - logger - limpia_cadena - md5check - float_equal - sqlWrap - is_numeric - clean_blank - ); +our @EXPORT = qw( + pandora_daemonize + logger + limpia_cadena + md5check + float_equal + sqlWrap + is_numeric + clean_blank + ); ########################################################################## @@ -43,15 +44,23 @@ our @EXPORT = qw( daemonize # Put program in background (for daemon mode) ########################################################################## -sub daemonize { - chdir '/tmp' or die "Can't chdir to /tmp: $!"; - open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; - open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; - open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!"; - defined(my $pid = fork) or die "Can't fork: $!"; +sub pandora_daemonize { + my $pa_config = $_[0]; + open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; + open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; + open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!"; + chdir '/tmp' or die "Can't chdir to /tmp: $!"; + defined(my $pid = fork) or die "Can't fork: $!"; exit if $pid; - setsid or die "Can't start a new session: $!"; + setsid or die "Can't start a new session: $!"; umask 0; + + # Store PID of this process in file presented by config token + if ($pa_config->{'PID'} ne ""){ + open (FILE, "> ".$pa_config->{'PID'}) or die "[FATAL] Cannot open PIDfile at ".$pa_config->{'PID'}; + print FILE "$$"; + close (FILE); + } } diff --git a/pandora_server/util/pandora_dbstress.README b/pandora_server/util/pandora_dbstress.README index e0220b7051..75c0688c21 100644 --- a/pandora_server/util/pandora_dbstress.README +++ b/pandora_server/util/pandora_dbstress.README @@ -1,8 +1,34 @@ -This small utility is make to test your database speed in Pandora Scheme. +Pandora FMS DB Stress +===================== -You need to create an agent and assing modules for automated data injection with this tool. Name this modules acording this: +This is a small tool to test your database performance. It also could be used to "pregenerate" random or periodic data (using trigonometry functions) and populate ficticious modules. + +You need to create an agent and assign modules for automated data injection with this tool. You need to name that modules according to this notation: random - To generate "random" data. curve - To generate a math curve using trigonometrical functions, useful to see interpolation working with different intervals, etc boolean - Generate "random" boolean data. +So you could use any name that contains words "random, curve or boolean", for example: + +random_1 or curve_other + +You only could choose "data server" kind of module. + +Finetuning DB stress tool +========================= + +Tool is preconfigured to search in all agents for modules called random, curve or boolean, and to use a interval of 300 and during 30 days. + +If want to modify this behaviour you should edit pandora_dbstress script and modify some variables at the top of file: + + # Configure here target (AGENT_ID for Stress) + my $target_module = -1; # -1 for all modules of that agent + my $target_agent = -1; + my $target_interval = 300; + my $target_days = 30; + +Set there target_module (for a fixed module) or set -1 to process all matching targets +Set there target_agent (for a specific agent). +Set target_interval in seconds for default module interval data periodicity. +Set target_days, number of days in the past from current timestamp. diff --git a/pandora_server/util/pandora_dbstress.pl b/pandora_server/util/pandora_dbstress.pl index b351e18517..4cbbf5234b 100755 --- a/pandora_server/util/pandora_dbstress.pl +++ b/pandora_server/util/pandora_dbstress.pl @@ -21,8 +21,8 @@ my $target_module = -1; # -1 for all modules of that agent my $target_agent = -1; -my $target_interval = 1200; -my $target_days = 12; +my $target_interval = 300; +my $target_days = 30; ################################################################################ ################################################################################