diff --git a/pandora_server/ChangeLog b/pandora_server/ChangeLog index 278c19aff3..11e5437739 100644 --- a/pandora_server/ChangeLog +++ b/pandora_server/ChangeLog @@ -1,3 +1,9 @@ +2009-06-05 Ramon Novoa + + * util/pandora_xml_stress.pl, + util/pandora_xml_stress.README: Added to repository. XML stress tool + and README. + 2009-06-02 Ramon Novoa * lib/PandoraFMS/DataServer.pm: Created a new function to retrieve XML diff --git a/pandora_server/util/pandora_xml_stress.README b/pandora_server/util/pandora_xml_stress.README new file mode 100644 index 0000000000..45affa25a6 --- /dev/null +++ b/pandora_server/util/pandora_xml_stress.README @@ -0,0 +1,83 @@ +Pandora FMS XML Stress +====================== + +This is a small script that generates XML data files like the ones sent by +Pandora FMS agents. + +The scripts reads agent names from a text file and generates XML data files for +each agent according to a configuration file, where modules are defined as +templates. + +Modules are filled with random data. An initial value and the probability of +the module data changing may be specified. + +Run the script like this: + + ./pandora_xml_stress + +Sample configuration file +========================= + +# File containing a list of agent names (one per line). +agent_file agent_names.txt + +# Directory where XML data files will be placed, by default /tmp. +temporal /var/spool/pandora/data_in + +# Pandora FMS XML Stress log file, logs to stdout by default. +log_file pandora_xml_stress.log + +# XML version, by default 1.0. +xml_version 1.0 + +# XML encoding, by default ISO-8859-1. +encoding ISO-8859-1 + +# Operating system (shared by all agents), by default Linux. +os_name Linux + +# Operating system version (shared by all agents), by default 2.6. +os_version 2.6 + +# Agent interval, by default 300. +agent_interval 300 + +# Data file generation start date, by default now. +time_from 2009-06-01 00:00:00 + +# Data file generation end date, by default now. +time_to 2009-06-05 00:00:00 + +# Delay after generating the first data file for each agent to avoid +# race conditions when auto-creating the agent, by default 2. +startup_delay 2 + +# Module definitions. Similar to pandora_agent.conf. + +module_begin +module_name Module_1 +module_type generic_data +module_descripcion Module 1 description. +module_max 100 +module_min 0 +# Probability of the module data changing, by default 100% +module_variation 100 +module_end + +module_begin +module_name Module_2 +module_type generic_data_string +module_descripcion Module 2 description. +# Maximum string length, by default 0. +module_max 20 +# Minimum string length, by default 0 +module_min 10 +module_end + +module_begin +module_name Module_3 +module_type generic_proc +module_descripcion Module 3 description. +# Initial data. +module_data 1 +module_end diff --git a/pandora_server/util/pandora_xml_stress.pl b/pandora_server/util/pandora_xml_stress.pl new file mode 100755 index 0000000000..625ce0e226 --- /dev/null +++ b/pandora_server/util/pandora_xml_stress.pl @@ -0,0 +1,280 @@ +#!/usr/bin/perl +################################################################################ +# Pandora XML Stress tool. +################################################################################ +# Copyright (c) 2009 Ramon Novoa, rnovoa@artica.es +# Copyright (c) 2009 Artica Soluciones Tecnologicas S.L. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# 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); + +# 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 = ) { + + # A module definition + if ($line =~ m/module_begin/) { + my %module; + + while (my $line = ) { + last if ($line =~ m/module_end/); + + # A comment + next if ($line =~ m/^#/); + + # Unknown line + next if ($line !~ /^\s*(\w+)\s+(.+)$/); + + $module{$1} = $2; + } + + push (@{$modules}, \%module); + $Modules++; + next; + } + + # A comment + next if ($line =~ m/^#/); + + # Unknown line + next if ($line !~ /^\s*(\w+)\s+(.+)$/); + + $conf->{$1} = $2; + } + close (FILE); +} + +################################################################################ +# Generate XML files. +################################################################################ +sub generate_xml_files ($$$) { + my ($agent_name, $conf, $modules) = @_; + + # Read agent configuration + my $interval = get_conf_token ($conf, 'interval', '300'); + my $xml_version = get_conf_token ($conf, 'xml_version', '1.0'); + my $encoding = get_conf_token ($conf, 'encoding', 'ISO-8859-1'); + 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'); + + # 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); + + # Generate data files + my $utimestamp = $utimestamp_from; + while ($utimestamp < $utimestamp_to) { + + # XML header + my $timestamp = strftime ("%Y-%m-%d %H:%M:%S", localtime ($utimestamp)); + my $xml_data = "\n"; + $xml_data .= "\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_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', ''); + + # Generate module data + $xml_data .= "\t\n"; + $xml_data .= "\t\t$module_name\n"; + $xml_data .= "\t\t$module_description\n"; + $xml_data .= "\t\t$module_type\n"; + my $rnd_data = generate_random_data ($module_type, $module_data, $module_min, $module_max, $module_variation); + $module->{'module_data'} = $rnd_data; + $xml_data .= "\t\t$rnd_data\n"; + $xml_data .= "\t\n"; + } + + $xml_data .= "\n"; + + # Fix the temporal path + my $last_char = chop ($temporal); + $temporal .= $last_char if ($last_char ne '/'); + + # Save the XML data file + my $xml_file = $temporal . '/' . $agent_name . $utimestamp . '.data'; + open (FILE, ">", $xml_file) || die ("[error] Could not write to '$xml_file': $!.\n\n"); + print FILE $xml_data; + close (FILE); + + copy_xml_file ($conf, $xml_file); + $XMLFiles++; + + # 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 (2); + } + + # Generic data + return int (rand ($max - $min + 1) + $min); +} + +################################################################################ +# Returns the value of a configuration token. +################################################################################ +sub copy_xml_file ($$) { + my ($conf, $file) = @_; + + my $server_ip = get_conf_token ($conf, 'server_ip', ''); + 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 $file > /dev/null 2>&1`; + 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}; +} + +################################################################################ +# 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); + } +} + +################################################################################ +# Main +################################################################################ +my (%conf, @modules); + +# Check command line parameters +if ($#ARGV != 0) { + print "Usage:\n\t$0 \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"); + +my $t0 = gettimeofday (); + +# Launch a thread for each agent +while (my $agent_name = ) { + chomp ($agent_name); + threads->create (\&generate_xml_files, $agent_name, \%conf, \@modules); + $Agents++; +} +close (FILE); + +# 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, '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(threads::all)) { + $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)");