pandora_perf_report v1

This commit is contained in:
fbsanchez 2017-12-18 18:50:26 +01:00
parent 2b6300b441
commit fbee7205e5
1 changed files with 380 additions and 0 deletions

View File

@ -0,0 +1,380 @@
#!/usr/bin/perl
################################################################################
# Pandora FMS Performance Report Tool
# Copyright (c) 2017 Artica Soluciones Tecnologicas S.L.
################################################################################
use strict;
use warnings;
use POSIX qw (strftime floor);
use Sys::Hostname;
use Time::HiRes qw(gettimeofday tv_interval);
use lib '/usr/lib/perl5';
# Pandora Modules.
use PandoraFMS::Config;
use PandoraFMS::Tools;
use PandoraFMS::DataServer;
use PandoraFMS::DB;
use PandoraFMS::Core;
use Thread::Semaphore;
our $VERSION = '1.0';
my $STRESS_AGENT_NAME = '__PANDORA_STRESS_AGENT__'; # Name of the agent used for XML stress tests.
my $STRESS_AGENT_MODULES = 50; # Number of modules per agent.
my $STRESS_AGENT_OPS = 50; # Number of XML iterations (one XML per iteration).
my $STRESS_AGENT_XML = { # Hash representing data coming from an XML data file.
'os_name' => 'Other',
'version' => '7.0NG',
'interval' => '300',
'description' => 'Pandora FMS XML stress agent',
'os_version' => '',
'agent_name' => $STRESS_AGENT_NAME,
'timestamp' => 0,
'agent_alias' => $STRESS_AGENT_NAME,
'module' => []
};
my $STRESS_CPU_OPS = 100000; # Number of CPU iterations.
my $STRESS_DB_OPS = 5; # Number of DB iterations.
my $STRESS_DB_BLOCK = 1; # Number of rows read per SELECT statement.
################################################################################
# Print a generic ratio calculated as $num/$den.
################################################################################
sub ratio {
my ($num, $den) = @_;
return $den == 0 ? "Inf" : sprintf("%10.2f", $num/$den);
}
################################################################################
# Print a generic numeric metric.
################################################################################
sub metric {
my ($metric) = @_;
# Integer.
if ($metric =~ /^-?\d+\z/) {
return sprintf("%10d", $metric);
}
# Float.
else {
return sprintf("%10.2f", $metric);
}
}
################################################################################
# Print log messages.
################################################################################
sub print_log {
my ($sec, $msg) = @_;
print(strftime ("%Y-%m-%d %H:%M:%S", localtime()) . " [$sec] $msg\n");
}
################################################################################
# Stress the CPU.
################################################################################
sub stress_cpu {
my $t0 = [gettimeofday];
print_log("CPU", "start");
for (my $i = 1; $i <= $STRESS_CPU_OPS; $i++) {
my $sqrt = sqrt($i);
my $exp = exp($i);
my $log = log($i);
my $sin = sin($i);
my $cos = cos($i);
}
my $elapsed = tv_interval($t0) * 1000; # Convert from s to ms.
print_log("CPU", "stop");
return ratio($STRESS_CPU_OPS, $elapsed);
}
################################################################################
# Stress the DB.
################################################################################
sub stress_db {
my ($dbh) = @_;
print_log("DB INSERT", "start");
my $t0 = [gettimeofday];
for (my $i = 1; $i <= $STRESS_DB_OPS; $i++) {
db_do($dbh, "INSERT INTO tagente_datos (`id_agente_modulo`, `datos`, `utimestamp`) VALUES (0, ?, ?)", $STRESS_DB_OPS - $i, $i);
}
my $insert_score = ratio($STRESS_DB_OPS, tv_interval($t0));
print_log("DB INSERT", "stop");
print_log("DB UPDATE", "start");
$t0 = [gettimeofday];
for (my $i = 1; $i <= $STRESS_DB_OPS; $i++) {
db_do($dbh, "UPDATE tagente_datos SET datos=? WHERE id_agente_modulo = 0 AND utimestamp = ?", $i, $i);
}
my $update_score = ratio($STRESS_DB_OPS, tv_interval($t0));
print_log("DB UPDATE", "stop");
print_log("DB SELECT", "start");
$t0 = [gettimeofday];
for (my $i = 1; $i <= $STRESS_DB_OPS; $i++) {
db_do($dbh, "SELECT SQL_NO_CACHE datos FROM tagente_datos WHERE id_agente_modulo = 0 AND utimestamp > ? AND utimestamp < ?", $i, $i + $STRESS_DB_BLOCK);
}
my $select_score = ratio($STRESS_DB_OPS, tv_interval($t0));
print_log("DB SELECT", "stop");
print_log("DB DELETE", "start");
$t0 = [gettimeofday];
for (my $i = 1; $i <= $STRESS_DB_OPS; $i++) {
db_do($dbh, "DELETE FROM tagente_datos WHERE id_agente_modulo = 0 AND utimestamp = ?", $i);
}
my $delete_score = ratio($STRESS_DB_OPS, tv_interval($t0));
print_log("DB DELETE", "stop");
return { 'insert_score' => $insert_score, 'update_score' => $update_score, 'select_score' => $select_score, 'delete_score' => $delete_score };
}
################################################################################
# Stress a single agent.
################################################################################
sub stress_agent {
my ($dbh, $conf) = @_;
# Initialize some lexical variables needed by the Data Server.
PandoraFMS::DataServer->new($conf);
# Create the stress agent.
my $agent_id = get_agent_id($dbh, $STRESS_AGENT_NAME);
if ($agent_id == -1) {
print_log("XML", "creating stress agent");
$agent_id = pandora_create_agent ($conf, '', safe_input($STRESS_AGENT_NAME), '127.0.0.1', 10, 0, 10, 'Pandora FMS XML stress agent', 300, $dbh);
} else {
print_log("XML", "stress agent already exists");
}
if (!defined($agent_id) || $agent_id == -1) {
die("Error creating stress agent.\n\n");
}
# Make sure the timestamp from the XML file (as opposed to the timestamp in the XML data) is never used.
$conf->{'use_xml_timestamp'} = 0;
# Pre-generate timestamps.
my @timestamps;
for (my $i = 0, my $offset = 0; $i < $STRESS_AGENT_OPS; $i++, $offset += 172800) {
$timestamps[$i] = strftime ("%Y-%m-%d %H:%M:%S", localtime($offset));
}
print_log("XML", "initializing $STRESS_AGENT_MODULES modules");
for (my $i = 0; $i < $STRESS_AGENT_MODULES; $i++) {
push(@{$STRESS_AGENT_XML->{'module'}},
{
'name' => ["Module $i"],
'data' => [$i],
'type' => ['generic_data']
});
}
print_log("XML", "start");
my $t0 = [gettimeofday];
for (my $i = 0; $i < $STRESS_AGENT_OPS; $i++) {
$STRESS_AGENT_XML->{'timestamp'} = $timestamps[$i];
PandoraFMS::DataServer::process_xml_data ($conf, $0, $STRESS_AGENT_XML, 0, $dbh);
}
my $elapsed = tv_interval($t0);
my $agent_score = ratio($STRESS_AGENT_OPS, $elapsed);
my $module_score = ratio($STRESS_AGENT_OPS * $STRESS_AGENT_MODULES, $elapsed);
print_log("XML", "stop");
print_log("XML", "deleting stress agent");
pandora_delete_agent($dbh, $agent_id);
return { 'agent_score' => $agent_score, 'module_score' => $module_score };
}
################################################################################
# Compute table stats.
################################################################################
sub table_stats {
my ($dbh, $conf) = @_;
my $stats = {
tagent_access => 'N/A',
tagente => 'N/A',
tagente_datos => 'N/A',
tagente_datos_string => 'N/A',
tagente => 'N/A',
tevento => 'N/A',
tsesion => 'N/A',
};
my @rows = get_db_rows($dbh, "SELECT TABLE_NAME, TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA=?
AND TABLE_NAME IN (?, ?, ?, ?, ?, ?, ?)",
$conf->{'dbname'},
'tagent_access',
'tagente',
'tagente_datos',
'tagente_datos_string',
'tagente_modulo',
'tevento',
'tsesion',
);
foreach my $row (@rows) {
$stats->{$row->{'TABLE_NAME'}} = metric($row->{'TABLE_ROWS'});
}
return $stats;
}
################################################################################
# Add recommendations based on the given table stats.
################################################################################
sub table_comments {
my ($stats) = @_;
my $comments = {
tagent_access => 'OK',
tagente => 'OK',
tagente_datos => 'OK',
tagente_datos_string => 'OK',
tagente_modulo => 'OK',
tagente => 'OK',
tevento => 'OK',
tsesion => 'OK',
};
if ($stats->{'tagent_access'} > $stats->{'tagente'} * 24 * 250) {
$comments->{'tagent_access'} = 'CRITICAL: Table too big. Please contact our support team at: support@artica.es';
} elsif ($stats->{'tagent_access'} > $stats->{'tagente'} * 24 * 100) {
$comments->{'tagent_access'} = 'WARNING: Table too big. Please contact our support team at: support@artica.es';
}
if ($stats->{'tagente_datos'} > 5000000) {
$comments->{'tagente_datos'} = 'CRITICAL: Table too big. Please use a history database or decrease the purge period.';
} elsif ($stats->{'tagente_datos'} > 1000000) {
$comments->{'tagente_datos'} = 'WARNING: Table too big. Please use a history database or decrease the purge period.';
}
if ($stats->{'tagente_modulo'} > 500000) {
$comments->{'tsesion'} = 'CRITICAL: Table too big. Please contact our support team at: support@artica.es';
} elsif ($stats->{'tagente_modulo'} > 350000) {
$comments->{'tsesion'} = 'WARNING Table too big. Please contact our support team at: support@artica.es';
}
if ($stats->{'tevento'} > 50000) {
$comments->{'tevento'} = 'CRITICAL: Table too big. Please use a history database or decrease the purge period.';
} elsif ($stats->{'tevento'} > 25000) {
$comments->{'tevento'} = 'WARNING: Table too big. Please use a history database or decrease the purge period.';
}
if ($stats->{'tsesion'} > 50000) {
$comments->{'tsesion'} = 'CRITICAL: Table too big. Please contact our support team at: support@artica.es';
} elsif ($stats->{'tsesion'} > 15000) {
$comments->{'tsesion'} = 'WARNING: Table too big. Please contact our support team at: support@artica.es';
}
return $comments;
}
################################################################################
# Main.
################################################################################
my %conf;
# Connect to the DB.
pandora_init(\%conf,"Pandora FMS Performance Report Tool");
pandora_load_config(\%conf);
my $dbh = db_connect($conf{'dbengine'}, $conf{'dbname'}, $conf{'dbhost'}, $conf{'dbport'}, $conf{'dbuser'}, $conf{'dbpass'});
# Do not show server messages when running the stress tests.
$conf{'verbosity'} = 0;
$conf{'daemon'} = 0;
# CPU score.
my $cpu_score = stress_cpu();
# DB Scores.
my $db_scores = stress_db($dbh);
# Table stats.
my $table_stats = table_stats($dbh, \%conf);
# Add recommendations.
my $table_comments = table_comments($table_stats);
# Agent and module stats.
my $agent_scores = stress_agent($dbh, \%conf);
db_disconnect($dbh);
# Print the report.
my $tstamp = strftime("%Y-%m-%d %H:%M:%S", localtime());
my $host = hostname;
my $cpu = `cat /proc/cpuinfo | grep "model name" | head -1 | cut -d: -f2 | tr -d ' '`;
chomp($cpu);
my $cores = `cat /proc/cpuinfo | grep "model name" | wc -l`;
chomp($cores);
my $mem = `cat /proc/meminfo | grep "MemTotal" | cut -d: -f2 | tr -d ' '`;
chomp($mem);
my $max_agents = floor ($agent_scores->{'agent_score'} * 300);
my $max_modules = floor ($agent_scores->{'module_score'} * 300);
print <<__EOF
-------------------------------------------------------------------------------
Pandora FMS Performance Report Tool v$VERSION
Report generated at: $tstamp
Host: $host
CPU: $cpu
Cores: $cores
Memory: $mem
Metric Value Reference value for small/medium/large systems (*)
------ ----- --------------------------------------------------
CPU ops/ms $cpu_score (NA/NA/NA)
DB INSERT/s $db_scores->{'insert_score'} (NA/NA/NA)
DB UPDATE/s $db_scores->{'update_score'} (NA/NA/NA)
DB SELECT/s $db_scores->{'select_score'} (NA/NA/NA)
DB DELETE/s $db_scores->{'delete_score'} (NA/NA/NA)
(*) small ~ 250 agents | medium ~ 1000 agents | large > 5000 agents
Database Analisys Row count Comments & recomendations
----------------- --------- -------------------------
Agent table $table_stats->{'tagente'} $table_comments->{'tagente'}
Module table $table_stats->{'tagente_modulo'} $table_comments->{'tagente_modulo'}
Data table $table_stats->{'tagente_datos'} $table_comments->{'tagente_datos'}
String data table $table_stats->{'tagente_datos_string'} $table_comments->{'tagente_datos_string'}
Event table $table_stats->{'tevento'} $table_comments->{'tevento'}
Access stats $table_stats->{'tagent_access'} $table_comments->{'tagent_access'}
Audit information $table_stats->{'tsesion'} $table_comments->{'tsesion'}
Agent Data Processing (**)
--------------------------
Agents per second $agent_scores->{'agent_score'}
Modules per second $agent_scores->{'module_score'}
Max usage recommended (**)(***)
-------------------------------
Max agents $max_agents
Max modules $max_modules
(**) Single thread worst case scenario. Not representative of the real
performance of a multi-threaded Pandora FMS Data Server.
(***) Taking a base interval 300s (default).
__EOF