1014 lines
32 KiB
Perl
1014 lines
32 KiB
Perl
#!/usr/bin/perl
|
|
################################################################################
|
|
# Pandora XML Stress tool.
|
|
################################################################################
|
|
# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es
|
|
# Copyright (c) 2012 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.
|
|
################################################################################
|
|
use strict;
|
|
use warnings;
|
|
|
|
use threads;
|
|
use threads::shared;
|
|
use Time::Local;
|
|
use Time::HiRes qw(gettimeofday);
|
|
|
|
use POSIX qw (strftime ceil floor);
|
|
|
|
use Data::Dumper;
|
|
use Math::Trig;
|
|
|
|
use File::Copy;
|
|
|
|
# Global variables used for statistics
|
|
my $Agents :shared = 0;
|
|
my $Modules :shared = 0;
|
|
my $XMLFiles :shared = 0;
|
|
|
|
my $LogLock :shared;
|
|
|
|
########################################################################
|
|
# Load the configuration file.
|
|
########################################################################
|
|
sub load_config ($\%\@) {
|
|
my ($conf_file, $conf, $modules) = @_;
|
|
|
|
open (FILE, "<", $conf_file) || die ("[error] Could not open configuration file '$conf_file': $!.\n\n");
|
|
|
|
while (my $line = <FILE>) {
|
|
$line =~ s/[\r\n]+//g;
|
|
|
|
# A module definition
|
|
if ($line =~ m/module_begin/) {
|
|
my %module;
|
|
|
|
# A comment
|
|
next if ($line =~ m/^#/);
|
|
|
|
while (my $line = <FILE>) {
|
|
$line =~ s/[\r\n]+//g;
|
|
|
|
# A comment
|
|
next if ($line =~ m/^#/);
|
|
|
|
last if ($line =~ m/module_end/);
|
|
|
|
# Unknown line
|
|
next if ($line !~ /^\s*(\w+)\s+(.+)$/);
|
|
|
|
$module{$1} = $2;
|
|
}
|
|
|
|
push (@{$modules}, \%module);
|
|
$Modules++;
|
|
next;
|
|
}
|
|
|
|
# Unknown line
|
|
next if ($line !~ /^\s*(\w+)\s+(.+)$/);
|
|
|
|
$conf->{$1} = $2;
|
|
}
|
|
close (FILE);
|
|
}
|
|
|
|
|
|
################################################################################
|
|
# Generate XML files.
|
|
################################################################################
|
|
sub generate_xml_files ($$$$$$) {
|
|
my ($agents, $start, $step, $conf, $modules, $local_conf) = @_;
|
|
|
|
# Read agent configuration
|
|
my $interval = get_conf_token ($conf, 'agent_interval', '300');
|
|
my $xml_version = get_conf_token ($conf, 'xml_version', '1.0');
|
|
my $encoding = get_conf_token ($conf, 'encoding', 'UTF-8');
|
|
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');
|
|
|
|
my $startup_delay = get_conf_token ($conf, 'startup_delay', '5');
|
|
my $ag_timezone_offset = get_conf_token ($conf, 'timezone_offset', '0');
|
|
my $ag_timezone_offset_range = get_conf_token ($conf, 'timezone_offset_range', '0');
|
|
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');
|
|
my $group = get_conf_token ($conf, 'group', 'Servers');
|
|
|
|
# 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);
|
|
die ("[error] Invalid time_from: $time_from\n\n")
|
|
unless ($time_from =~ /(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/);
|
|
my $utimestamp_from = timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
|
|
|
|
# Get time_to
|
|
my $time_to = get_conf_token ($conf, 'time_to', $time_now);
|
|
die ("[error] Invalid time_to: $time_to\n\n")
|
|
unless ($time_to =~ /(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/);
|
|
my $utimestamp_to = timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
|
|
|
|
my %modules_src_pointers = init_src_pointers($modules);
|
|
|
|
# Previous data for incremental modules
|
|
my %prev_rnd_data;
|
|
|
|
# Generate data files
|
|
my $utimestamp = $utimestamp_from;
|
|
while ($utimestamp <= $utimestamp_to) {
|
|
for (my $i = $start; $i < $start + $step; $i++) {
|
|
|
|
# Get the name of the agent
|
|
last unless defined ($agents->[$i]);
|
|
my $agent_name = $agents->[$i];
|
|
|
|
# Use the modules of local conf of agent.
|
|
if ($local_conf->{$agent_name}) {
|
|
$modules = $local_conf->{$agent_name};
|
|
}
|
|
|
|
# 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;
|
|
|
|
# XML header
|
|
my @localtime = localtime ($utimestamp);
|
|
my $wday = $localtime[6];
|
|
my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", @localtime);
|
|
my $xml_data = "<?xml version='$xml_version' encoding='$encoding'?>\n";
|
|
my $sign = int rand(2);
|
|
my $agent_name_converted = convert_xml_agent_name($agent_name);
|
|
$ag_timezone_offset += ($sign*(-1)+(1-$sign)) * int rand($ag_timezone_offset_range);
|
|
$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_converted' timezone_offset='$ag_timezone_offset' longitude='$ag_longitude' latitude='$ag_latitude' altitude='$ag_altitude'>\n";
|
|
|
|
foreach my $module (@{$modules}) {
|
|
# Skip unnamed modules
|
|
my $module_name = get_conf_token ($module, 'module_name', '');
|
|
next if ($module_name eq '');
|
|
|
|
# Read module configuration
|
|
my $module_type = get_conf_token ($module, 'module_type', 'generic_data');
|
|
my $module_description = get_conf_token ($module, 'module_description', '');
|
|
my $module_unit = get_conf_token ($module, 'module_unit', '');
|
|
my $module_incremental = get_conf_token ($module, 'module_incremental', '0');
|
|
my $attenuation = get_conf_token ($module, 'module_attenuation', '0');
|
|
my @attenuation_wdays = get_conf_token_array ($module, 'module_attenuation_wdays', ' ');
|
|
|
|
#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');
|
|
my $module_src = get_generation_parameter($module, 'src', 'source.txt');
|
|
|
|
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', '');
|
|
|
|
# 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";
|
|
|
|
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";
|
|
}
|
|
$xml_data .= "\t\t<unit><![CDATA[$module_unit]]></unit>\n";
|
|
|
|
# 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);
|
|
}
|
|
}
|
|
elsif ($generation_type eq 'SOURCE') {
|
|
$rnd_data = generate_data_from_source($module_name, $module_src, \%modules_src_pointers);
|
|
|
|
}
|
|
|
|
# Data attenuation
|
|
if ($attenuation != 0 && ($#attenuation_wdays < 0 || grep{$_ eq $wday} @attenuation_wdays)) {
|
|
$rnd_data *= $attenuation;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
# Save previous data
|
|
$module->{'module_data'} = $rnd_data;
|
|
$xml_data .= "\t\t<data><![CDATA[$rnd_data]]></data>\n";
|
|
$xml_data .= "\t</module>\n";
|
|
}
|
|
|
|
$xml_data .= "</agent_data>\n";
|
|
|
|
# Fix the temporal path
|
|
my $last_char = chop ($temporal);
|
|
$temporal .= $last_char if ($last_char ne '/');
|
|
|
|
# Save the XML data file
|
|
# The temporal dir is normaly the /var/spool/pandora/data_in
|
|
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");
|
|
print FILE $xml_data;
|
|
close (FILE);
|
|
|
|
copy_xml_file ($conf, $xml_fullpath_file, $xml_file);
|
|
$XMLFiles++;
|
|
}
|
|
|
|
#Update src pointers for a new xml
|
|
update_src_pointers(\%modules_src_pointers);
|
|
|
|
# First run, let the server create the new agent before sending more data
|
|
if ($utimestamp == $utimestamp_from) {
|
|
threads->yield ();
|
|
sleep ($startup_delay);
|
|
}
|
|
|
|
$utimestamp += $interval;
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Generates random data according to the module type.
|
|
################################################################################
|
|
sub generate_random_data ($$$$$) {
|
|
my ($module_type, $current_data, $min, $max, $variation) = @_;
|
|
|
|
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))]);
|
|
}
|
|
|
|
# Proc
|
|
if ($module_type =~ m/proc/) {
|
|
return int (rand ($max - $min + 1) + $min);
|
|
}
|
|
|
|
# Generic data
|
|
return int (rand ($max - $min + 1) + $min);
|
|
}
|
|
|
|
################################################################################
|
|
# 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;
|
|
|
|
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);
|
|
|
|
if ($prob >= $other_rnd) {
|
|
return int (rand ($max - $min + 1) + $min);
|
|
}
|
|
else {
|
|
return $avg;
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Converts special characteres to properly xml escaping characters
|
|
################################################################################
|
|
sub convert_xml_agent_name($$$) {
|
|
|
|
my ($agent_name_xml) = @_;
|
|
$_ = $agent_name_xml;
|
|
if(/&/){
|
|
s/&/&/g;
|
|
}
|
|
|
|
if (/['"?<>]/){
|
|
s/"/"/g;
|
|
s/'/'/g;
|
|
s/</</g;
|
|
s/>/>/g;
|
|
}
|
|
return $_;
|
|
}
|
|
|
|
################################################################################
|
|
# Generates data from a txt source
|
|
################################################################################
|
|
sub generate_data_from_source ($$$) {
|
|
my ($module_name, $module_src, $pointers) = @_;
|
|
|
|
my $data = 0;
|
|
|
|
my $pointer;
|
|
|
|
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};
|
|
|
|
$pointer = $pointer % ($#data_array + 1);
|
|
|
|
$data = $data_array[$pointer];
|
|
|
|
}
|
|
else {
|
|
#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') {
|
|
|
|
$module_name =~ s/\ /\_/;
|
|
|
|
$pointers{$module_name} = 0;
|
|
}
|
|
}
|
|
|
|
return %pointers;
|
|
}
|
|
|
|
################################################################################
|
|
# Updates SRC pointer for src modules
|
|
################################################################################
|
|
sub update_src_pointers ($) {
|
|
|
|
my ($pointers) = shift;
|
|
|
|
foreach my $p (keys %{$pointers}) {
|
|
|
|
#Add 1 to the pointer
|
|
$pointers->{$p}++;
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Returns the value of a configuration token.
|
|
################################################################################
|
|
sub copy_xml_file ($$) {
|
|
my ($conf, $fullpath_file, $file) = @_;
|
|
|
|
my $server_ip = get_conf_token ($conf, 'server_ip', '');
|
|
my $local_copy = get_conf_token($conf, 'local_copy', 0);
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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};
|
|
}
|
|
|
|
################################################################################
|
|
# 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;
|
|
}
|
|
|
|
################################################################################
|
|
# 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;
|
|
}
|
|
|
|
################################################################################
|
|
# Prints a message to the logfile.
|
|
################################################################################
|
|
sub log_message ($$) {
|
|
my ($conf, $message) = @_;
|
|
my $utimestamp = time ();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
########################################################################
|
|
# INI MD5 FUNCTIONS
|
|
########################################################################
|
|
|
|
# Used to calculate the MD5 checksum of a string
|
|
use constant MOD232 => 2**32;
|
|
|
|
########################################################################
|
|
# MD5 leftrotate function.
|
|
# See http://en.wikipedia.org/wiki/MD5#Pseudocode.
|
|
########################################################################
|
|
sub leftrotate ($$) {
|
|
my ($x, $c) = @_;
|
|
|
|
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 () {
|
|
|
|
# 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);
|
|
|
|
# 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;
|
|
|
|
# Note: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
|
|
|
|
# Initialize variables
|
|
my $h0 = 0x67452301;
|
|
my $h1 = 0xEFCDAB89;
|
|
my $h2 = 0x98BADCFE;
|
|
my $h3 = 0x10325476;
|
|
|
|
# Pre-processing
|
|
my $msg = unpack ("B*", pack ("A*", $str));
|
|
my $bit_len = length ($msg);
|
|
|
|
# Append "1" bit to message
|
|
$msg .= '1';
|
|
|
|
# Append "0" bits until message length in bits ≡ 448 (mod 512)
|
|
$msg .= '0' while ((length ($msg) % 512) != 448);
|
|
|
|
# Append bit /* bit, not byte */ length of unpadded message as 64-bit little-endian integer to message
|
|
$msg .= unpack ("B64", pack ("VV", $bit_len));
|
|
|
|
# Process the message in successive 512-bit chunks
|
|
for (my $i = 0; $i < length ($msg); $i += 512) {
|
|
|
|
my @w;
|
|
my $chunk = substr ($msg, $i, 512);
|
|
|
|
# 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))));
|
|
}
|
|
|
|
# Initialize hash value for this chunk
|
|
my $a = $h0;
|
|
my $b = $h1;
|
|
my $c = $h2;
|
|
my $d = $h3;
|
|
my $f;
|
|
my $g;
|
|
|
|
# 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;
|
|
}
|
|
|
|
my $temp = $d;
|
|
$d = $c;
|
|
$c = $b;
|
|
$b = ($b + leftrotate (($a + $f + $K[$y] + $w[$g]) % MOD232, $R[$y])) % MOD232;
|
|
$a = $temp;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
# 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;
|
|
my $conf = shift;
|
|
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';
|
|
|
|
|
|
$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;
|
|
my $directory_temp = get_conf_token($conf, 'directory_temp', '/tmp');
|
|
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', '.');
|
|
|
|
my $directory_temp = get_conf_token($conf, 'directory_temp', '/tmp');
|
|
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) {
|
|
|
|
#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");
|
|
|
|
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
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 = '';
|
|
my $temp = "";
|
|
|
|
# Create the block of modules.
|
|
foreach my $module (@{$modules}) {
|
|
|
|
|
|
$temp = $temp . "
|
|
module_begin";
|
|
while ( my ($key, $value) = each %$module ) {
|
|
$temp = $temp . "\n$key $value";
|
|
}
|
|
$temp = $temp . "
|
|
module_end\n";
|
|
}
|
|
print ($temp);
|
|
my $default_conf =
|
|
"# General Parameters
|
|
# ==================
|
|
|
|
server_ip " . get_conf_token ($conf, 'server_ip', 'localhost') . "
|
|
server_path /var/spool/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 {
|
|
log_message ($conf, "\tERROR:\tThe $agent.conf is not create.'\n");
|
|
}
|
|
}
|
|
|
|
|
|
# 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;
|
|
|
|
# A comment
|
|
next if ($line =~ m/^#/);
|
|
|
|
while (my $line = <CONF_FILE>) {
|
|
|
|
# A comment
|
|
next if ($line =~ m/^#/);
|
|
|
|
last if ($line =~ m/module_end/);
|
|
|
|
# Unknown line
|
|
next if ($line !~ /^\s*(\w+)\s+(.+)$/);
|
|
|
|
$module{$1} = $2;
|
|
}
|
|
|
|
push(@return, \%module);
|
|
}
|
|
}
|
|
|
|
close (CONF_FILE);
|
|
}
|
|
else {
|
|
log_message ($conf, "\tERROR:\tOpen to parse the $agent_name.conf.'\n");
|
|
}
|
|
|
|
return \@return;
|
|
}
|
|
|
|
################################################################################
|
|
# 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'});
|
|
open (FILE, "<", $conf{'agent_file'}) ||
|
|
die ("[error] Could not open agent configuration file '" . $conf{'agent_file'} . "': $!.\n\n");
|
|
|
|
# Read agent names
|
|
my @agents;
|
|
while (my $agent_name = <FILE>) {
|
|
chomp ($agent_name);
|
|
push (@agents, $agent_name);
|
|
$Agents++;
|
|
}
|
|
close (FILE);
|
|
|
|
# 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);
|
|
|
|
# Get the maximum number of threads and the number of agents per thread
|
|
my $max_threads = 0 + get_conf_token (\%conf, 'max_threads', '10');
|
|
|
|
my $step = ceil ($Agents / $max_threads);
|
|
|
|
my $t0 = gettimeofday ();
|
|
for (my $i = 0; $i < $Agents; $i += $step) {
|
|
threads->create (\&generate_xml_files, \@agents, $i, $step, \%conf, \@modules, \%local_conf);
|
|
}
|
|
|
|
# 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);
|
|
my $interval = get_conf_token (\%conf, 'agent_interval', '300');
|
|
log_message (\%conf, "Generating XML data files for $Agents agents from $time_from to $time_to interval $interval.");
|
|
|
|
# Wait for all threads to finish
|
|
foreach my $thr (threads->list()) {
|
|
$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)");
|