2009-06-05 17:38:19 +02:00
#!/usr/bin/perl
################################################################################
# Pandora XML Stress tool.
################################################################################
# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es
2012-03-07 21:00:56 +01:00
# Copyright (c) 2012 Artica Soluciones Tecnologicas S.L.
2009-06-05 17:38:19 +02:00
#
# 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.
################################################################################
use strict ;
use warnings ;
use threads ;
use threads::shared ;
use Time::Local ;
use Time::HiRes qw( gettimeofday ) ;
2011-05-04 12:50:19 +02:00
use POSIX qw (strftime ceil floor ) ;
2009-06-05 17:38:19 +02:00
2011-04-29 11:17:28 +02:00
use Data::Dumper ;
use Math::Trig ;
2011-05-04 12:50:19 +02:00
use File::Copy ;
2009-06-05 17:38:19 +02:00
# Global variables used for statistics
my $ Agents : shared = 0 ;
my $ Modules : shared = 0 ;
my $ XMLFiles : shared = 0 ;
my $ LogLock : shared ;
2013-06-12 14:16:44 +02:00
########################################################################
2009-06-05 17:38:19 +02:00
# Load the configuration file.
2013-06-12 14:16:44 +02:00
########################################################################
2009-06-05 17:38:19 +02:00
sub load_config ($\%\@) {
my ( $ conf_file , $ conf , $ modules ) = @ _ ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
open ( FILE , "<" , $ conf_file ) || die ( "[error] Could not open configuration file '$conf_file': $!.\n\n" ) ;
while ( my $ line = <FILE> ) {
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# A module definition
if ( $ line =~ m/module_begin/ ) {
my % module ;
2012-06-11 12:25:58 +02:00
2009-07-08 13:01:47 +02:00
# A comment
next if ( $ line =~ m/^#/ ) ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
while ( my $ line = <FILE> ) {
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# A comment
next if ( $ line =~ m/^#/ ) ;
2012-06-11 12:25:58 +02:00
2009-07-08 13:01:47 +02:00
last if ( $ line =~ m/module_end/ ) ;
2013-09-02 09:20:10 +02:00
2009-06-05 17:38:19 +02:00
# Unknown line
next if ( $ line !~ /^\s*(\w+)\s+(.+)$/ ) ;
$ module { $ 1 } = $ 2 ;
}
push ( @ { $ modules } , \ % module ) ;
$ Modules + + ;
next ;
}
# Unknown line
next if ( $ line !~ /^\s*(\w+)\s+(.+)$/ ) ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
$ conf - > { $ 1 } = $ 2 ;
}
close ( FILE ) ;
}
2012-09-28 13:52:50 +02:00
2009-06-05 17:38:19 +02:00
################################################################################
# Generate XML files.
################################################################################
2011-05-04 12:50:19 +02:00
sub generate_xml_files ($$$$$$) {
my ( $ agents , $ start , $ step , $ conf , $ modules , $ local_conf ) = @ _ ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# Read agent configuration
2009-07-08 13:01:47 +02:00
my $ interval = get_conf_token ( $ conf , 'agent_interval' , '300' ) ;
2009-06-05 17:38:19 +02:00
my $ xml_version = get_conf_token ( $ conf , 'xml_version' , '1.0' ) ;
2012-06-21 13:47:23 +02:00
my $ encoding = get_conf_token ( $ conf , 'encoding' , 'UTF-8' ) ;
2009-06-05 17:38:19 +02:00
my $ os_name = get_conf_token ( $ conf , 'os_name' , 'Linux' ) ;
my $ os_version = get_conf_token ( $ conf , 'os_version' , '2.6' ) ;
my $ temporal = get_conf_token ( $ conf , 'temporal' , '/tmp' ) ;
2013-09-02 09:20:10 +02:00
2010-01-20 14:34:13 +01:00
my $ startup_delay = get_conf_token ( $ conf , 'startup_delay' , '5' ) ;
my $ ag_timezone_offset = get_conf_token ( $ conf , 'timezone_offset' , '0' ) ;
2010-02-05 16:27:25 +01:00
my $ ag_timezone_offset_range = get_conf_token ( $ conf , 'timezone_offset_range' , '0' ) ;
2010-01-12 18:22:14 +01:00
my $ latitude_base = get_conf_token ( $ conf , 'latitude_base' , '40.42056' ) ;
my $ longitude_base = get_conf_token ( $ conf , 'longitude_base' , '-3.708187' ) ;
my $ altitude_base = get_conf_token ( $ conf , 'altitude_base' , '0' ) ;
my $ position_radius = get_conf_token ( $ conf , 'position_radius' , '10' ) ;
2013-01-22 09:59:53 +01:00
my $ group = get_conf_token ( $ conf , 'group' , 'Servers' ) ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# Get time_from
my $ time_now = strftime ( "%Y-%m-%d %H:%M:%S" , localtime ( ) ) ;
my $ time_from = get_conf_token ( $ conf , 'time_from' , $ time_now ) ;
2013-09-02 09:20:10 +02:00
die ( "[error] Invalid time_from: $time_from\n\n" )
unless ( $ time_from =~ /(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/ ) ;
2009-06-05 17:38:19 +02:00
my $ utimestamp_from = timelocal ( $ 6 , $ 5 , $ 4 , $ 3 , $ 2 - 1 , $ 1 - 1900 ) ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# Get time_to
my $ time_to = get_conf_token ( $ conf , 'time_to' , $ time_now ) ;
2013-09-02 09:20:10 +02:00
die ( "[error] Invalid time_to: $time_to\n\n" )
unless ( $ time_to =~ /(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/ ) ;
2009-06-05 17:38:19 +02:00
my $ utimestamp_to = timelocal ( $ 6 , $ 5 , $ 4 , $ 3 , $ 2 - 1 , $ 1 - 1900 ) ;
2012-06-11 12:25:58 +02:00
2012-09-28 13:52:50 +02:00
my % modules_src_pointers = init_src_pointers ( $ modules ) ;
2013-05-20 21:01:35 +02:00
# Previous data for incremental modules
my % prev_rnd_data ;
2009-06-05 17:38:19 +02:00
# Generate data files
my $ utimestamp = $ utimestamp_from ;
2009-06-08 20:36:12 +02:00
while ( $ utimestamp <= $ utimestamp_to ) {
for ( my $ i = $ start ; $ i < $ start + $ step ; $ i + + ) {
2012-06-11 12:25:58 +02:00
2009-06-08 20:36:12 +02:00
# Get the name of the agent
last unless defined ( $ agents - > [ $ i ] ) ;
my $ agent_name = $ agents - > [ $ i ] ;
2011-05-04 12:50:19 +02:00
# Use the modules of local conf of agent.
if ( $ local_conf - > { $ agent_name } ) {
$ modules = $ local_conf - > { $ agent_name } ;
}
2010-01-12 18:22:14 +01:00
# Agent random position
my $ ag_latitude = $ latitude_base + ( rand ( $ position_radius ) - $ position_radius /2)/ 100 ;
my $ ag_longitude = $ longitude_base + ( rand ( $ position_radius ) - $ position_radius /2)/ 100 ;
my $ ag_altitude = $ altitude_base + ( rand ( $ position_radius ) - $ position_radius /2)/ 100 ;
2009-06-08 20:36:12 +02:00
# XML header
2013-05-20 13:20:39 +02:00
my @ localtime = localtime ( $ utimestamp ) ;
my $ wday = $ localtime [ 6 ] ;
my $ timestamp = strftime ( "%Y-%m-%d %H:%M:%S" , @ localtime ) ;
2009-06-08 20:36:12 +02:00
my $ xml_data = "<?xml version='$xml_version' encoding='$encoding'?>\n" ;
2010-02-05 16:27:25 +01:00
my $ sign = int rand ( 2 ) ;
$ ag_timezone_offset += ( $ sign * ( - 1 ) + ( 1 - $ sign ) ) * int rand ( $ ag_timezone_offset_range ) ;
2013-01-22 09:59:53 +01:00
$ xml_data . = "<agent_data os_name='$os_name' os_version='$os_version' interval='$interval' group='$group' version='$os_version' timestamp='$timestamp' agent_name='$agent_name' timezone_offset='$ag_timezone_offset' longitude='$ag_longitude' latitude='$ag_latitude' altitude='$ag_altitude'>\n" ;
2011-05-04 12:50:19 +02:00
2009-06-08 20:36:12 +02:00
foreach my $ module ( @ { $ modules } ) {
# Skip unnamed modules
my $ module_name = get_conf_token ( $ module , 'module_name' , '' ) ;
next if ( $ module_name eq '' ) ;
2012-06-11 12:25:58 +02:00
2009-06-08 20:36:12 +02:00
# Read module configuration
my $ module_type = get_conf_token ( $ module , 'module_type' , 'generic_data' ) ;
my $ module_description = get_conf_token ( $ module , 'module_description' , '' ) ;
2012-03-07 21:00:56 +01:00
my $ module_unit = get_conf_token ( $ module , 'module_unit' , '' ) ;
2013-05-20 21:01:35 +02:00
my $ module_incremental = get_conf_token ( $ module , 'module_incremental' , '0' ) ;
2013-05-20 13:20:39 +02:00
my $ attenuation = get_conf_token ( $ module , 'module_attenuation' , '0' ) ;
my @ attenuation_wdays = get_conf_token_array ( $ module , 'module_attenuation_wdays' , ' ' ) ;
2012-06-11 12:25:58 +02:00
2011-04-29 11:17:28 +02:00
#my $module_min = get_conf_token ($module, 'module_min', '0');
#my $module_max = get_conf_token ($module, 'module_max', '255');
#my $module_variation = get_conf_token ($module, 'module_variation', '100');
#my $module_data = get_conf_token ($module, 'module_data', '');
# Extract the data config for the generator.
my $ generation_type = get_generation_parameter ( $ module , 'type' , 'RANDOM' ) ;
my $ module_variation = get_generation_parameter ( $ module , 'variation' , '100' ) ;
my $ module_min = get_generation_parameter ( $ module , 'min' , '0' ) ;
my $ module_max = get_generation_parameter ( $ module , 'max' , '255' ) ;
my $ module_data = get_generation_parameter ( $ module , 'data' , '' ) ;
my $ module_prob = get_generation_parameter ( $ module , 'prob' , '50' ) ;
my $ module_avg = get_generation_parameter ( $ module , 'avg' , '127' ) ;
my $ module_time_wave_length = get_generation_parameter ( $ module , 'time_wave_length' , '0' ) ;
my $ module_time_offset = get_generation_parameter ( $ module , 'time_offset' , '0' ) ;
2012-09-28 13:52:50 +02:00
my $ module_src = get_generation_parameter ( $ module , 'src' , 'source.txt' ) ;
2011-04-29 11:17:28 +02:00
2013-03-11 17:24:25 +01:00
my $ module_min_critical = get_conf_token ( $ module , 'module_min_critical' , '' ) ;
my $ module_max_critical = get_conf_token ( $ module , 'module_max_critical' , '' ) ;
my $ module_min_warning = get_conf_token ( $ module , 'module_min_warning' , '' ) ;
my $ module_max_warning = get_conf_token ( $ module , 'module_max_warning' , '' ) ;
2009-06-08 20:36:12 +02:00
# Generate module data
$ xml_data . = "\t<module>\n" ;
$ xml_data . = "\t\t<name>$module_name</name>\n" ;
$ xml_data . = "\t\t<description>$module_description</description>\n" ;
$ xml_data . = "\t\t<type>$module_type</type>\n" ;
2013-07-09 11:28:10 +02:00
2013-03-11 17:24:25 +01:00
if ( $ module_min_critical ne '' ) {
$ xml_data . = "\t\t<min_critical>$module_min_critical</min_critical>\n" ;
}
if ( $ module_max_critical ne '' ) {
$ xml_data . = "\t\t<max_critical>$module_max_critical</max_critical>\n" ;
}
if ( $ module_min_warning ne '' ) {
$ xml_data . = "\t\t<min_warning>$module_min_warning</min_warning>\n" ;
}
if ( $ module_max_warning ne '' ) {
$ xml_data . = "\t\t<max_warning>$module_max_warning</max_warning>\n" ;
}
2012-03-07 21:00:56 +01:00
$ xml_data . = "\t\t<unit><![CDATA[$module_unit]]></unit>\n" ;
2011-04-29 11:17:28 +02:00
# Generate data
my $ rnd_data = $ module - > { 'module_data' } ;
if ( $ generation_type eq 'RANDOM' ) {
$ rnd_data = generate_random_data ( $ module_type , $ module_data ,
$ module_min , $ module_max , $ module_variation ) ;
}
elsif ( $ generation_type eq 'SCATTER' ) {
if ( ( $ module_type eq 'generic_data_string' ) ||
( $ module_type eq 'async_string' ) ) {
printf "\n" ;
log_message ( $ conf , "\tERROR:\tTry to generate scatter data in string module '$module_name' in agent '$agent_name'\n" ) ;
$ rnd_data = $ module_data ;
}
else {
$ rnd_data = generate_scatter_data ( $ module_type , $ module_data ,
$ module_min , $ module_max , $ module_prob , $ module_avg ) ;
}
}
elsif ( $ generation_type eq 'CURVE' ) {
if ( ( $ module_type eq 'generic_data_string' ) ||
( $ module_type eq 'async_string' ) ) {
printf "\n" ;
log_message ( $ conf , "\tERROR:\tTry to generate curve data in string module '$module_name' in agent '$agent_name'\n" ) ;
$ rnd_data = $ module_data ;
}
else {
$ rnd_data = generate_curve_data ( $ utimestamp , $ module_min , $ module_max ,
$ module_time_wave_length , $ module_time_offset ) ;
}
2013-02-18 17:24:39 +01:00
}
elsif ( $ generation_type eq 'SOURCE' ) {
$ rnd_data = generate_data_from_source ( $ module_name , $ module_src , \ % modules_src_pointers ) ;
2011-04-29 11:17:28 +02:00
}
2013-05-20 13:20:39 +02:00
# Data attenuation
2013-05-20 21:01:35 +02:00
if ( $ attenuation != 0 && ( $# attenuation_wdays < 0 || grep { $ _ eq $ wday } @ attenuation_wdays ) ) {
2013-05-20 13:20:39 +02:00
$ rnd_data *= $ attenuation ;
}
2013-05-20 21:01:35 +02:00
# Incremental module
if ( $ module_incremental == 1 ) {
if ( defined ( $ prev_rnd_data { $ module_name } ) ) {
$ rnd_data += $ prev_rnd_data { $ module_name } ;
}
$ prev_rnd_data { $ module_name } = $ rnd_data ;
}
2011-04-29 11:17:28 +02:00
# Save previous data
2009-06-08 20:36:12 +02:00
$ module - > { 'module_data' } = $ rnd_data ;
2013-02-18 17:24:39 +01:00
$ xml_data . = "\t\t<data><![CDATA[$rnd_data]]></data>\n" ;
2009-06-08 20:36:12 +02:00
$ xml_data . = "\t</module>\n" ;
2012-06-11 12:25:58 +02:00
}
2013-02-18 17:24:39 +01:00
2009-06-08 20:36:12 +02:00
$ xml_data . = "</agent_data>\n" ;
2012-06-11 12:25:58 +02:00
2009-06-08 20:36:12 +02:00
# Fix the temporal path
my $ last_char = chop ( $ temporal ) ;
$ temporal . = $ last_char if ( $ last_char ne '/' ) ;
2012-06-11 12:25:58 +02:00
2009-06-08 20:36:12 +02:00
# Save the XML data file
2011-05-04 12:50:19 +02:00
# The temporal dir is normaly the /var/spool/pandora/data_in
2013-06-19 10:53:56 +02:00
my $ xml_fullpath_file = $ temporal . '/' . $ agent_name . '_' . $ utimestamp . '.data' ;
my $ xml_file = $ agent_name . '_' . $ utimestamp . '.data' ;
open ( FILE , ">" , $ xml_fullpath_file ) || die ( "[error] Could not write to '$xml_fullpath_file': $!.\n\n" ) ;
2009-06-08 20:36:12 +02:00
print FILE $ xml_data ;
close ( FILE ) ;
2012-06-11 12:25:58 +02:00
2013-06-19 10:53:56 +02:00
copy_xml_file ( $ conf , $ xml_fullpath_file , $ xml_file ) ;
2009-06-08 20:36:12 +02:00
$ XMLFiles + + ;
}
2012-06-11 12:25:58 +02:00
2012-09-28 13:52:50 +02:00
#Update src pointers for a new xml
update_src_pointers ( \ % modules_src_pointers ) ;
2009-06-05 17:38:19 +02:00
# First run, let the server create the new agent before sending more data
if ( $ utimestamp == $ utimestamp_from ) {
threads - > yield ( ) ;
sleep ( $ startup_delay ) ;
}
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
$ utimestamp += $ interval ;
}
}
################################################################################
# Generates random data according to the module type.
################################################################################
sub generate_random_data ($$$$$) {
my ( $ module_type , $ current_data , $ min , $ max , $ variation ) = @ _ ;
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
my $ change_rnd = int rand ( 100 ) ;
return $ current_data unless ( $ variation > $ change_rnd ) or $ current_data eq '' ;
# String
if ( $ module_type =~ m/string/ ) {
my @ chars = ( "A" .. "Z" , "a" .. "z" , 0 .. 9 ) ;
return join ( "" , @ chars [ map { rand @ chars } ( 1 .. ( rand ( $ max - $ min + 1 ) + $ min ) ) ] ) ;
}
2012-06-11 12:25:58 +02:00
2009-06-05 17:38:19 +02:00
# Proc
if ( $ module_type =~ m/proc/ ) {
2011-01-07 01:01:13 +01:00
return int ( rand ( $ max - $ min + 1 ) + $ min ) ;
2009-06-05 17:38:19 +02:00
}
# Generic data
return int ( rand ( $ max - $ min + 1 ) + $ min ) ;
}
2011-04-29 11:17:28 +02:00
################################################################################
# Generates curve data.
################################################################################
sub generate_curve_data ($$$$$$) {
my ( $ utimestamp , $ module_min , $ module_max , $ module_time_wave_length ,
$ module_time_offset ) = @ _ ;
#f(x) = (max - min) * Sin( (t * pi) / (wave_length) + (pi * (offset / wave_length))) + min
######################################################
# GRAPHICAL EXPLAIN
#
# offset
# |
# (max - min) -> |----- . . . .
# |V V. . . .
# ---------------|---------------------------------
# | . . . ^ ^
# min -> | . . . | |
# -------
# |
# wave_length
#
######################################################
my $ return = ( $ module_max - $ module_min ) / 2 *
sin ( ( $ utimestamp * pi ) / $ module_time_wave_length +
( pi * ( $ module_time_offset / $module_time_wave_length))) + ($module_min + $module_max) / 2 ;
2013-02-18 17:24:39 +01:00
2011-04-29 11:17:28 +02:00
return $ return ;
}
################################################################################
# Generates scatter data.
################################################################################
sub generate_scatter_data ($$$$$$) {
my ( $ module_type , $ current_data , $ min , $ max , $ prob , $ avg ) = @ _ ;
# And check the probability now
my $ other_rnd = int rand ( 100 ) ;
2013-02-18 17:24:39 +01:00
if ( $ prob >= $ other_rnd ) {
2011-04-29 11:17:28 +02:00
return int ( rand ( $ max - $ min + 1 ) + $ min ) ;
}
else {
return $ avg ;
}
}
2012-09-28 13:52:50 +02:00
################################################################################
# Generates data from a txt source
################################################################################
sub generate_data_from_source ($$$) {
my ( $ module_name , $ module_src , $ pointers ) = @ _ ;
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
my $ data = 0 ;
my $ pointer ;
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
if ( - e $ module_src ) {
#Get data and split to an array
open ( FD , "<" , $ module_src ) ;
my @ data_array = <FD> ;
close ( FD ) ;
$ module_name =~ s/\ /\_/ ;
$ pointer = $ pointers - > { $ module_name } ;
2013-02-18 17:24:39 +01:00
2013-07-09 11:28:10 +02:00
$ pointer = $ pointer % ( $ # data_array + 1 ) ;
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
$ data = $ data_array [ $ pointer ] ;
2013-02-18 17:24:39 +01:00
}
else {
2012-09-28 13:52:50 +02:00
#There was an error, set last to 0 and return data
return $ data ;
}
return $ data ;
}
################################################################################
# Initialize SRC pointer for src modules
################################################################################
sub init_src_pointers ($) {
my ( $ modules ) = shift ;
my % pointers ;
foreach my $ mod ( @ { $ modules } ) {
# Skip unnamed modules
my $ module_name = get_conf_token ( $ mod , 'module_name' , '' ) ;
next if ( $ module_name eq '' ) ;
my $ type = get_generation_parameter ( $ mod , 'type' , 'RANDOM' ) ;
if ( $ type eq 'SOURCE' ) {
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
$ module_name =~ s/\ /\_/ ;
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
$ pointers { $ module_name } = 0 ;
}
}
return % pointers ;
}
################################################################################
# Updates SRC pointer for src modules
################################################################################
sub update_src_pointers ($) {
my ( $ pointers ) = shift ;
2013-02-18 17:24:39 +01:00
2012-09-28 13:52:50 +02:00
foreach my $ p ( keys % { $ pointers } ) {
#Add 1 to the pointer
$ pointers - > { $ p } + + ;
}
}
2009-06-05 17:38:19 +02:00
################################################################################
# Returns the value of a configuration token.
################################################################################
sub copy_xml_file ($$) {
2013-06-19 10:53:56 +02:00
my ( $ conf , $ fullpath_file , $ file ) = @ _ ;
2009-06-05 17:38:19 +02:00
my $ server_ip = get_conf_token ( $ conf , 'server_ip' , '' ) ;
2013-06-19 10:53:56 +02:00
my $ local_copy = get_conf_token ( $ conf , 'local_copy' , 0 ) ;
2013-02-18 17:24:39 +01:00
2013-06-19 10:53:56 +02:00
if ( $ local_copy ) {
my $ local_dir = get_conf_token ( $ conf , 'local_dir' , '/var/spool/pandora/data_in' ) ;
move ( "$fullpath_file" , "$local_dir/$file" ) ;
}
else {
return if ( $ server_ip eq '' ) ;
my $ server_port = get_conf_token ( $ conf , 'server_port' , '41121' ) ;
my $ tentacle_opts = get_conf_token ( $ conf , 'tentacle_opts' , '' ) ;
# Send the file and delete it
`tentacle_client -a $server_ip -p $server_port $tentacle_opts "$fullpath_file" > /dev/null 2>&1` ;
if ( $? != 0 ) {
log_message ( $ conf , "\tERROR:\tTry to send XML file (" . $ file . ") with tentacle\n" ) ;
}
}
2009-06-05 17:38:19 +02:00
unlink ( $ file ) ;
}
################################################################################
# Returns the value of a configuration token.
################################################################################
sub get_conf_token ($$$) {
my ( $ hash_ref , $ token , $ def_value ) = @ _ ;
return $ def_value unless ref ( $ hash_ref ) and defined ( $ hash_ref - > { $ token } ) ;
return $ hash_ref - > { $ token } ;
}
2013-05-20 13:20:39 +02:00
################################################################################
# Returns the value of a configuration token.
################################################################################
sub get_conf_token_array ($$$) {
my ( $ hash_ref , $ token , $ separator ) = @ _ ;
my @ tokens = ( ) ;
return @ tokens unless ref ( $ hash_ref ) and defined ( $ hash_ref - > { $ token } ) ;
@ tokens = split ( $ separator , $ hash_ref - > { $ token } ) ;
return @ tokens ;
}
2011-04-29 11:17:28 +02:00
################################################################################
# Returns the parameter of a generator configuration of module.
################################################################################
sub get_generation_parameter ($$$) {
my ( $ hash_ref , $ token , $ def_value ) = @ _ ;
return $ def_value unless ref ( $ hash_ref ) and defined ( $ hash_ref - > { 'module_exec' } ) ;
my $ configuration = $ hash_ref - > { 'module_exec' } ;
my $ value = $ def_value ;
$ value = $ 1 if ( $ configuration =~ /$token=([^;]+)/ ) ;
return $ value ;
}
2009-06-05 17:38:19 +02:00
################################################################################
# Prints a message to the logfile.
################################################################################
sub log_message ($$) {
my ( $ conf , $ message ) = @ _ ;
my $ utimestamp = time ( ) ;
2013-02-18 17:24:39 +01:00
2009-06-05 17:38:19 +02:00
my $ log_file = get_conf_token ( $ conf , 'log_file' , '' ) ;
# Log to stdout
if ( $ log_file eq '' ) {
print "[$utimestamp] $message\n" ;
return ;
}
# Log to file
{
lock $ LogLock ;
open ( LOG_FILE , '>>' , $ log_file ) || die ( "[error] Could not open log file '$log_file': $!.\n\n" ) ;
print LOG_FILE "[$utimestamp] $message\n" ;
close ( LOG_FILE ) ;
}
}
2011-05-04 12:50:19 +02:00
2013-06-12 14:16:44 +02:00
########################################################################
2011-05-04 12:50:19 +02:00
# INI MD5 FUNCTIONS
2013-06-12 14:16:44 +02:00
########################################################################
2011-05-04 12:50:19 +02:00
# Used to calculate the MD5 checksum of a string
use constant MOD232 = > 2 ** 32 ;
2013-06-12 14:16:44 +02:00
########################################################################
# MD5 leftrotate function.
# See http://en.wikipedia.org/wiki/MD5#Pseudocode.
########################################################################
2011-05-04 12:50:19 +02:00
sub leftrotate ($$) {
my ( $ x , $ c ) = @ _ ;
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
return ( 0xFFFFFFFF & ( $ x << $ c ) ) | ( $ x >> ( 32 - $ c ) ) ;
}
###############################################################################
# Initialize some variables needed by the MD5 algorithm.
# See http://en.wikipedia.org/wiki/MD5#Pseudocode.
###############################################################################
my ( @ R , @ K ) ;
sub md5_init () {
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
# R specifies the per-round shift amounts
@ R = ( 7 , 12 , 17 , 22 , 7 , 12 , 17 , 22 , 7 , 12 , 17 , 22 , 7 , 12 , 17 , 22 ,
5 , 9 , 14 , 20 , 5 , 9 , 14 , 20 , 5 , 9 , 14 , 20 , 5 , 9 , 14 , 20 ,
4 , 11 , 16 , 23 , 4 , 11 , 16 , 23 , 4 , 11 , 16 , 23 , 4 , 11 , 16 , 23 ,
6 , 10 , 15 , 21 , 6 , 10 , 15 , 21 , 6 , 10 , 15 , 21 , 6 , 10 , 15 , 21 ) ;
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
# Use binary integer part of the sines of integers (radians) as constants
for ( my $ i = 0 ; $ i < 64 ; $ i + + ) {
$ K [ $ i ] = floor ( abs ( sin ( $ i + 1 ) ) * MOD232 ) ;
}
}
###############################################################################
# Return the MD5 checksum of the given string.
# Pseudocode from http://en.wikipedia.org/wiki/MD5#Pseudocode.
###############################################################################
sub md5 ($) {
my $ str = shift ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Note: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Initialize variables
my $ h0 = 0x67452301 ;
my $ h1 = 0xEFCDAB89 ;
my $ h2 = 0x98BADCFE ;
my $ h3 = 0x10325476 ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Pre-processing
my $ msg = unpack ( "B*" , pack ( "A*" , $ str ) ) ;
my $ bit_len = length ( $ msg ) ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Append "1" bit to message
$ msg . = '1' ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Append "0" bits until message length in bits ≡ 448 (mod 512)
$ msg . = '0' while ( ( length ( $ msg ) % 512 ) != 448 ) ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Append bit /* bit, not byte */ length of unpadded message as 64-bit little-endian integer to message
$ msg . = unpack ( "B64" , pack ( "VV" , $ bit_len ) ) ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Process the message in successive 512-bit chunks
for ( my $ i = 0 ; $ i < length ( $ msg ) ; $ i += 512 ) {
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
my @ w ;
my $ chunk = substr ( $ msg , $ i , 512 ) ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Break chunk into sixteen 32-bit little-endian words w[i], 0 <= i <= 15
for ( my $ j = 0 ; $ j < length ( $ chunk ) ; $ j += 32 ) {
push ( @ w , unpack ( "V" , pack ( "B32" , substr ( $ chunk , $ j , 32 ) ) ) ) ;
}
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Initialize hash value for this chunk
my $ a = $ h0 ;
my $ b = $ h1 ;
my $ c = $ h2 ;
my $ d = $ h3 ;
my $ f ;
my $ g ;
2013-06-19 10:53:56 +02:00
2011-05-04 12:50:19 +02:00
# Main loop
for ( my $ y = 0 ; $ y < 64 ; $ y + + ) {
if ( $ y <= 15 ) {
$ f = $ d ^ ( $ b & ( $ c ^ $ d ) ) ;
$ g = $ y ;
}
elsif ( $ y <= 31 ) {
$ f = $ c ^ ( $ d & ( $ b ^ $ c ) ) ;
$ g = ( 5 * $ y + 1 ) % 16 ;
}
elsif ( $ y <= 47 ) {
$ f = $ b ^ $ c ^ $ d ;
$ g = ( 3 * $ y + 5 ) % 16 ;
}
else {
$ f = $ c ^ ( $ b | ( 0xFFFFFFFF & ( ~ $ d ) ) ) ;
$ g = ( 7 * $ y ) % 16 ;
}
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
my $ temp = $ d ;
$ d = $ c ;
$ c = $ b ;
$ b = ( $ b + leftrotate ( ( $ a + $ f + $ K [ $ y ] + $ w [ $ g ] ) % MOD232 , $ R [ $ y ] ) ) % MOD232 ;
$ a = $ temp ;
}
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
# Add this chunk's hash to result so far
$ h0 = ( $ h0 + $ a ) % MOD232 ;
$ h1 = ( $ h1 + $ b ) % MOD232 ;
$ h2 = ( $ h2 + $ c ) % MOD232 ;
$ h3 = ( $ h3 + $ d ) % MOD232 ;
}
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
# Digest := h0 append h1 append h2 append h3 #(expressed as little-endian)
return unpack ( "H*" , pack ( "V" , $ h0 ) ) . unpack ( "H*" , pack ( "V" , $ h1 ) ) . unpack ( "H*" , pack ( "V" , $ h2 ) ) . unpack ( "H*" , pack ( "V" , $ h3 ) ) ;
}
################################################################################
# END MD5 FUNCTIONS
################################################################################
################################################################################
################################################################################
# Sends a file to the server.
################################################################################
sub send_file ($$) {
my $ file = shift ;
2013-09-02 09:20:10 +02:00
my $ conf = shift ;
2011-05-04 12:50:19 +02:00
my $ output ;
my $ server_ip = get_conf_token ( $ conf , 'server_ip' , '' ) ;
my $ server_port = get_conf_token ( $ conf , 'server_port' , '41121' ) ;
my $ tentacle_options = get_conf_token ( $ conf , 'tentacle_options' , '' ) ;
# Shell command separator
my $ CmdSep = ';' ;
# $DevNull
my $ DevNull = '/dev/null' ;
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
$ output = `tentacle_client -v -a $server_ip -p $server_port $tentacle_options $file 2>&1 >$DevNull` ;
# Get the errorlevel
my $ rc = $? >> 8 ;
if ( $ rc != 0 ) {
log_message ( $ conf , "\tERROR:\tError sending file '$file': $output" ) ;
}
return $ rc ;
}
################################################################################
# Receive a file from the server.
################################################################################
sub recv_file ($$) {
my $ file = shift ;
my $ conf = shift ;
my $ output ;
2013-09-02 09:20:10 +02:00
my $ directory_temp = get_conf_token ( $ conf , 'directory_temp' , '/tmp' ) ;
2011-05-04 12:50:19 +02:00
my $ server_ip = get_conf_token ( $ conf , 'server_ip' , '' ) ;
my $ server_port = get_conf_token ( $ conf , 'server_port' , '41121' ) ;
my $ tentacle_options = get_conf_token ( $ conf , 'tentacle_options' , '' ) ;
# Shell command separator
my $ CmdSep = ';' ;
# $DevNull
my $ DevNull = '/dev/null' ;
$ output = `cd "$directory_temp"$CmdSep tentacle_client -v -g -a $server_ip -p $server_port $tentacle_options $file 2>&1 >$DevNull` ;
# Get the errorlevel
my $ rc ;
$ rc = $? >> 8 ;
if ( $ rc != 0 ) {
log_message ( $ conf , "\tERROR:\tGetting the remote $file.'\n" ) ;
log_message ( $ conf , "\tERROR:\t$output'\n" ) ;
}
return $ rc ;
}
################################################################################
################################################################################
# Get the send agent conf and generate modules.
################################################################################
sub get_and_send_agent_conf (\@\%\@\%) {
my ( $ agents , $ conf , $ modules , $ local_conf ) = @ _ ;
my $ get_and_send_agent_conf = get_conf_token ( $ conf , 'get_and_send_agent_conf' , '0' ) ;
my $ directory_confs = get_conf_token ( $ conf , 'directory_confs' , '.' ) ;
2013-09-02 09:20:10 +02:00
my $ directory_temp = get_conf_token ( $ conf , 'directory_temp' , '/tmp' ) ;
2011-05-04 12:50:19 +02:00
my $ md5_agent_name = '' ;
if ( $ get_and_send_agent_conf == 1 ) {
foreach my $ agent ( @ { $ agents } ) {
$ md5_agent_name = md5 ( $ agent ) ;
if ( open ( CONF_FILE , "$directory_confs/$agent.conf" ) ) {
binmode ( CONF_FILE ) ;
my $ conf_md5 = md5 ( join ( '' , <CONF_FILE> ) ) ;
close ( CONF_FILE ) ;
# Get the remote MD5 file
if ( recv_file ( "$md5_agent_name.md5" , $ conf ) != 0 ) {
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
#The remote agent don't recive, then it send the agent conf and md5.
open ( MD5_FILE , ">$directory_temp/$md5_agent_name.md5" )
|| log_message ( $ conf , "\tERROR:\tCould not open file '$directory_temp/$md5_agent_name.md5' for writing: $!." ) ;
print MD5_FILE $ conf_md5 ;
close ( MD5_FILE ) ;
copy ( "$directory_confs/$agent.conf" , "$directory_temp/$md5_agent_name.conf" ) ;
send_file ( "$directory_temp/$md5_agent_name.conf" , $ conf ) ;
send_file ( "$directory_temp/$md5_agent_name.md5" , $ conf ) ;
log_message ( $ conf , "\tINFO:\tUploading configuration for the first time." ) ;
unlink ( "$directory_temp/$md5_agent_name.conf" ) ;
unlink ( "$directory_temp/$md5_agent_name.md5" ) ;
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
}
else {
#There is a remote agent.
open ( MD5_FILE , "< $directory_temp/$md5_agent_name.md5" )
|| log_message ( $ conf , "Could not open file '$directory_confs/$md5_agent_name.md5' for writing: $!." ) ;
#Get the first version of md5 file.
my $ remote_conf_md5 = <MD5_FILE> ;
close ( MD5_FILE ) ;
if ( $ remote_conf_md5 ne $ conf_md5 ) {
if ( recv_file ( "$md5_agent_name.conf" , $ conf ) != 0 ) {
log_message ( $ conf , "\tERROR:\t Get the remote '$agent.conf'." ) ;
}
else {
move ( "$directory_temp/$md5_agent_name.conf" , "$directory_confs/$agent.conf" ) ;
}
}
}
2013-09-02 09:20:10 +02:00
2011-05-04 12:50:19 +02:00
}
else {
log_message ( $ conf , "\tWARNING:\tThere is not the $agent.conf .'\n" ) ;
my $ interval = get_conf_token ( $ conf , 'agent_interval' , '300' ) ;
my $ timezone_offset = get_conf_token ( $ conf , 'timezone_offset' , '0' ) ;
my $ module_txt = '' ;
2012-02-06 13:06:01 +01:00
my $ temp = "" ;
2011-05-04 12:50:19 +02:00
# Create the block of modules.
foreach my $ module ( @ { $ modules } ) {
2013-09-02 09:20:10 +02:00
$ temp = $ temp . "
2011-05-04 12:50:19 +02:00
module_begin
module_name " . $module->{'module_name'} . "
module_type " . $module->{'module_type'} . "
module_exec " . $module->{'module_exec'} . "
module_min " . $module->{'module_min'} . "
module_max " . $module->{'module_max'} . "
module_end
" ;
}
my $ default_conf =
" # General Parameters
# ==================
2011-11-16 13:21:47 +01:00
server_ip " . get_conf_token ($conf, 'server_ip', 'localhost') . "
2011-05-04 12:50:19 +02:00
server_path /var/s pool /pandora/ data_in
temporal / tmp
logfile /var/ log /pandora/ pandora_agent . log
# Interval in seconds, 300 by default
interval $ interval
# Debug mode only generate XML, and stop after first execution,
# and does not copy XML to server.
debug 0
# By default, agent takes machine name
agent_name $ agent
# Agent description
description This conf is generated with pandora_xml_stress .
# Timezone offset: Difference with the server timezone
#timezone_offset $timezone_offset
# Listening TCP port for remote server. By default is 41121 (for tentacle)
# if you want to use SSH use 22, and FTP uses 21.
server_port 41121
# Transfer mode: tentacle, ftp, ssh or local
transfer_mode tentacle
# If set to 1 allows the agent to be configured via the web console (Only Enterprise version)
remote_config 1 " . $ temp ;
if ( open ( CONF_FILE , ">$directory_confs/$agent.conf" ) ) {
print CONF_FILE $ default_conf ;
close ( CONF_FILE ) ;
open ( CONF_FILE , "$directory_confs/$agent.conf" ) ;
binmode ( CONF_FILE ) ;
my $ conf_md5 = md5 ( join ( '' , <CONF_FILE> ) ) ;
close ( CONF_FILE ) ;
#Send files.
open ( MD5_FILE , "> $directory_temp/$md5_agent_name.md5" )
|| log_message ( $ conf , "\tERROR:\tCould not open file '$directory_temp/$agent.conf' for writing: $!." ) ;
print MD5_FILE $ conf_md5 ;
close ( MD5_FILE ) ;
copy ( "$directory_confs/$agent.conf" , "$directory_temp/$md5_agent_name.conf" ) ;
send_file ( "$directory_temp/$md5_agent_name.conf" , $ conf ) ;
send_file ( "$directory_temp/$md5_agent_name.md5" , $ conf ) ;
log_message ( $ conf , "\tINFO:\tUploading configuration for the first time." ) ;
unlink ( "$directory_temp/$md5_agent_name.conf" ) ;
unlink ( "$directory_temp/$md5_agent_name.md5" ) ;
}
else {
2013-01-28 13:16:16 +01:00
log_message ( $ conf , "\tERROR:\tThe $agent.conf is not create.'\n" ) ;
2011-05-04 12:50:19 +02:00
}
}
# Fill the local conf for generate data
my $ conf = parse_local_conf ( $ agent , $ conf ) ;
$ local_conf - > { $ agent } = $ conf ;
}
}
}
################################################################################
# Parse local conf.
################################################################################
sub parse_local_conf ($$) {
my ( $ agent_name , $ conf ) = @ _ ;
my $ directory_confs = get_conf_token ( $ conf , 'directory_confs' , '.' ) ;
my @ return ;
if ( open ( CONF_FILE , "$directory_confs/$agent_name.conf" ) ) {
my $ line = '' ;
while ( <CONF_FILE> ) {
$ line = $ _ ;
# A module definition
if ( $ line =~ m/module_begin/ ) {
my % module ;
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
# A comment
next if ( $ line =~ m/^#/ ) ;
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
while ( my $ line = <CONF_FILE> ) {
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
# A comment
next if ( $ line =~ m/^#/ ) ;
2013-06-12 14:16:44 +02:00
2011-05-04 12:50:19 +02:00
last if ( $ line =~ m/module_end/ ) ;
# Unknown line
next if ( $ line !~ /^\s*(\w+)\s+(.+)$/ ) ;
$ module { $ 1 } = $ 2 ;
}
push ( @ return , \ % module ) ;
}
}
close ( CONF_FILE ) ;
}
else {
2013-01-28 13:16:16 +01:00
log_message ( $ conf , "\tERROR:\tOpen to parse the $agent_name.conf.'\n" ) ;
2011-05-04 12:50:19 +02:00
}
return \ @ return ;
}
2009-06-05 17:38:19 +02:00
################################################################################
# Main
################################################################################
my ( % conf , @ modules ) ;
# Check command line parameters
if ( $# ARGV != 0 ) {
print "Usage:\n\t$0 <configuration file>\n\n" ;
exit 1 ;
}
# Load configuration file
load_config ( $ ARGV [ 0 ] , % conf , @ modules ) ;
die ( "[error] No agent file specified in configuration file.\n\n" ) unless defined ( $ conf { 'agent_file' } ) ;
2011-04-29 11:17:28 +02:00
open ( FILE , "<" , $ conf { 'agent_file' } ) ||
die ( "[error] Could not open agent configuration file '" . $ conf { 'agent_file' } . "': $!.\n\n" ) ;
2009-06-05 17:38:19 +02:00
2009-06-08 20:36:12 +02:00
# Read agent names
my @ agents ;
2009-06-05 17:38:19 +02:00
while ( my $ agent_name = <FILE> ) {
chomp ( $ agent_name ) ;
2009-06-08 20:36:12 +02:00
push ( @ agents , $ agent_name ) ;
2009-06-05 17:38:19 +02:00
$ Agents + + ;
}
close ( FILE ) ;
2011-05-04 12:50:19 +02:00
# Init MD5
md5_init ( ) ;
# Get the agent conf, instead use the conf in the pandora_xml_stress.conf
my % local_conf ;
get_and_send_agent_conf ( @ agents , % conf , @ modules , % local_conf ) ;
2009-06-08 20:36:12 +02:00
# Get the maximum number of threads and the number of agents per thread
2011-05-04 12:50:19 +02:00
my $ max_threads = 0 + get_conf_token ( \ % conf , 'max_threads' , '10' ) ;
2011-04-29 11:17:28 +02:00
2009-06-08 20:36:12 +02:00
my $ step = ceil ( $ Agents / $ max_threads ) ;
my $ t0 = gettimeofday ( ) ;
for ( my $ i = 0 ; $ i < $ Agents ; $ i += $ step ) {
2011-05-04 12:50:19 +02:00
threads - > create ( \ & generate_xml_files , \ @ agents , $ i , $ step , \ % conf , \ @ modules , \ % local_conf ) ;
2009-06-08 20:36:12 +02:00
}
2009-06-05 17:38:19 +02:00
# Log some information for the user
my $ time_now = strftime ( "%Y-%m-%d %H:%M:%S" , localtime ( ) ) ;
my $ time_from = get_conf_token ( \ % conf , 'time_from' , $ time_now ) ;
my $ time_to = get_conf_token ( \ % conf , 'time_to' , $ time_now ) ;
2009-12-21 11:16:01 +01:00
my $ interval = get_conf_token ( \ % conf , 'agent_interval' , '300' ) ;
2009-06-05 17:38:19 +02:00
log_message ( \ % conf , "Generating XML data files for $Agents agents from $time_from to $time_to interval $interval." ) ;
# Wait for all threads to finish
2012-02-06 13:06:01 +01:00
foreach my $ thr ( threads - > list ( ) ) {
2009-06-05 17:38:19 +02:00
$ thr - > join ( ) ;
}
my $ t1 = gettimeofday ( ) ;
# Log statistics
log_message ( \ % conf , "\tTotal agents:\t$Agents\n\t\tTotal modules:\t" . ( $ Agents * $ Modules ) . "\t($Modules per agent)\n\t\tTotal XML:\t$XMLFiles\t(" . int ( $ XMLFiles / ( $ t1 - $ t0 ) ) . " per second)" ) ;