You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2957 lines
97 KiB
Plaintext
2957 lines
97 KiB
Plaintext
head 1.5;
|
|
access;
|
|
symbols;
|
|
locks
|
|
nagios:1.5; strict;
|
|
comment @# @;
|
|
|
|
|
|
1.5
|
|
date 2013.05.03.14.55.23; author nagios; state Exp;
|
|
branches;
|
|
next 1.4;
|
|
|
|
1.4
|
|
date 2013.04.15.18.23.32; author nagios; state Exp;
|
|
branches;
|
|
next 1.3;
|
|
|
|
1.3
|
|
date 2013.02.13.20.01.55; author nagios; state Exp;
|
|
branches;
|
|
next 1.2;
|
|
|
|
1.2
|
|
date 2013.01.04.19.50.05; author nagios; state Exp;
|
|
branches;
|
|
next 1.1;
|
|
|
|
1.1
|
|
date 2012.11.12.16.14.21; author nagios; state Exp;
|
|
branches;
|
|
next ;
|
|
|
|
|
|
desc
|
|
@Original
|
|
@
|
|
|
|
|
|
1.5
|
|
log
|
|
@Remove unnecessary comments; manage all thread termination from main thread
|
|
@
|
|
text
|
|
@#!/usr/bin/perl -w
|
|
#
|
|
# By Igor Gubenko (igubenko@@Princeton.EDU) 09/07/2012
|
|
#
|
|
# Script to create or update a Cacti graph from given Nagios output
|
|
#
|
|
# $Header: /usr/local/nagios/libexec/eventhandlers/RCS/cacti_update,v 1.4 2013/04/15 18:23:32 nagios Exp nagios $
|
|
#
|
|
|
|
use lib qw(/usr/local/perl/modules/lib/perl5/site_perl/5.8.8 /usr/local/perl/modules/lib/perl5/5.8.8 /usr/local/perl/modules/lib64/perl5/site_perl/5.8.8 /usr/local/perl/modules/lib64/perl5/5.8.8 /usr/local/perl/lib/perl5/site_perl/5.8.8 /usr/local/perl/lib/perl5/5.8.8 /usr/local/perl/lib64/perl5/site_perl/5.8.8 /usr/local/perl/lib64/perl5/5.8.8);
|
|
|
|
use strict;
|
|
use warnings;
|
|
use WWW::Mechanize;
|
|
use HTML::TableExtract;
|
|
use Data::Dumper;
|
|
use Digest::MD5 qw(md5);
|
|
use threads;
|
|
use threads::shared;
|
|
use Thread::Queue;
|
|
use POSIX;
|
|
use DBI;
|
|
use File::Pid;
|
|
|
|
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
|
|
my $myname = $0;
|
|
my ($mech, $cacdb);
|
|
my $cacti_path = '/usr/local/cacti';
|
|
my $rrapath = '/usr/local/cacti/nagiosrrd';
|
|
my $rrdtool = '/usr/local/groundwork/common/bin/rrdtool';
|
|
my $php = '/usr/local/groundwork/php/bin/php';
|
|
my $spec_file = '/usr/local/nagios/libexec/eventhandlers/cacti_service_spec';
|
|
my $close_file = '/usr/local/nagios/libexec/eventhandlers/posttasks';
|
|
my $perf_file = '/usr/local/nagios/var/log/service_perfdata.dat';
|
|
#my $perf_file = '/usr/local/nagios/libexec/eventhandlers/data.temp';
|
|
my $log_file = '/usr/local/nagios/var/log/cacti_update.log';
|
|
my $persistence_file = '/usr/local/nagios/var/cacti_persist.cache';
|
|
my $pidpath = "/var/run/cacti/" . $myname . ".pid";
|
|
my $pidfile;
|
|
|
|
my $cacti_user = 'csgtst05';
|
|
my $cacti_pass = 'C5Gt$t42!!!!!!!!';
|
|
#my $cacti_user = 'groundwk';
|
|
#my $cacti_pass = 'th4de/DC';
|
|
|
|
my $cacti_host = "ims204";
|
|
my $cacti_url = "https://${cacti_host}.princeton.edu/cactigraphs";
|
|
|
|
my $rracacspec = 'RRA:AVERAGE:0.5:1:500 RRA:AVERAGE:0.5:1:600 RRA:AVERAGE:0.5:6:700 RRA:AVERAGE:0.5:24:775 RRA:AVERAGE:0.5:288:797 RRA:MAX:0.5:1:500 RRA:MAX:0.5:1:600 RRA:MAX:0.5:6:700 RRA:MAX:0.5:24:775 RRA:MAX:0.5:288:797';
|
|
|
|
my %spec :shared;
|
|
my %cacti_hosts;
|
|
my %data_templates;
|
|
my %perfhist :shared;
|
|
my %graph_templates;
|
|
my $verbose = 1;
|
|
my $outtype = "log";
|
|
my $terminate :shared = 0;
|
|
my $waiton :shared = 0;
|
|
my $pnag;
|
|
my $pnagcac;
|
|
|
|
#my @@data_templates_created;
|
|
my %data_source_created;
|
|
|
|
sub begin;
|
|
sub load;
|
|
sub unload;
|
|
sub reload;
|
|
sub read_config;
|
|
sub read_perfdata;
|
|
sub update_rrd;
|
|
|
|
sub cacti_update;
|
|
sub dt_check_create;
|
|
sub gt_check_create;
|
|
sub ds_check_create;
|
|
sub g_check_create;
|
|
|
|
sub end_program;
|
|
sub graceful_die;
|
|
sub verbose;
|
|
|
|
sub DB_get;
|
|
sub get_cacti_data_templates;
|
|
sub get_cacti_data_template_component_id;
|
|
#sub get_cacti_data_sources;
|
|
sub get_cacti_host_template_ds;
|
|
sub get_cacti_graph_templates;
|
|
sub get_cacti_host_ds_components;
|
|
sub get_cacti_graph_template_component;
|
|
sub get_cacti_graph;
|
|
sub get_cacti_hosts;
|
|
|
|
begin;
|
|
|
|
graceful_die 1, 1;
|
|
|
|
##################################
|
|
#### Subroutines
|
|
|
|
# This routine creates/updates Cacti to use external RRD's
|
|
sub cacti_update {
|
|
# Next part is to create the Cacti mapping, for whatever graphs if wasn't done yet
|
|
# 1) Create Data Template -- per RRD, per host
|
|
# 2) Create Graph Template -- per Graph spec
|
|
# 3) Create Data Source -- per RRD, per host
|
|
# 4) Create New Graph -- per Graph spec --- doesn't have a "name" - just association
|
|
#
|
|
verbose 1, "------ Cacti update thread started ------";
|
|
|
|
verbose 1, "Trying to log to Cacti...";
|
|
|
|
$mech = WWW::Mechanize->new(agent => 'Mozilla/5.0', stack_depth => '2', noproxy => '1');
|
|
|
|
$mech->credentials($cacti_user, $cacti_pass);
|
|
|
|
## Get initial Cacti login page
|
|
$mech->get($cacti_url);
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Console#) {
|
|
verbose 0, "Failed to login. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
graceful_die 1, 1;
|
|
}
|
|
|
|
verbose 1, "Login to Cacti successful\n";
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content . "\n\n##########################";
|
|
|
|
$cacdb = DBI -> connect('DBI:mysql:cactigraphs', 'cactus', 'th4de/DC', {'RaiseError' => 1});
|
|
|
|
unless ($cacdb) {
|
|
verbose 0, "Failed to connect to the Cacti database: $DBI::errstr";
|
|
graceful_die 1, 1;
|
|
}
|
|
|
|
verbose 1, "Successfully connected to the Cacti database\n";
|
|
|
|
my $hst_tmp = get_cacti_hosts;
|
|
%cacti_hosts = %$hst_tmp;
|
|
|
|
my $dt_tmp = get_cacti_data_templates;
|
|
%data_templates = %$dt_tmp;
|
|
|
|
my $gt_tmp = get_cacti_graph_templates;
|
|
%graph_templates = %$gt_tmp;
|
|
|
|
open PERS, ">> $persistence_file";
|
|
|
|
CAC: while ( $_ = $pnagcac -> dequeue ) {
|
|
last if /Terminate|Reload/;
|
|
|
|
next unless /Next service/;
|
|
|
|
my %perfdata;
|
|
my ($curhost, $curserv, $hstspec);
|
|
|
|
while ( $_ = $pnagcac -> dequeue ) {
|
|
last if /End service/;
|
|
last CAC if /Terminate|Reload/;
|
|
|
|
/Host: (.*)/ and do {
|
|
$curhost = $1;
|
|
$perfdata{$curhost} = {};
|
|
};
|
|
/Service: (.*)/ and do {
|
|
verbose 1, "Cacti: New perf for Cacti: \"$curhost\" -- \"$1\"";
|
|
$curserv = $1;
|
|
$perfdata{$curhost} -> {$curserv} = {};
|
|
};
|
|
/Host Spec: (.*)/ and $hstspec = $1;
|
|
/Service Spec: (.*)/ and do {
|
|
lock %spec;
|
|
lock %{$spec{$1}};
|
|
$perfdata{$curhost} -> {$curserv} -> {spec} = $spec{$1} -> {$hstspec};
|
|
};
|
|
/Perf: (.*)/ and do {
|
|
my $perf = $1;
|
|
$_ = $pnagcac -> dequeue;
|
|
my @@perfsplit = split /::: /, $perf;
|
|
my $perflbl = shift @@perfsplit;
|
|
$perfdata{$curhost} -> {$curserv} -> {perf} -> {$perflbl} = \@@perfsplit;
|
|
};
|
|
}
|
|
|
|
my $curperf = $perfdata{$curhost}->{$curserv};
|
|
|
|
# Check if the host exists, first. If it's not in Cacti, there's nothing to do really
|
|
next unless grep /^${curhost}(|\.princeton\.edu)$/i, keys %cacti_hosts;
|
|
verbose 1, "Cacti: \"$curhost\" exists. Need to check for graph data";
|
|
|
|
# Check if the data template exists, and if not, create it
|
|
next if dt_check_create ($curperf, $curserv, $curhost);
|
|
verbose 1, "Cacti: Data template exists for \"$curserv\". Need to check for graph data";
|
|
|
|
# Check if the graph template exists, and if not, create it
|
|
next if gt_check_create ($curperf, $curserv, $curhost);
|
|
verbose 1, "Cacti: Graph template exists for \"$curserv\". Need to check for data sources, and graphs";
|
|
|
|
# Check if the data source exists, and if not, create it
|
|
next if ds_check_create ($curperf, $curserv, $curhost);
|
|
verbose 1, "Cacti: Data source exists for \"$curhost\" - \"$curserv\". Need to check for the graph";
|
|
|
|
# Check if the graph exists, and if not, create it
|
|
#g_check_create ($curperf, $curserv, $curhost);
|
|
|
|
if (g_check_create ($curperf, $curserv, $curhost)) {
|
|
lock %perfhist;
|
|
lock %{$perfhist{$curhost}};
|
|
$perfhist{$curhost}->{$curserv} = 1; # Error, do not write to persistence; also try processing again
|
|
} else {
|
|
lock %perfhist;
|
|
lock %{$perfhist{$curhost}};
|
|
$perfhist{$curhost}->{$curserv} = 2; # OK, processed fine - write to persistence
|
|
|
|
print PERS "${curhost}::::${curserv}\n";
|
|
}
|
|
}
|
|
|
|
close PERS;
|
|
|
|
$cacdb -> disconnect;
|
|
$mech->get ("${cacti_url}/logout.php");
|
|
|
|
verbose 1, "------ Cacti update thread terminated ------";
|
|
|
|
return 0;
|
|
}
|
|
|
|
# This routine checks if a data template exists, and if not, it creates it
|
|
sub dt_check_create {
|
|
my $data = shift;
|
|
my $serv = shift;
|
|
my $host = shift;
|
|
|
|
# We need a unique name for a template, unique for service name AND its perfdata items
|
|
my $cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
|
|
# If we have the data template on record, don't waste time
|
|
#return 0 if grep m#^$cactiserv$#i, @@data_templates_created;
|
|
return 0 if (grep m#^$cactiserv$#i, keys %data_templates);
|
|
|
|
verbose (1, "Cacti: Data Template not found for \"$cactiserv\" - will create");
|
|
|
|
# Initial Cacti page containing data template data
|
|
# $mech->get ("${cacti_url}/data_templates.php");
|
|
|
|
# unless ($mech -> success() && $mech->title() =~ m#Console -> Data Templates#) {
|
|
# verbose 0, "Cacti: Failed to fetch \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
# verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
# return 1;
|
|
# }
|
|
|
|
# verbose 2, "Cacti: Query of ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
##############################################################
|
|
# Search for the data template - check if it exists
|
|
# $mech->submit_form(
|
|
# form_name => 'form_data_template',
|
|
# fields => {
|
|
# filter => $cactiserv
|
|
# },
|
|
# );
|
|
|
|
# unless ($mech -> success() && $mech->title() =~ m#Console -> Data Templates#) {
|
|
# verbose 0, "Cacti: Failed to submit to \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
# verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
# return 1;
|
|
# }
|
|
|
|
# verbose 2, "Cacti: Submit to ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
##############################################################
|
|
# Found the data template?
|
|
#<a class='linkEditMain' href='data_templates.php?action=template_edit&id=27'><span style='background-color: #F8D93D;'>Cisco Router - 5 Minute CPU</span>
|
|
# if (grep m#<a class='linkEditMain' href='data_templates.php.*>${cactiserv}</span>#, $mech->response()->decoded_content) {
|
|
# verbose (1, "Cacti: Data Template already exists for \"$cactiserv\"");
|
|
# return 0;
|
|
# }
|
|
# verbose (1, "Cacti: Data Template not found for \"$cactiserv\" - will create");
|
|
|
|
# No, need to create it
|
|
# Go to data template add page
|
|
# $mech->follow_link (text => 'Add');
|
|
|
|
$mech->get ("${cacti_url}/data_templates.php?action=template_edit");
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Templates -> \(Edit\)#) {
|
|
verbose 0, "Cacti: Failed to go to the data template addition page. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Accessed the page to add a data template:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
###############################################################
|
|
|
|
my @@dsnames;
|
|
@@dsnames = keys %{$data->{perf}}; # Our pefdata labels, which are also internal data source names for Cacti
|
|
|
|
my $inids = shift @@dsnames;
|
|
my @@create_item_regexs;
|
|
my $create_item_spec_match;
|
|
my %typemap = ( GAUGE => 1, COUNTER => 2, DERIVE => 3, ABSOLUTE => 4 );
|
|
|
|
{ lock $data->{spec}; lock %{$data->{spec}}; lock %{$data->{spec}->{create_items}};
|
|
@@create_item_regexs = keys %{$data->{spec}->{create_items}};
|
|
$create_item_spec_match = join "", grep { $inids =~ /^$_$/i } @@create_item_regexs;
|
|
lock %{$data->{spec}->{create_items}->{$create_item_spec_match}};
|
|
|
|
#print "data_source_name => $inids,
|
|
#t_rrd_minimum => $data->{spec}->{create_items}->{$create_item_spec_match}->{min},
|
|
#t_rrd_maximum => $data->{spec}->{create_items}->{$create_item_spec_match}->{max},
|
|
#data_source_type_id => $typemap{$data->{spec}->{create_items}->{$create_item_spec_match}->{type}},\n";
|
|
#exit 1;
|
|
|
|
# In Cacti, we first create an initial data template, with an initial data source name, and then we can add more
|
|
$mech->submit_form(
|
|
fields => {
|
|
template_name => "$cactiserv",
|
|
name => "|host_description| - $cactiserv",
|
|
active => 'unchecked',
|
|
data_source_name => $inids,
|
|
rrd_minimum => $data->{spec}->{create_items}->{$create_item_spec_match}->{min},
|
|
rrd_maximum => $data->{spec}->{create_items}->{$create_item_spec_match}->{max},
|
|
data_source_type_id => $typemap{$data->{spec}->{create_items}->{$create_item_spec_match}->{type}},
|
|
},
|
|
);
|
|
}
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save the new data template \"$serv\" -- \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved the new data template \"$serv\" to ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
#################################################################
|
|
|
|
# Now we can add additional items to the data template (perfdata labels), if needed
|
|
my $dsprev = $inids;
|
|
foreach my $nextds (@@dsnames) {
|
|
$create_item_spec_match = join "", grep { $nextds =~ /^$_$/i } @@create_item_regexs;
|
|
|
|
# First we ask to create a new component
|
|
$mech -> follow_link (text => 'New');
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#\d+: $dsprev#) {
|
|
verbose 0, "Cacti: Failed to create new item in the new data template \"$serv\" -- \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Created new component for new data template \"$serv\", ds component \"$nextds\" in ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
##################################################
|
|
# Then we save it with necessary parameters
|
|
{ lock $data->{spec}; lock %{$data->{spec}}; lock %{$data->{spec}->{create_items}}; lock %{$data->{spec}->{create_items}->{$create_item_spec_match}};
|
|
$mech->submit_form(
|
|
fields => {
|
|
data_source_name => $nextds,
|
|
rrd_minimum => $data->{spec}->{create_items}->{$create_item_spec_match}->{min},
|
|
rrd_maximum => $data->{spec}->{create_items}->{$create_item_spec_match}->{max},
|
|
data_source_type_id => $typemap{$data->{spec}->{create_items}->{$create_item_spec_match}->{type}},
|
|
},
|
|
);
|
|
}
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save the new data template \"$serv\" with new item \"$nextds\" - \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved new component for new data template \"$serv\", ds component \"$nextds\" in ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
$dsprev = $nextds;
|
|
}
|
|
|
|
#push @@data_templates_created, $cactiserv;
|
|
my $dt_tmp = get_cacti_data_templates;
|
|
%data_templates = %$dt_tmp;
|
|
|
|
verbose (1, "Cacti: Successfully created a new data template for service \"$cactiserv\"");
|
|
return 0; # Successful creation of all components
|
|
}
|
|
|
|
# Check if the graph template exists, and if not, create
|
|
sub gt_check_create {
|
|
my $data = shift;
|
|
my $serv = shift;
|
|
my $host = shift;
|
|
my ($cactiserv, $uniqname);
|
|
|
|
$uniqname = unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
$cactiserv = "${serv}_${uniqname}";
|
|
|
|
{ lock $data->{spec}; lock %{$data->{spec}};
|
|
# Does the template exist?
|
|
#return 0 if grep /^$data->{spec}->{graph_template_name}_${cactiserv}$/i, @@graph_templates;
|
|
return 0 if exists $graph_templates{"$data->{spec}->{graph_template_name}_${cactiserv}"};
|
|
}
|
|
|
|
verbose 1, "Cacti: Graph template not found. Will create";
|
|
|
|
# Access initial page containing the graph templates
|
|
$mech->get ("${cacti_url}/graph_templates.php");
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Graph Templates#) {
|
|
verbose 0, "Cacti: Failed to fetch \"${cacti_url}/graph_templates.php\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Query of ${cacti_url}/graph_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
#######################################################
|
|
# Add a new one
|
|
$mech -> follow_link (text => 'Add');
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Graph Templates -> \(Edit\)#) {
|
|
verbose 0, "Cacti: Failed to follow the \"Add\" link. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Followed the \"Add\" link:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
########################################################
|
|
# Next we need to save the new graph template with global graph template data
|
|
|
|
my ($title, $hgt, $wdt, $t_upper, $t_lower, $t_base, $g_templ_name, $g_templ_label);
|
|
|
|
{ lock $data->{spec}; lock %{$data->{spec}};
|
|
$title = "|host_description| - " . (exists $data->{spec}->{graph_title} ? $data->{spec}->{graph_title} : $serv) . "($uniqname)";
|
|
$hgt = exists $data->{spec}->{graph_height} ? $data->{spec}->{graph_height} : 120;
|
|
$wdt = exists $data->{spec}->{graph_width} ? $data->{spec}->{graph_width} : 500;
|
|
$t_upper = exists $data->{spec}->{graph_upper_limit} ? $data->{spec}->{graph_upper_limit} : 100;
|
|
$t_lower = exists $data->{spec}->{graph_lower_limit} ? $data->{spec}->{graph_lower_limit} : 0;
|
|
$t_base = exists $data->{spec}->{graph_base} ? $data->{spec}->{graph_base} : 1000;
|
|
$g_templ_name = $data->{spec}->{graph_template_name};
|
|
|
|
# If the desired graph label is the same as the data source item name (for a single DS, we need to get it out)
|
|
my ($firstlbl) = keys %{$data->{perf}};
|
|
$g_templ_label = $data->{spec}->{graph_label} =~ m#\^item\^#i ? $data->{perf}->{$firstlbl}->[5] : $data->{spec}->{graph_label};
|
|
}
|
|
|
|
#print "$title###$data->{spec}->{graph_label}###\n";
|
|
#return 1;
|
|
|
|
$mech -> submit_form (
|
|
fields => {
|
|
name => "${g_templ_name}_${cactiserv}",
|
|
title => $title,
|
|
height => $hgt,
|
|
width => $wdt,
|
|
upper_limit => $t_upper,
|
|
lower_limit => $t_lower,
|
|
base_value => $t_base,
|
|
vertical_label => $g_templ_label,
|
|
},
|
|
);
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save the new graph template \"${g_templ_name}_${cactiserv}\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved new graph template \"${g_templ_name}_${cactiserv}\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
############################################################
|
|
# Now we need to add graph components, and associate them with items in the relevant data template
|
|
|
|
my (@@dsnames, @@graph_item_regexs);
|
|
@@dsnames = keys %{$data->{perf}};
|
|
{ lock $data->{spec}; lock %{$data->{spec}}; lock %{$data->{spec}->{graph_items}};
|
|
@@graph_item_regexs = keys %{$data->{spec}->{graph_items}};
|
|
}
|
|
|
|
my (%data_template_sel, %colors_sel, %function_type_sel, %graph_type_sel, %cdef_sel, %gprint_sel);
|
|
my ($data_template_sel_items, $colors_sel_items, $function_type_sel_items, $graph_type_sel_items, $cdef_sel_items, $gprint_sel_items);
|
|
|
|
# For each data source item
|
|
foreach my $curds (@@dsnames) {
|
|
my $graph_item_spec_match = join "", grep { $curds =~ /^$_$/i } @@graph_item_regexs;
|
|
my @@gr_items;
|
|
|
|
{ lock $data->{spec}; lock %{$data->{spec}}; lock %{$data->{spec}->{graph_items}}; lock %{$data->{spec}->{graph_items}->{$graph_item_spec_match}};
|
|
@@gr_items = @@{$data->{spec}->{graph_items}->{$graph_item_spec_match}};
|
|
}
|
|
|
|
# For each graph component of relevant ds item (line, area, etc)
|
|
foreach my $curgritem (@@gr_items) {
|
|
$mech -> follow_link (text => 'Add', n => 1);
|
|
my $curcont = $mech->response()->decoded_content;
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Graph Templates -> \(Edit\) -> Graph Template Items#) {
|
|
verbose 0, "Cacti: Failed to add a new graph component by following the \"Add\" link. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $curcont;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Followed the \"Add\" link to add a new graph component:\n" . $curcont . "\n\n######################";
|
|
|
|
# Load a list of data templates
|
|
($data_template_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^task_item_id$/,
|
|
);
|
|
# We only need to determine the list of data templates once
|
|
# @@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values if keys %data_template_sel == 0;
|
|
|
|
#print Dumper (\%data_template_sel);
|
|
|
|
# We need to determine the relevant data template id to select each time
|
|
# We do this, in case the key matched case-insensitive
|
|
# my $keytemp = join "", grep m#^${cactiserv}\s+\-\s+\(${curds}\)$#i, keys %data_template_sel;
|
|
my $keytemp = get_cacti_data_template_component_id ($cactiserv, $curds);
|
|
#verbose 0, Dumper ($keytemp);
|
|
unless ($keytemp && @@$keytemp == 1) {
|
|
verbose 0, "Cacti: Could not locate the data_template ID for \"$cactiserv\"";
|
|
return 1;
|
|
}
|
|
$keytemp = $keytemp->[0];
|
|
#verbose 0, "\n###$keytemp###\n";
|
|
#print $data_template_sel{$keytemp}, "\n";
|
|
# Select the right data template
|
|
# $data_template_sel_items -> value ($data_template_sel{$keytemp});
|
|
$data_template_sel_items -> value ($keytemp);
|
|
|
|
# Load a list of colors
|
|
($colors_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^color_id$/,
|
|
);
|
|
@@colors_sel{$colors_sel_items->value_names} = $colors_sel_items->possible_values if keys %colors_sel == 0;
|
|
|
|
# Load a list of graph types (AREA, GRPINT, etc)
|
|
($graph_type_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^graph_type_id$/,
|
|
);
|
|
@@graph_type_sel{$graph_type_sel_items->value_names} = $graph_type_sel_items->possible_values if keys %graph_type_sel == 0;
|
|
|
|
# Load a list of functions (AVERAGE, MIN, MAX)
|
|
($function_type_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^consolidation_function_id$/,
|
|
);
|
|
@@function_type_sel{$function_type_sel_items->value_names} = $function_type_sel_items->possible_values if keys %function_type_sel == 0;
|
|
|
|
# Load a list of CDEF's
|
|
($cdef_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^cdef_id$/,
|
|
);
|
|
@@cdef_sel{$cdef_sel_items->value_names} = $cdef_sel_items->possible_values if keys %cdef_sel == 0;
|
|
|
|
# Load a list of GPRINT types
|
|
($gprint_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^gprint_id$/,
|
|
);
|
|
@@gprint_sel{$gprint_sel_items->value_names} = $gprint_sel_items->possible_values if keys %gprint_sel == 0;
|
|
|
|
|
|
my $gritem = join "", keys %{$curgritem}; # AREA, LINE1, GRPINT, etc
|
|
|
|
# Select the right graph type
|
|
$keytemp = join "", grep m#^$gritem$#i, keys %graph_type_sel;
|
|
$graph_type_sel_items -> value ($graph_type_sel{$keytemp});
|
|
|
|
# Function is average for graphical items, and whatever is specified for everything else
|
|
my $func = $gritem =~ m#area|stack|line\d+#i ? 'AVERAGE' : $curgritem -> {$gritem} -> {function};
|
|
|
|
# Select the right function type
|
|
$keytemp = join "", grep m#^$func$#i, keys %function_type_sel;
|
|
$function_type_sel_items -> value ($function_type_sel{$keytemp});
|
|
|
|
# Select the right CDEF if necessary
|
|
if (exists $curgritem->{$gritem}->{cdef}) {
|
|
$keytemp = join "", grep m#^$curgritem->{$gritem}->{cdef}$#i, keys %cdef_sel;
|
|
$cdef_sel_items -> value ($cdef_sel{$keytemp});
|
|
}
|
|
|
|
# Select the right GPRINT type if necessary
|
|
if (exists $curgritem->{$gritem}->{type}) {
|
|
$keytemp = join "", grep m#^$curgritem->{$gritem}->{type}$#i, keys %gprint_sel;
|
|
$gprint_sel_items -> value ($gprint_sel{$keytemp});
|
|
}
|
|
|
|
my $text;
|
|
|
|
$text = $curgritem -> {$gritem} -> {text} =~ m#\^item\^# ? $data->{perf}->{$curds}->[5] : $curgritem -> {$gritem} -> {text};
|
|
|
|
# Select the right color, or if missing/random - select a random color
|
|
exists $curgritem -> {$gritem} -> {color} and do {
|
|
$keytemp = join "", grep m#^$curgritem->{$gritem}->{color}$#i, keys %colors_sel;
|
|
|
|
# Color not found or random color
|
|
my @@colorkeys = keys %colors_sel;
|
|
while ($keytemp eq 'FFFFFF' || $keytemp eq '') { # White color is ignored
|
|
$keytemp = $colorkeys[int (rand (scalar (@@colorkeys)))];
|
|
}
|
|
|
|
$colors_sel_items -> value ($colors_sel{$keytemp});
|
|
};
|
|
|
|
# Hard return checked if this is the last item
|
|
#my $hard_return = $gr_items[@@gr_items-1] eq $curgritem ? 'checked' : 'unchecked';
|
|
$mech -> field (hard_return => 'on') if ($gr_items[@@gr_items-1] eq $curgritem || exists $curgritem -> {$gritem} -> {eol});
|
|
|
|
$mech -> submit_form (
|
|
fields => {
|
|
text_format => $text,
|
|
# hard_return => $hard_return,
|
|
},
|
|
);
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save a new graph item $curds -> $func for new graph template \"$g_templ_name\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved new graph item $curds -> $func for new graph template \"$g_templ_name\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
}
|
|
}
|
|
|
|
verbose 1, "Cacti: Graph template \"${g_templ_name}_${cactiserv}\" successfully created";
|
|
|
|
my $gt_tmp = get_cacti_graph_templates;
|
|
%graph_templates = %$gt_tmp;
|
|
|
|
#@@graph_templates = `$php -q ${cacti_path}/cli/add_graphs.php --list-graph-templates`;
|
|
#shift @@graph_templates;
|
|
#map { chomp; s/^\d+\s+//; } @@graph_templates;
|
|
|
|
return 0; # Successful creation of all components
|
|
}
|
|
|
|
# Check if the data source exists, and if not, create
|
|
sub ds_check_create {
|
|
my $data = shift;
|
|
my $serv = shift;
|
|
my $host = shift;
|
|
my $cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
|
|
return 0 if (get_cacti_host_template_ds ($host, $cactiserv));
|
|
#if (get_cacti_host_template_ds ($host, $cactiserv)) {
|
|
#verbose 1, "Data source already exists for host \"$host\" and service \"$cactiserv\"";
|
|
#return 0;
|
|
#}
|
|
|
|
# If we have the data source on record, don't waste time
|
|
# return 0 if exists $data_source_created{$cactiserv} && grep m#^$host$#i, @@{$data_source_created{$cactiserv}};
|
|
|
|
# First see if the data sources already exist
|
|
# Get the page with data sources
|
|
# $mech -> get ("${cacti_url}/data_sources.php");
|
|
|
|
# unless ($mech -> success() && $mech->title() =~ m#Console -> Data Sources#) {
|
|
# verbose 0, "Cacti: Failed to fetch \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
# verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
# return 1;
|
|
# }
|
|
|
|
# verbose 2, "Cacti: Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
##################################################
|
|
my (%data_template_sel, %host_sel);
|
|
my ($data_template_sel_items, $host_sel_items);
|
|
|
|
# Load a list of data templates
|
|
# ($data_template_sel_items) = $mech -> find_all_inputs (
|
|
# type => 'option',
|
|
# name_regex => qr/^template_id$/,
|
|
# );
|
|
# @@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values;
|
|
|
|
# Locate the needed data template in the list, and select it
|
|
# my $servkey = join "", grep m#^$cactiserv$#i, keys %data_template_sel;
|
|
# my $hostkey;
|
|
# if (exists $data_template_sel{$servkey}) { # No hosts with the template???
|
|
# $data_template_sel_items -> value ($data_template_sel{$servkey});
|
|
####################
|
|
# Load a list of hosts
|
|
# ($host_sel_items) = $mech -> find_all_inputs (
|
|
# type => 'option',
|
|
# name_regex => qr/^host_id$/,
|
|
# );
|
|
# @@host_sel{$host_sel_items->value_names} = $host_sel_items->possible_values;
|
|
|
|
# Locate the needed host in the list, and select it
|
|
# $hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
# $hostkey = join "", grep m# \(${host}\.#i, keys %host_sel if $hostkey eq '';
|
|
# if (exists $host_sel{$hostkey}) {
|
|
# $host_sel_items -> value ($host_sel{$hostkey});
|
|
###################
|
|
|
|
# Now search for it
|
|
# $mech -> submit_form (
|
|
# form_name => 'form_data_sources',
|
|
# );
|
|
|
|
# unless ($mech -> success() && $mech->title() =~ m#Console -> Data Sources#) {
|
|
# verbose 0, "Cacti: Failed to search \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
# verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
# return 1;
|
|
# }
|
|
|
|
# verbose 2, "Cacti: Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
# Data source exists?
|
|
# return 0 if $mech->response()->decoded_content !~ m#No Data Sources#;
|
|
# }
|
|
# }
|
|
########################################################
|
|
|
|
verbose 1, "Cacti: Data source not found for host \"$host\" and service \"$cactiserv\". Will create";
|
|
|
|
# Add a new data source
|
|
# $mech->follow_link (text => 'Add');
|
|
$mech -> get ("${cacti_url}/data_sources.php?action=ds_edit&host_id=-1");
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Sources -> \(Edit\)#) {
|
|
verbose 0, "Cacti: Failed to add a new data source by following the \"Add\" link. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Followed the \"Add\" link to add a new data source:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
########################################################
|
|
### We reload the lists because these are OBJECTS specific to the current page of $mech
|
|
# Load a list of data templates
|
|
($data_template_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^data_template_id$/,
|
|
);
|
|
# @@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values;
|
|
|
|
# Locate the needed data template in the list, and select it
|
|
# $servkey = join "", grep m#^$cactiserv$#i, keys %data_template_sel;
|
|
# $data_template_sel_items -> value ($data_template_sel{$servkey});
|
|
$data_template_sel_items -> value ($data_templates{$cactiserv});
|
|
|
|
# Load a list of hosts
|
|
($host_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^host_id$/,
|
|
);
|
|
@@host_sel{$host_sel_items->value_names} = $host_sel_items->possible_values;
|
|
|
|
# Locate the needed host in the list, and select it
|
|
my $hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
$hostkey = join "", grep m# \(${host}\.#i, keys %host_sel if $hostkey eq '';
|
|
$host_sel_items -> value ($host_sel{$hostkey});
|
|
|
|
# Save the new data source with selected host and data template
|
|
$mech -> submit_form;
|
|
|
|
unless ($mech -> success()) {
|
|
verbose 0, "Cacti: Failed to create a new data source by associating a host with data template. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Created a new data source:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
#####################################################################
|
|
|
|
# Need to save another page having a path to the RRD file
|
|
$mech -> submit_form (
|
|
fields => {
|
|
data_source_path => "${rrapath}/${host}_${serv}.rrd",
|
|
},
|
|
);
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save a new data source for template \"$cactiserv\" and host \"$host\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
# $data_source_created{$cactiserv} = [] unless exists $data_source_created{$cactiserv};
|
|
# push @@{$data_source_created{$cactiserv}}, $host;
|
|
|
|
verbose 2, "Cacti: Saved a new data source for template \"$cactiserv\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
verbose 1, "Cacti: Successfully created a new data source for host \"$host\" and service \"$cactiserv\"";
|
|
return 0; # Successful creation of a new data source
|
|
}
|
|
|
|
|
|
# Check if the graph exists, and if not, create
|
|
sub g_check_create {
|
|
my $data = shift;
|
|
my $serv = shift;
|
|
my $host = shift;
|
|
my ($cactiserv, $uniqname);
|
|
|
|
$uniqname = unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
$cactiserv = "${serv}_${uniqname}";
|
|
|
|
my (%graph_template_sel, %host_sel);
|
|
my ($graph_template_sel_items, $host_sel_items, $g_templ_title);
|
|
|
|
my $hst = join "", grep m#^${host}(|\.princeton\.edu)$#i, keys %cacti_hosts; # look up hostname
|
|
#$hst = grep m#^${host}$#i, keys %cacti_hosts if $hst eq ''; # look up description (might be different)
|
|
#return 1 unless exists $cacti_hosts{$hst}; # Host could not be located in Cacti for some reason (unprobable since this was already checked)
|
|
return 1 unless $hst; # Host could not be located in Cacti for some reason (unprobable since this was already checked)
|
|
|
|
my $host_ds_comp = get_cacti_host_ds_components $host;
|
|
|
|
#my @@host_graphs = `$php ${cacti_path}/cli/add_tree.php --list-graphs --host-id=$cacti_hosts{$hst}`;
|
|
my $hst_grphs = get_cacti_graph $host;
|
|
|
|
# Does the graph exist for the host?
|
|
{ lock %{$data->{spec}};
|
|
$g_templ_title = exists $data->{spec}->{graph_title} ? $data->{spec}->{graph_title} : $serv;
|
|
#if (grep m#^\d+\s+\S+\s+\-\s+$g_templ_title\($uniqname\)\s+#i, @@host_graphs) {
|
|
if (exists $hst_grphs->{"${host} - ${g_templ_title}(${uniqname})"}) {
|
|
verbose 1, "Cacti: Graph exists for host \"$host\" and service \"$serv\"";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
verbose 1, "Cacti: Graph for host \"$host\" and service \"$serv\" could not be located. Will create";
|
|
|
|
$mech -> get ("${cacti_url}/graphs.php?action=graph_edit&host_id=-1");
|
|
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Graph Management -> \(Edit\)#) {
|
|
verbose 0, "Cacti: Failed to add a new graph for template \"$cactiserv\" and host \"$host\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Created a new graph for template \"$cactiserv\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
##################################################
|
|
|
|
# Load a list of graph templates
|
|
($graph_template_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^graph_template_id$/,
|
|
);
|
|
# @@graph_template_sel{$graph_template_sel_items->value_names} = $graph_template_sel_items->possible_values;
|
|
|
|
# Locate the needed graph template in the list, and select it
|
|
my ($graphkey, $g_templ_name);
|
|
|
|
{ lock %{$data->{spec}};
|
|
$g_templ_name = $data->{spec}->{graph_template_name} . "_$cactiserv";
|
|
$graphkey = $graph_templates{$g_templ_name};
|
|
#$graphkey = join "", grep m#^${g_templ_name}$#i, keys %graph_template_sel;
|
|
}
|
|
#$graph_template_sel_items -> value ($graph_template_sel{$graphkey});
|
|
$graph_template_sel_items -> value ($graphkey);
|
|
####################
|
|
# Load a list of hosts
|
|
($host_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^host_id$/,
|
|
);
|
|
# @@host_sel{$host_sel_items->value_names} = $host_sel_items->possible_values;
|
|
|
|
# Locate the needed host in the list, and select it
|
|
# my $hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
# $hostkey = join "", grep m# \(${host}\.princeton\.edu\)#i, keys %host_sel if $hostkey eq '';
|
|
# $host_sel_items -> value ($host_sel{$hostkey});
|
|
$host_sel_items -> value ($cacti_hosts{$host});
|
|
|
|
# This is done solely to get the name of the host if the description differs from the hostname
|
|
# $hostkey =~ m#^(\S+)#;
|
|
# my $hostkey_temp = $1;
|
|
|
|
###################
|
|
# Save
|
|
$mech -> submit_form;
|
|
|
|
#unless ($mech -> success() && $mech->response()->decoded_content =~ m#${hostkey_temp} \- ${g_templ_title}\(${uniqname}\)#) {
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#${host} \- ${g_templ_title}\(${uniqname}\)#) {
|
|
verbose 0, "Cacti: Failed to save the new graph for graph template \"${g_templ_title}(${uniqname})\" and host \"$host\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved the new graph for graph template \"${g_templ_title}(${uniqname})\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
##########################################################
|
|
|
|
# Now let's select (map) the relevant data sources to data template items of the graph
|
|
|
|
my $curcont = join "", $mech -> response() -> decoded_content;
|
|
my $ds_sel_items;
|
|
my %ds_sel;
|
|
#<tr id='row_task_item_id_310' bgcolor='#E5E5E5'>
|
|
#<td width='50%'>
|
|
#<font class='textEditTitle'>Data Source [home]</font><br>
|
|
while ($curcont =~ m#<tr id='row_task_item_id_([^']+)[^\n]+\n[^\n]+[^>]+>Data Source \[([^\]]+)\]</font><br>#mg) {
|
|
my ($item_id, $found_ds_name) = ($1, $2);
|
|
|
|
($ds_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^task_item_id_${item_id}$/,
|
|
);
|
|
#@@ds_sel{$ds_sel_items->value_names} = $ds_sel_items->possible_values;
|
|
|
|
#my $dskey = join "", grep m#^${host} \- ${cactiserv} \($found_ds_name\)$#i, keys %ds_sel;
|
|
|
|
#$ds_sel_items -> value ($ds_sel{$dskey});
|
|
$ds_sel_items -> value ($host_ds_comp->{"${host} - ${cactiserv}(${found_ds_name})"});
|
|
}
|
|
|
|
$mech -> submit_form;
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Save Successful#) {
|
|
verbose 0, "Cacti: Failed to save the components of the new graph for graph template \"${g_templ_name}_${cactiserv}\" and host \"$host\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Saved the components of the new graph for graph template \"${g_templ_name}_${cactiserv}\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
verbose 1, "Cacti: Successfully created a graph for host \"$host\"";
|
|
|
|
return 0; # Graph creation successful
|
|
}
|
|
|
|
sub begin {
|
|
# If we are not outputting to STDOUT, then we daemonize
|
|
if ($outtype eq 'log') {
|
|
my $pid;
|
|
use POSIX qw(setsid);
|
|
chdir '/';
|
|
open STDIN, '/dev/null' or die "Cannot read \"/dev/null\"";
|
|
open STDOUT, '>> /dev/null' or die "Cannot write to \"/dev/null\"";
|
|
open STDERR, '>> /dev/null' or die "Cannot write to \"/dev/null\"";
|
|
defined ($pid = fork) or die "Cannot fork: $!";
|
|
exit 0 if $pid;
|
|
|
|
POSIX::setsid or die "Cannot start a new session";
|
|
|
|
$SIG{PIPE} = 'ignore';
|
|
|
|
$pidfile = File::Pid -> new ({file => $pidpath,});
|
|
$pidfile -> write or die "Cannot write PID file: $!";
|
|
}
|
|
|
|
$SIG{HUP} = \&reload; # SIGHUP causes configuration re-read
|
|
$SIG{TERM} = \&end_program; # SIGTERM causes graceful termination
|
|
$SIG{INT} = \&end_program; # CTRL+C causes graceful termination
|
|
|
|
my $descr = load;
|
|
|
|
# Nagios pipe read
|
|
read_perfdata $descr->[0], $descr->[1];
|
|
|
|
$pidfile -> remove if $outtype eq 'log';
|
|
}
|
|
|
|
|
|
# This routine does things that might later be reloaded on request
|
|
sub load {
|
|
$pnag = Thread::Queue -> new;
|
|
$pnagcac = Thread::Queue -> new;
|
|
|
|
if ($outtype eq 'log') {
|
|
open LOG, ">> $log_file" or die "Cannot open logfile \"$log_file\"";
|
|
}
|
|
|
|
verbose 0, "###### Program Started ###########\n";
|
|
|
|
# Load performance history
|
|
open PERS, "< $persistence_file" and do {
|
|
foreach (<PERS>) {
|
|
chomp;
|
|
my ($hst, $svc) = split /::::/;
|
|
$perfhist{$hst} = &share ({}) unless exists $perfhist{$hst};
|
|
$perfhist{$hst}->{$svc} = 2;
|
|
}
|
|
close PERS;
|
|
};
|
|
|
|
read_config;
|
|
|
|
my $rrd_thr = threads -> new (\&update_rrd);
|
|
$rrd_thr -> detach;
|
|
my $cac_thr = threads -> new (\&cacti_update);
|
|
$cac_thr -> detach;
|
|
|
|
return [ $rrd_thr, $cac_thr ];
|
|
}
|
|
|
|
# This routine terminates things that might then be reloaded, or the program can be terminated
|
|
sub unload {
|
|
|
|
lock $waiton and $waiton = 0;
|
|
|
|
# Clear performance history
|
|
foreach (keys %perfhist) {
|
|
delete $perfhist{$_};
|
|
}
|
|
%perfhist = ();
|
|
|
|
verbose 0, "########## Program terminated ##########";
|
|
|
|
close LOG;
|
|
|
|
# This is a way to execute various external tasks before final termination, or reload
|
|
# Things such as cleanup, and log rotation should go in this file
|
|
open POSTTASKS, "< $close_file" and do {
|
|
foreach (<POSTTASKS>) {
|
|
chomp;
|
|
`$_`;
|
|
}
|
|
close POSTTASKS;
|
|
};
|
|
|
|
}
|
|
|
|
sub read_config {
|
|
#sleep 20;
|
|
lock %spec;
|
|
%spec = ();
|
|
|
|
#@@graph_templates = `$php -q ${cacti_path}/cli/add_graphs.php --list-graph-templates`;
|
|
#shift @@graph_templates;
|
|
#map { chomp; s/^\d+\s+//; } @@graph_templates;
|
|
|
|
#my @@cac_hosts = `$php -q ${cacti_path}/cli/add_graphs.php --list-hosts`;
|
|
#foreach (@@cac_hosts) { /^(\d+)\s+(\S+)\s+(.*)/ and do { $cacti_hosts{$2} = $1; $cacti_hosts{$3} = $1; }; }
|
|
|
|
open SPECFILE, "< $spec_file" or graceful_die (1, 1, "Unable to open the service spec file!!!\n");
|
|
my @@speclines = <SPECFILE>;
|
|
close SPECFILE;
|
|
|
|
verbose 1, "---- Reading RRD specification ----";
|
|
|
|
my ($curservreg, $curhostreg, $graphspec, $itemspec, $itemspec2, $createitem, $createitemspec);
|
|
foreach (@@speclines) {
|
|
chomp;
|
|
next if /^\s*(#|$)/;
|
|
|
|
# Beginning of a new spec definition, along with a unique service regex
|
|
/^\s*service_regex\s+(\S+)\s+{\s*$/ and do {
|
|
undef $curservreg;
|
|
undef $curhostreg;
|
|
undef $graphspec;
|
|
($curservreg, $curhostreg) = ($1, '^self^'); # By default we assume that there's a graph for each individual host
|
|
$spec{$curservreg} = &share ({});
|
|
$spec{$curservreg} -> {$curhostreg} = &share ({});
|
|
next;
|
|
};
|
|
|
|
next unless defined $curservreg;
|
|
|
|
# Line that defines a host regex to match for hosts. If this occurs, we remove the previously assumed ^self^
|
|
/^\s*host_regex:\s+(.*)$/ and do {
|
|
delete $spec{$curservreg} -> {$curhostreg};
|
|
$curhostreg = $1;
|
|
$spec{$curservreg} -> {$curhostreg} = {};
|
|
next;
|
|
};
|
|
|
|
# Beginning of a block defining RRD creation
|
|
/^\s*create_items\s*{/ and do {
|
|
$createitem = 1;
|
|
$spec{$curservreg} -> {$curhostreg} -> {create_items} = &share ({});
|
|
next;
|
|
};
|
|
|
|
# Beginning of a block defining Cacti graph template creation
|
|
/^\s*graph_items\s*{/ and do {
|
|
$graphspec = 1;
|
|
$spec{$curservreg} -> {$curhostreg} -> {graph_items} = &share ({});
|
|
next;
|
|
};
|
|
|
|
# Item related to the general Cacti graph template definition
|
|
/^\s*graph_([^:]+):\s+(.*)/ and do {
|
|
my $grphprop = "graph_$1";
|
|
$spec{$curservreg} -> {$curhostreg} -> {$grphprop} = $2;
|
|
next;
|
|
};
|
|
|
|
# Data source item names that need mapping (illegal chars?)
|
|
/^\s*map_items:\s*(.*)/ and do {
|
|
my $itmap = $1;
|
|
$spec{$curservreg} -> {$curhostreg} -> {map_items} = &share ({});
|
|
%{$spec{$curservreg} -> {$curhostreg} -> {map_items}} = split /\s*=>\s*/, $itmap;
|
|
};
|
|
|
|
# Data source items to be ignored (not graphs)
|
|
/^\s*ignore_items:\s*(.*)/ and do {
|
|
$spec{$curservreg} -> {$curhostreg} -> {ignore_items} = &share ([]);
|
|
@@{$spec{$curservreg} -> {$curhostreg} -> {ignore_items}} = split /\s*,\s*/, $1;
|
|
};
|
|
|
|
# Item defining an RRD label to match
|
|
$createitem && /^\s*(\S+)\s+{/ and do {
|
|
$createitemspec = $1;
|
|
$spec{$curservreg} -> {$curhostreg} -> {create_items} -> {$1} = &share ({});
|
|
next;
|
|
};
|
|
|
|
# Item defining a data source to match
|
|
$graphspec && /^\s*(\S+)\s+{/ and do {
|
|
# Different graph items using the matched data source, such as AREA or GPRINT
|
|
$itemspec and do {
|
|
$itemspec2 = $1;
|
|
|
|
push @@{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}}, &share ({});
|
|
$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}[@@{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}} - 1] -> {$itemspec2} = &share ({});
|
|
#print "def itemspec2 => $itemspec2\n";
|
|
next;
|
|
};
|
|
|
|
$itemspec = $1;
|
|
$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec} = &share ([]);
|
|
#print "def itemspec => $itemspec\n";
|
|
next;
|
|
};
|
|
|
|
# Record current RRD label property
|
|
$createitemspec && /^\s*([^:]+):\s*(.*)/ and do {
|
|
$spec{$curservreg} -> {$curhostreg} -> {create_items} -> {$createitemspec} -> {$1} = $2;
|
|
};
|
|
|
|
# Record current Graph item property
|
|
$itemspec2 && /^\s*([^:]+):\s*(.*)/ and do {
|
|
$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}[@@{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}} - 1] -> {$itemspec2} -> {$1} = $2;
|
|
};
|
|
|
|
# End of some block
|
|
/^\s*}/ and do {
|
|
$createitem and do {
|
|
$createitemspec and do {
|
|
undef $createitemspec;
|
|
next;
|
|
};
|
|
undef $createitem;
|
|
};
|
|
$graphspec and do {
|
|
$itemspec and do {
|
|
$itemspec2 and do {
|
|
#print "undef itemspec2 => $itemspec2\n";
|
|
undef $itemspec2;
|
|
next;
|
|
};
|
|
#print "undef itemspec => $itemspec\n";
|
|
undef $itemspec;
|
|
next;
|
|
};
|
|
#print "undef graphspec\n";
|
|
undef $graphspec;
|
|
};
|
|
next;
|
|
};
|
|
|
|
}
|
|
|
|
#print Dumper (\%spec);
|
|
#exit 1;
|
|
|
|
}
|
|
|
|
# The following will continuously read the perfdata Nagios pipe
|
|
sub read_perfdata {
|
|
my ($rrd_thr, $cac_thr) = @@_;
|
|
|
|
#$LASTSERVICECHECK$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICEOUTPUT$\t$SERVICEPERFDATA$
|
|
open PERFDATA, "< $perf_file" or graceful_die (0, 1, "Unable to open the perfdata file \"$perf_file\"!!!");
|
|
|
|
verbose (1, "Performance data pipe \"$perf_file\" successfully opened for reading");
|
|
|
|
# The following should loop forever, since we're reading from a pipe
|
|
# Termination should be done by a signal, such as SIGTERM
|
|
# Each line contains perfdata info for a host/service
|
|
while (<PERFDATA>) {
|
|
chomp;
|
|
verbose (4, "Perfdata line read: #$_#\n");
|
|
|
|
$pnag -> enqueue ("$_");
|
|
|
|
# If we've been commanded to finish
|
|
last if (lock $terminate && $terminate);
|
|
|
|
if (lock $waiton && $waiton) {
|
|
if ($waiton == 1) {
|
|
#$pnag -> insert (0, "Reload");
|
|
#$pnagcac -> insert (0, "Reload");
|
|
$pnag -> enqueue ("Reload");
|
|
$pnagcac -> enqueue ("Reload");
|
|
$waiton = 2;
|
|
}
|
|
if ($rrd_thr -> is_running || $cac_thr -> is_running) { verbose 1, "Waiting for the threads to complete..."; next; }
|
|
unload;
|
|
my $descr = load;
|
|
($rrd_thr, $cac_thr) = @@$descr;
|
|
}
|
|
|
|
}
|
|
|
|
close PERFDATA;
|
|
|
|
verbose (1, "Performance data pipe \"$perf_file\" closed");
|
|
#sleep 60;
|
|
#$pnag -> insert (0, "Terminate");
|
|
#$pnagcac -> insert (0, "Terminate");
|
|
$pnag -> enqueue ("Terminate");
|
|
$pnagcac -> enqueue ("Terminate");
|
|
|
|
# If one of the threads is still running, just display a message
|
|
while ($rrd_thr -> is_running || $cac_thr -> is_running) { verbose 1, "Waiting for the threads to complete..."; sleep 3; }
|
|
|
|
unload;
|
|
}
|
|
|
|
sub update_rrd {
|
|
my %perfdata;
|
|
my $rrd_thr_wait = 0;
|
|
|
|
verbose (1, "------ Update RRD thread started ------");
|
|
|
|
while ( $_ = $pnag -> dequeue ) {
|
|
verbose (4, "Update RRD dequeued: ##$_##");
|
|
|
|
last if /^(Terminate|Reload)$/;
|
|
|
|
my ($tm, $hst, $svc, $svcout, $svcperf) = split /\t/;
|
|
|
|
next unless defined $tm && defined $hst && defined $svc && defined $svcout && defined $svcperf;
|
|
#$svcperf = $svcout if not defined $svcperf || $svcperf eq ''; ##### need to put in possibility of regex parsing the output if needed. This will require some regex matching in create_ spec
|
|
|
|
verbose (3, "Perfdata line parsed: ####$_#####$tm###$svc###$hst###$svcout###$svcperf###");
|
|
|
|
# This skips perfdata lines not matching the spec - don't care about these
|
|
lock %spec;
|
|
next if $svcperf =~ /^\s*$/
|
|
|| ! grep ($svc =~ m#$_#i, keys %spec)
|
|
|| ! grep { my $svtmp = $_; lock %{$spec{$svtmp}}; $svc =~ m#$svtmp#i and do {
|
|
grep { $hst =~ m#$_#i || $_ eq '^self^' } keys %{$spec{$svtmp}}
|
|
};
|
|
} keys %spec;
|
|
$svc =~ s/\s+/_/g;
|
|
|
|
# change to 2 later!!!
|
|
verbose (1, "Perfdata line matching spec: ###$svc###$hst###$svcout###$svcperf###; Queue size: " . $pnag->pending);
|
|
|
|
$perfdata{$hst} = {} unless exists $perfdata{$hst};
|
|
$perfdata{$hst}->{$svc} = {} unless exists $perfdata{$hst}->{$svc};
|
|
my $curperf = $perfdata{$hst}->{$svc};
|
|
$curperf->{perf} = {};
|
|
$curperf->{time} = $tm;
|
|
|
|
# Each perfdata line might carry perf output (separated by space) for several items that we need to parse
|
|
# 'label'=value[UOM];[warn];[crit];[min];[max] -- per rrdtool docs, label should be [a-zA-Z0-9_]+, 1-19chars long
|
|
foreach my $nextperf (split /\s+/, $svcperf) {
|
|
#print "#####$nextperf####\n";
|
|
my ($lbl, $rest) = split /=/, $nextperf;
|
|
next unless defined $rest;
|
|
|
|
$lbl =~ s/'//g; # We don't need the single quotes anyhow
|
|
my $graphlabel = $lbl;
|
|
$lbl =~ s/[^a-zA-Z0-9_]//g;
|
|
$lbl = substr $lbl, 0, 19;
|
|
my ($val, $wrn, $crt, $min, $max) = split /;/, $rest;
|
|
$val =~ s/[^0-9\.\-]//g;
|
|
|
|
$curperf->{perf}->{$lbl} = [];
|
|
push @@{$curperf->{perf}->{$lbl}}, $val, $wrn, $crt, $min, $max, $graphlabel;
|
|
|
|
# change to 2 later!!!
|
|
verbose (2, "Perfdata deciphered: ###$val###$graphlabel###");
|
|
}
|
|
|
|
# Link the relevant part of the spec hash to the %perfdata for further use
|
|
# For every service specification, remove ignored items, and map items to map
|
|
#lock %spec;
|
|
foreach my $svcspec (keys %spec) {
|
|
next unless $svc =~ m#${svcspec}#;
|
|
lock %{$spec{$svcspec}};
|
|
|
|
# For every host specification
|
|
foreach my $hostspec (keys %{$spec{$svcspec}}) {
|
|
next unless ($hst =~ /^${hostspec}$/i || $hostspec eq '^self^');
|
|
|
|
lock %{$spec{$svcspec}->{$hostspec}};
|
|
my $curspec :shared = $spec{$svcspec}->{$hostspec};
|
|
$curperf->{spec} = $curspec;
|
|
|
|
# Remove perfdata DS's from the ignore map
|
|
exists $curspec->{ignore_items} and do {
|
|
lock @@{$curspec->{ignore_items}};
|
|
foreach (@@{$curspec->{ignore_items}}) {
|
|
next unless exists $curperf->{perf}->{$_};
|
|
#print Dumper ($curperf->{perf}->{$_});
|
|
delete $curperf->{perf}->{$_};
|
|
}
|
|
};
|
|
|
|
# Map DS names if needed
|
|
exists $curspec->{map_items} and do {
|
|
lock %{$curspec->{map_items}};
|
|
foreach (keys %{$curspec->{map_items}}) {
|
|
next unless exists $curperf->{perf}->{$_};
|
|
$curperf->{perf}->{$curspec->{map_items}->{$_}} = $curperf->{perf}->{$_};
|
|
delete $curperf->{perf}->{$_};
|
|
}
|
|
};
|
|
|
|
# This service is probably useless, since it yields no stats that we want to graph according to the spec
|
|
if (keys %{$curperf->{perf}} == 0) {
|
|
delete $curperf->{perf};
|
|
delete $perfdata{$hst}->{$svc};
|
|
delete $perfdata{$hst} unless keys %{$perfdata{$hst}} > 0;
|
|
}
|
|
|
|
#/maestro=2196MB;2406;2706;0;3007
|
|
#/cognos/iowa=68% /cognos/ohio=53%
|
|
|
|
#/usr/local/groundwork/common/bin/rrdtool create \
|
|
#/var/local/cacti-0.8.7h/rra/sdpadfs300w_avgdiskreadpersec_9866.rrd \
|
|
#--step 300 \
|
|
#DS:AvgDiskWriteQueueLe:GAUGE:600:0:100 \
|
|
#DS:AvgDiskReadQueueLen:GAUGE:600:0:100 \
|
|
#DS:PercentFreeSpace:GAUGE:600:0:100 \
|
|
#DS:PercentDiskWriteTim:GAUGE:600:0:100 \
|
|
#DS:PercentDiskReadTime:GAUGE:600:0:100 \
|
|
#DS:AvgDiskReadPerSec:GAUGE:600:0:U \
|
|
#DS:AvgDiskWritePerSec:GAUGE:600:0:U \
|
|
#DS:FreeMegabytes:GAUGE:600:0:U \
|
|
#RRA:AVERAGE:0.5:1:500 \
|
|
#RRA:AVERAGE:0.5:1:600 \
|
|
#RRA:AVERAGE:0.5:6:700 \
|
|
#RRA:AVERAGE:0.5:24:775 \
|
|
#RRA:AVERAGE:0.5:288:797 \
|
|
#RRA:MAX:0.5:1:500 \
|
|
#RRA:MAX:0.5:1:600 \
|
|
#RRA:MAX:0.5:6:700 \
|
|
#RRA:MAX:0.5:24:775 \
|
|
#RRA:MAX:0.5:288:797 \
|
|
#
|
|
|
|
# rrdcreate?
|
|
# rrdtool create filename [--start|-b start time] [--step|-s step] [--no-overwrite] [DS:ds-name:DST:dst arguments] [RRA:CF:cf arguments]
|
|
# If the RRD data file for the current data set does not exist, create it
|
|
if (! -f "${rrapath}/${hst}_${svc}.rrd") {
|
|
#print "###", keys %{$perfdata{$hst}->{$svc}}, "###\n";
|
|
lock %{$curspec->{create_items}};
|
|
my @@create_item_regexs = keys %{$curspec->{create_items}};
|
|
my $ds_create_spec = '';
|
|
|
|
foreach my $dsitem (keys %{$curperf->{perf}}) {
|
|
my $create_item_spec_match = join "", grep { $dsitem =~ /^$_$/i } @@create_item_regexs;
|
|
lock %{$curspec->{create_items}->{$create_item_spec_match}};
|
|
$ds_create_spec .= ($create_item_spec_match eq '') ?
|
|
"DS:$dsitem:$curspec->{create_items}->{$create_item_spec_match}->{type}:600:$curspec->{create_items}->{$create_item_spec_match}->{min}:$curspec->{create_items}->{$create_item_spec_match}->{max} " :
|
|
"DS:$dsitem:GAUGE:600:0:U ";
|
|
}
|
|
#print "####$ds_create_spec####\n";
|
|
chomp $ds_create_spec;
|
|
my @@rrdout = `$rrdtool create ${rrapath}/${hst}_${svc}.rrd --step 300 $ds_create_spec $rracacspec 2>&1`;
|
|
verbose (1, "Create RRD: $rrdtool create ${rrapath}/${hst}_${svc}.rrd --step 300 $ds_create_spec $rracacspec\nResponse: " . join ("\n", @@rrdout));
|
|
}
|
|
|
|
# rrdupdate
|
|
# rrdtool {update | updatev} filename [--template|-t ds-name[:ds-name]...] [--daemon address] [--] N|timestamp:value[:value...] at-timestamp@@value[:value...] [timestamp:value[:value...] ...]
|
|
my $valsupdate = join ":", (map { $curperf->{perf}->{$_}->[0]; } keys %{$curperf->{perf}});
|
|
my @@rrdout = `$rrdtool update ${rrapath}/${hst}_${svc}.rrd $curperf->{time}:$valsupdate 2>&1`;
|
|
verbose (1, "Update RRD: $rrdtool update ${rrapath}/${hst}_${svc}.rrd $curperf->{time}:$valsupdate\nResponse: " . join ("\n", @@rrdout));
|
|
|
|
#next;
|
|
# Record processed data
|
|
# 0 - initial unprocessed, 1 - error, try again, 2 - successfully processed previously
|
|
lock %perfhist;
|
|
$perfhist{$hst} = &share ({}) unless exists $perfhist{$hst};
|
|
lock %{$perfhist{$hst}};
|
|
unless (exists $perfhist{$hst}->{$svc} && $perfhist{$hst}->{$svc} != 1) {
|
|
$perfhist{$hst}->{$svc} = 0; # Initially mark it as unprocessed by Cacti
|
|
$pnagcac -> enqueue ("Next service");
|
|
$pnagcac -> enqueue ("Host: ${hst}");
|
|
$pnagcac -> enqueue ("Service: ${svc}");
|
|
$pnagcac -> enqueue ("Host Spec: ${hostspec}");
|
|
$pnagcac -> enqueue ("Service Spec: ${svcspec}");
|
|
foreach my $lbl (keys %{$curperf->{perf}}) {
|
|
$pnagcac -> enqueue ("Perf: $lbl");
|
|
$pnagcac -> enqueue (join "::: ", grep { defined $_ ? $_ : ""; } @@{$curperf->{perf}->{$lbl}});
|
|
}
|
|
$pnagcac -> enqueue ("End service");
|
|
}
|
|
|
|
} # End foreach hostspec
|
|
} # End foreach svcspec
|
|
|
|
} # End while (1)
|
|
|
|
verbose 1, "Pending queue items: " . $pnag->pending();
|
|
|
|
verbose (1, "------ Update RRD thread terminated ------");
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub verbose {
|
|
my $level = shift;
|
|
my $string = shift;
|
|
|
|
# print STDERR "### ", $string, "\n" if $verbose && $verbose >= $level;
|
|
if ($outtype eq 'log') {
|
|
my $dt = `date +"%a %b %d %T"`; chomp $dt;
|
|
print LOG "$dt => $string", "\n" if $verbose && $verbose >= $level;
|
|
} elsif ($outtype eq 'stderr') {
|
|
print STDERR "### ", $string, "\n" if $verbose && $verbose >= $level;
|
|
}
|
|
}
|
|
|
|
sub graceful_die {
|
|
my $immediate = shift;
|
|
my $state = shift;
|
|
my $str = shift;
|
|
verbose 0, $str if $str && $str ne '';
|
|
|
|
if ($immediate) {
|
|
if ($outtype eq 'log') {
|
|
close LOG;
|
|
$pidfile -> remove;
|
|
}
|
|
exit $state;
|
|
} else {
|
|
lock $terminate;
|
|
$terminate = 1;
|
|
}
|
|
}
|
|
|
|
sub reload {
|
|
verbose (0, "SIGHUP caught. Reloading configuration");
|
|
lock $waiton;
|
|
$waiton = 1 unless $waiton; # If waiton is already non-zero - we're in the process of reloading, so DND
|
|
}
|
|
|
|
sub end_program {
|
|
verbose (0, "Program terminate requested by user");
|
|
lock $terminate;
|
|
$terminate = 1;
|
|
}
|
|
|
|
# Args: cols, tables, conditions
|
|
sub DB_get {
|
|
my $colnames_p = shift;
|
|
my @@colnames = split /\s*,\s*/, $colnames_p;
|
|
|
|
my $tblnames_p = shift;
|
|
my @@tblnames = split /\s*,\s*/, $tblnames_p;
|
|
|
|
my $cond_p = shift;
|
|
|
|
my %res;
|
|
|
|
my $query = "SELECT $colnames_p FROM $tblnames_p";
|
|
$query .= " WHERE $cond_p" if $cond_p;
|
|
verbose 1, "###$query###";
|
|
my $sth = $cacdb -> prepare ($query);
|
|
graceful_die 1, 1, "Error initializing DB query \"$query\". Received: " . $cacdb->errstr unless $sth;
|
|
#verbose 0, "\n###$query prepared\n";
|
|
my $ref;
|
|
graceful_die 1, 1, "Error executing DB query \"$query\". Received: " . $cacdb->errstr unless $sth -> execute;
|
|
#verbose 0, "\n###$query executed\n";
|
|
|
|
while ($ref = $sth->fetchrow_hashref()) {
|
|
#verbose 0, (Dumper ($ref));
|
|
#verbose 0, "#########Next row\n";
|
|
foreach (@@colnames) {
|
|
s#.*\.##;
|
|
#verbose 0, "#########$_##\n";
|
|
# $res{"$_"} = [] unless exists $res{"$_"};
|
|
# push @@{$res{"$_"}}, $ref -> {"$_"};
|
|
push @@{$res{$_}||=[]}, $ref->{$_};
|
|
#verbose 0, "####" . $ref -> {"$_"} . "####\n";
|
|
}
|
|
}
|
|
#verbose 0, Dumper (\%res);
|
|
#verbose 0, "#######Done\n";
|
|
$sth -> finish;
|
|
|
|
return \%res;
|
|
}
|
|
|
|
sub get_cacti_data_templates {
|
|
my $res = DB_get ("id, name", "data_template");
|
|
return undef unless keys %$res > 0;
|
|
|
|
my %res2;
|
|
#print Dumper ($res);
|
|
for (my $ctr = 0; $ctr < @@{$res->{name}}; $ctr++) {
|
|
$res2{$res->{name}->[$ctr]} = $res->{id}->[$ctr];
|
|
}
|
|
|
|
return \%res2;
|
|
}
|
|
|
|
sub get_cacti_data_template_component_id {
|
|
my $templ_name = shift;
|
|
my $comp_name = shift;
|
|
|
|
#select data_template_rrd.id, data_template.name, data_template_rrd.data_source_name from data_template,data_template_data,data_template_rrd where data_template_rrd.data_template_id=data_template.id and data_template_data.data_template_id=data_template.id and data_template_data.local_data_id=0 and data_template_rrd.local_data_id=0;
|
|
|
|
my $res = DB_get ("data_template_rrd.id", "data_template, data_template_data, data_template_rrd", "data_template_rrd.data_template_id=data_template.id AND data_template_data.data_template_id=data_template.id AND data_template_data.local_data_id='0' AND data_template_rrd.local_data_id='0' AND data_template.name='" . $templ_name . "' AND data_template_rrd.data_source_name='" . $comp_name . "'");
|
|
|
|
return keys %$res == 1 ? $res -> {id} : undef;
|
|
}
|
|
|
|
#sub get_cacti_data_sources {
|
|
# my $res = DB_get ("id, name", "graph_templates");
|
|
# return undef unless keys %$res > 0;
|
|
|
|
# my %res2;
|
|
#print Dumper ($res);
|
|
# for (my $ctr = 0; $ctr < @@{$res->{name}}; $ctr++) {
|
|
# $res2{$res->{name}->[$ctr]} = $res->{id}->[$ctr];
|
|
# }
|
|
|
|
# return \%res2;
|
|
#}
|
|
|
|
sub get_cacti_host_template_ds {
|
|
my $host_name = shift;
|
|
my $templ_name = shift;
|
|
|
|
my $res = DB_get ("data_template_data.local_data_id", "data_local, data_template_data", "data_local.id=data_template_data.local_data_id AND data_local.host_id='" . $cacti_hosts{$host_name} . "' AND data_template_data.data_template_id='" . $data_templates{$templ_name} . "'");
|
|
|
|
return keys %$res == 1 ? $res->{local_data_id} : undef;
|
|
}
|
|
|
|
sub get_cacti_host_ds_components {
|
|
my $host_name = shift;
|
|
|
|
my $res = DB_get ("data_template_rrd.id, data_source_name, name_cache", "data_template_data, data_template_rrd, data_local", "host_id='" . $cacti_hosts{$host_name} . "' and data_template_rrd.local_data_id=data_local.id and data_local.id=data_template_data.local_data_id");
|
|
return undef unless keys %$res > 0;
|
|
|
|
my %res2;
|
|
#print Dumper ($res);
|
|
for (my $ctr = 0; $ctr < @@{$res->{id}}; $ctr++) {
|
|
$res2{"$res->{name_cache}->[$ctr]" . "($res->{data_source_name}->[$ctr])"} = $res->{id}->[$ctr];
|
|
}
|
|
|
|
return \%res2;
|
|
}
|
|
|
|
sub get_cacti_graph_templates {
|
|
my $res = DB_get ("id, name", "graph_templates");
|
|
return undef unless keys %$res > 0;
|
|
|
|
my %res2;
|
|
#print Dumper ($res);
|
|
for (my $ctr = 0; $ctr < @@{$res->{name}}; $ctr++) {
|
|
$res2{$res->{name}->[$ctr]} = $res->{id}->[$ctr];
|
|
}
|
|
|
|
return \%res2;
|
|
}
|
|
|
|
sub get_cacti_graph_template_component {
|
|
my $templ_name = shift;
|
|
|
|
my $res = DB_get ("name", "graph_template_input", "graph_template_id='" . $graph_templates{$templ_name} . "'");
|
|
return keys %$res == 0 ? undef : $res->{name}->[0];
|
|
}
|
|
|
|
sub get_cacti_graph {
|
|
my $host_name = shift;
|
|
|
|
my $res = DB_get ("graph_templates_graph.local_graph_id, graph_templates_graph.title_cache", "graph_local, graph_templates_graph", "graph_local.id=graph_templates_graph.local_graph_id AND graph_local.host_id='" . $cacti_hosts{$host_name} . "'");
|
|
|
|
my %res2;
|
|
#print Dumper ($res);
|
|
for (my $ctr = 0; $ctr < @@{$res->{local_graph_id}}; $ctr++) {
|
|
$res2{$res->{title_cache}->[$ctr]} = $res->{local_graph_id}->[$ctr];
|
|
}
|
|
|
|
return \%res2;
|
|
}
|
|
|
|
sub get_cacti_hosts {
|
|
my $res = DB_get ("id, description, hostname", "host");
|
|
return undef unless keys %$res > 0;
|
|
|
|
my %res2;
|
|
#print Dumper ($res);
|
|
for (my $ctr = 0; $ctr < @@{$res->{id}}; $ctr++) {
|
|
$res2{$res->{hostname}->[$ctr]} = $res->{id}->[$ctr];
|
|
$res2{$res->{description}->[$ctr]} = $res->{id}->[$ctr];
|
|
}
|
|
|
|
return \%res2;
|
|
}
|
|
|
|
@
|
|
|
|
|
|
1.4
|
|
log
|
|
@Working, good version. Threads, log rotation, signal response
|
|
@
|
|
text
|
|
@d7 1
|
|
a7 1
|
|
# $Header: /usr/local/nagios/libexec/eventhandlers/RCS/cacti_update,v 1.3 2013/02/13 20:01:55 nagios Exp nagios $
|
|
d150 1
|
|
a150 1
|
|
while ( $_ = $pnagcac -> dequeue ) {
|
|
d160 1
|
|
d488 1
|
|
a488 1
|
|
# For each graph component of relevant ds item
|
|
d563 1
|
|
a563 1
|
|
my $gritem = join "", keys %{$curgritem};
|
|
d590 1
|
|
a590 1
|
|
$text = $curgritem -> {$gritem} -> {text} =~ m#\^item\^$# ? $data->{perf}->{$curds}->[5] : $curgritem -> {$gritem} -> {text};
|
|
a940 3
|
|
$pnag = Thread::Queue -> new;
|
|
$pnagcac = Thread::Queue -> new;
|
|
|
|
d950 1
|
|
a950 1
|
|
$pidfile -> remove;
|
|
d956 3
|
|
d1181 7
|
|
a1187 2
|
|
$pnag -> enqueue ("Reload") if $waiton == 1;
|
|
$waiton = 2;
|
|
d1200 2
|
|
d1203 1
|
|
d1217 1
|
|
a1217 1
|
|
LK: while ( $_ = $pnag -> dequeue ) {
|
|
d1220 1
|
|
a1220 4
|
|
/^(Terminate|Reload)$/ and do {
|
|
$pnagcac -> enqueue ("$1");
|
|
last LK;
|
|
};
|
|
d1240 1
|
|
a1240 1
|
|
verbose (1, "Perfdata line matching spec: ###$svc###$hst###$svcout###$svcperf###");
|
|
a1384 19
|
|
|
|
# foreach my $hsthist (keys %perfhist) {
|
|
# foreach my $svchist (keys %{$perfhist{$hsthist}}) {
|
|
# $perfhist{$hsthist}->{$svchist} = 1 unless $perfhist{$hsthist}->{$svchist};
|
|
# }
|
|
# }
|
|
# }
|
|
# }
|
|
|
|
# If thread is done with success (0), mark the services in progress as done
|
|
#lock $thread_stat;
|
|
#if ($thread_stat == 0) {
|
|
# $thread_stat = 3; # Thread is not currently running
|
|
# foreach my $hsthist (keys %perfhist) {
|
|
# foreach my $svchist (keys %{$perfhist{$hsthist}}) {
|
|
# $perfhist{$hsthist}->{$svchist} = 2 if $perfhist{$hsthist}->{$svchist} == 1;
|
|
# }
|
|
# }
|
|
#}
|
|
a1387 5
|
|
# Remove old data
|
|
#foreach (keys %{$curperf->{perf}}) {
|
|
# delete $curperf->{perf}->{$_};
|
|
#}
|
|
#delete $curperf->{perf};
|
|
d1390 2
|
|
@
|
|
|
|
|
|
1.3
|
|
log
|
|
@Working version, mostly rewritten
|
|
@
|
|
text
|
|
@d7 1
|
|
a7 1
|
|
# $Header$
|
|
d22 2
|
|
a23 1
|
|
#use File::Pid;
|
|
d27 1
|
|
a27 1
|
|
my $mech;
|
|
d33 1
|
|
d35 1
|
|
d38 2
|
|
a39 1
|
|
my $pidpath = "/var/run/" . $myname . ".pid";
|
|
d53 1
|
|
d55 1
|
|
a55 1
|
|
my @@graph_templates;
|
|
d59 3
|
|
a61 2
|
|
my $pnag :shared;
|
|
my $pnagcac :shared;
|
|
d63 1
|
|
a63 1
|
|
my @@data_templates_created;
|
|
d67 3
|
|
d84 11
|
|
d112 36
|
|
d151 1
|
|
a151 1
|
|
last if /Terminate/;
|
|
a158 1
|
|
return 0 if /Terminate/;
|
|
d186 1
|
|
a186 3
|
|
#lock $terminate;
|
|
#last if $terminate == 1;
|
|
#next;
|
|
a216 3
|
|
|
|
lock $terminate;
|
|
last if $terminate == 1;
|
|
d221 3
|
|
d239 4
|
|
a242 1
|
|
return 0 if grep m#^$cactiserv$#i, @@data_templates_created;
|
|
d245 1
|
|
a245 1
|
|
$mech->get ("${cacti_url}/data_templates.php");
|
|
d247 5
|
|
a251 5
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Templates#) {
|
|
verbose 0, "Cacti: Failed to fetch \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
d253 1
|
|
a253 1
|
|
verbose 2, "Cacti: Query of ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d257 12
|
|
a268 6
|
|
$mech->submit_form(
|
|
form_name => 'form_data_template',
|
|
fields => {
|
|
filter => $cactiserv
|
|
},
|
|
);
|
|
d270 1
|
|
a270 7
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Templates#) {
|
|
verbose 0, "Cacti: Failed to submit to \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Submit to ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d274 5
|
|
a278 5
|
|
if (grep m#<a class='linkEditMain' href='data_templates.php.*>${cactiserv}</span>#, $mech->response()->decoded_content) {
|
|
verbose (1, "Cacti: Data Template already exists for \"$cactiserv\"");
|
|
return 0;
|
|
}
|
|
verbose (1, "Cacti: Data Template not found for \"$cactiserv\" - will create");
|
|
d282 3
|
|
a284 1
|
|
$mech->follow_link (text => 'Add');
|
|
d377 3
|
|
a379 1
|
|
push @@data_templates_created, $cactiserv;
|
|
d397 2
|
|
a398 1
|
|
return 0 if grep /^$data->{spec}->{graph_template_name}_${cactiserv}$/i, @@graph_templates;
|
|
d506 1
|
|
a506 1
|
|
@@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values if keys %data_template_sel == 0;
|
|
d512 4
|
|
a515 3
|
|
my $keytemp = join "", grep m#^${cactiserv}\s+\-\s+\(${curds}\)$#i, keys %data_template_sel;
|
|
|
|
if ($keytemp eq "") {
|
|
d519 2
|
|
a520 1
|
|
#print "\n###$keytemp###\n";
|
|
d523 2
|
|
a524 1
|
|
$data_template_sel_items -> value ($data_template_sel{$keytemp});
|
|
d589 1
|
|
a589 1
|
|
$text = $curgritem -> {$gritem} -> {text} =~ m#^\^item\^$# ? $data->{perf}->{$curds}->[5] : $curgritem -> {$gritem} -> {text};
|
|
d628 6
|
|
a633 3
|
|
@@graph_templates = `$php -q ${cacti_path}/cli/add_graphs.php --list-graph-templates`;
|
|
shift @@graph_templates;
|
|
map { chomp; s/^\d+\s+//; } @@graph_templates;
|
|
d645 6
|
|
d652 1
|
|
a652 1
|
|
return 0 if exists $data_source_created{$cactiserv} && grep m#^$host$#i, @@{$data_source_created{$cactiserv}};
|
|
d656 1
|
|
a656 1
|
|
$mech -> get ("${cacti_url}/data_sources.php");
|
|
d658 5
|
|
a662 5
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Sources#) {
|
|
verbose 0, "Cacti: Failed to fetch \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
d664 1
|
|
a664 1
|
|
verbose 2, "Cacti: Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d670 5
|
|
a674 5
|
|
($data_template_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^template_id$/,
|
|
);
|
|
@@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values;
|
|
d677 4
|
|
a680 4
|
|
my $servkey = join "", grep m#^$cactiserv$#i, keys %data_template_sel;
|
|
my $hostkey;
|
|
if (exists $data_template_sel{$servkey}) { # No hosts with the template???
|
|
$data_template_sel_items -> value ($data_template_sel{$servkey});
|
|
d683 5
|
|
a687 5
|
|
($host_sel_items) = $mech -> find_all_inputs (
|
|
type => 'option',
|
|
name_regex => qr/^host_id$/,
|
|
);
|
|
@@host_sel{$host_sel_items->value_names} = $host_sel_items->possible_values;
|
|
d690 4
|
|
a693 4
|
|
$hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
$hostkey = join "", grep m# \(${host}\.#i, keys %host_sel if $hostkey eq '';
|
|
if (exists $host_sel{$hostkey}) {
|
|
$host_sel_items -> value ($host_sel{$hostkey});
|
|
d697 9
|
|
a705 3
|
|
$mech -> submit_form (
|
|
form_name => 'form_data_sources',
|
|
);
|
|
d707 1
|
|
a707 7
|
|
unless ($mech -> success() && $mech->title() =~ m#Console -> Data Sources#) {
|
|
verbose 0, "Cacti: Failed to search \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
return 1;
|
|
}
|
|
|
|
verbose 2, "Cacti: Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d709 3
|
|
a711 3
|
|
return 0 if $mech->response()->decoded_content !~ m#No Data Sources#;
|
|
}
|
|
}
|
|
d717 2
|
|
a718 1
|
|
$mech->follow_link (text => 'Add');
|
|
d734 1
|
|
a734 1
|
|
@@data_template_sel{$data_template_sel_items->value_names} = $data_template_sel_items->possible_values;
|
|
d737 3
|
|
a739 2
|
|
$servkey = join "", grep m#^$cactiserv$#i, keys %data_template_sel;
|
|
$data_template_sel_items -> value ($data_template_sel{$servkey});
|
|
d749 1
|
|
a749 1
|
|
$hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
d778 2
|
|
a779 2
|
|
$data_source_created{$cactiserv} = [] unless exists $data_source_created{$cactiserv};
|
|
push @@{$data_source_created{$cactiserv}}, $host;
|
|
d803 7
|
|
a809 2
|
|
return 1 unless exists $cacti_hosts{$hst}; # Host could not be located in Cacti for some reason (unprobable since this was already checked)
|
|
my @@host_graphs = `$php ${cacti_path}/cli/add_tree.php --list-graphs --host-id=$cacti_hosts{$hst}`;
|
|
d814 2
|
|
a815 1
|
|
if (grep m#^\d+\s+\S+\s+\-\s+$g_templ_title\($uniqname\)\s+#i, @@host_graphs) {
|
|
d839 1
|
|
a839 1
|
|
@@graph_template_sel{$graph_template_sel_items->value_names} = $graph_template_sel_items->possible_values;
|
|
a844 1
|
|
$graphkey = join "", grep m#^$data->{spec}->{graph_template_name}_${cactiserv}$#i, keys %graph_template_sel;
|
|
d846 2
|
|
d849 2
|
|
a850 1
|
|
$graph_template_sel_items -> value ($graph_template_sel{$graphkey});
|
|
d857 1
|
|
a857 1
|
|
@@host_sel{$host_sel_items->value_names} = $host_sel_items->possible_values;
|
|
d860 4
|
|
a863 3
|
|
my $hostkey = join "", grep m#^$host #i, keys %host_sel;
|
|
$hostkey = join "", grep m# \(${host}\.princeton\.edu\)#i, keys %host_sel if $hostkey eq '';
|
|
$host_sel_items -> value ($host_sel{$hostkey});
|
|
d866 2
|
|
a867 2
|
|
$hostkey =~ m#^(\S+)#;
|
|
my $hostkey_temp = $1;
|
|
d873 2
|
|
a874 1
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#${hostkey_temp} \- ${g_templ_title}\(${uniqname}\)#) {
|
|
d898 1
|
|
a898 1
|
|
@@ds_sel{$ds_sel_items->value_names} = $ds_sel_items->possible_values;
|
|
d900 4
|
|
a903 2
|
|
my $dskey = join "", grep m#^${host} \- ${cactiserv} \($found_ds_name\)$#i, keys %ds_sel;
|
|
$ds_sel_items -> value ($ds_sel{$dskey});
|
|
d936 2
|
|
a937 4
|
|
open LOG, ">> $log_file" or die "Cannot open logfile \"$log_file\"";
|
|
|
|
#$pidfile = File::Pid -> new ({file => $pidpath,});
|
|
#$pidfile -> write or die "Cannot write PID file: $!";
|
|
a939 10
|
|
open PERS, "< $persistence_file" and do {
|
|
foreach (<PERS>) {
|
|
chomp;
|
|
my ($hst, $svc) = split /::::/;
|
|
$perfhist{$hst} = &share ({}) unless exists $perfhist{$hst};
|
|
$perfhist{$hst}->{$svc} = 2;
|
|
}
|
|
close PERS;
|
|
};
|
|
|
|
d943 1
|
|
a943 1
|
|
$SIG{HUP} = \&read_config; # SIGHUP causes configuration re-read
|
|
d947 1
|
|
a947 1
|
|
verbose 0, "###### Program Started ###########\n";
|
|
d949 2
|
|
a950 3
|
|
verbose 1, "Trying to log to Cacti...";
|
|
|
|
$mech = WWW::Mechanize->new(agent => 'Mozilla/5.0', stack_depth => '2', noproxy => '1');
|
|
d952 2
|
|
a953 1
|
|
$mech->credentials($cacti_user, $cacti_pass);
|
|
a954 2
|
|
## Get initial Cacti login page
|
|
$mech->get($cacti_url);
|
|
d956 4
|
|
a959 4
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Console#) {
|
|
verbose 0, "Failed to login. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
graceful_die 1, 1;
|
|
d962 12
|
|
a973 2
|
|
verbose 1, "Login to Cacti successful\n";
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content . "\n\n##########################";
|
|
a974 1
|
|
# We read config initially. Later it can be done using SIGHUP
|
|
d982 17
|
|
a998 2
|
|
# Nagios pipe read
|
|
read_perfdata $rrd_thr, $cac_thr;
|
|
d1000 9
|
|
a1008 2
|
|
# Be clean and log the **** out
|
|
$mech->get ("${cacti_url}/logout.php");
|
|
a1009 1
|
|
graceful_die 1, 0, "########## Program terminated ##########";
|
|
d1013 1
|
|
d1017 3
|
|
a1019 3
|
|
@@graph_templates = `$php -q ${cacti_path}/cli/add_graphs.php --list-graph-templates`;
|
|
shift @@graph_templates;
|
|
map { chomp; s/^\d+\s+//; } @@graph_templates;
|
|
d1021 2
|
|
a1022 2
|
|
my @@cac_hosts = `$php -q ${cacti_path}/cli/add_graphs.php --list-hosts`;
|
|
foreach (@@cac_hosts) { /^(\d+)\s+(\S+)\s+(.*)/ and do { $cacti_hosts{$2} = $1; $cacti_hosts{$3} = $1; }; }
|
|
a1173 1
|
|
lock $pnag;
|
|
d1177 9
|
|
a1185 7
|
|
if (lock $terminate && $terminate) {
|
|
# If one of the threads is still running, just display a message
|
|
if ($rrd_thr -> is_running || $cac_thr -> is_running) {
|
|
verbose 1, "Waiting for the threads to complete...";
|
|
} else { # Otherwise finish
|
|
last;
|
|
}
|
|
d1187 1
|
|
d1193 7
|
|
d1204 1
|
|
d1208 8
|
|
a1215 1
|
|
while ( $_ = $pnag -> dequeue ) {
|
|
d1249 1
|
|
a1366 1
|
|
lock $pnagcac;
|
|
a1405 7
|
|
|
|
lock $terminate;
|
|
if ($terminate) {
|
|
lock $pnagcac;
|
|
$pnagcac -> enqueue ("Terminate");
|
|
last;
|
|
}
|
|
d1435 1
|
|
a1435 1
|
|
#$pidfile -> remove;
|
|
d1444 6
|
|
d1456 150
|
|
@
|
|
|
|
|
|
1.2
|
|
log
|
|
@Mostly working
|
|
@
|
|
text
|
|
@d7 1
|
|
a7 1
|
|
# $HEADER$
|
|
d10 2
|
|
a11 1
|
|
use lib qw(/usr/local/perl/modules/lib/perl5/site_perl/5.8.8 /usr/local/perl/modules/lib/perl5/5.8.8 /usr/local/perl/modules/lib64/perl5/site_perl/5.8.8 /usr/local/perl/modules/lib64/perl5/5.8.8 /var/local/groundwork/perl/lib/site_perl/5.8.8/);
|
|
d13 1
|
|
d18 5
|
|
a22 2
|
|
use forks;
|
|
use forks::shared;
|
|
d25 2
|
|
a26 2
|
|
my $mech = WWW::Mechanize->new(agent => 'Mozilla/5.0', stack_depth => '2', noproxy => '1');
|
|
|
|
d34 2
|
|
d48 3
|
|
a50 3
|
|
my %cacti_hosts :shared;
|
|
my %curperfdata :shared;
|
|
my @@graph_templates :shared;
|
|
d54 2
|
|
d60 14
|
|
a73 49
|
|
sub read_config ();
|
|
sub read_perfdata ();
|
|
sub clean_perfdata ();
|
|
sub cacti_update ();
|
|
sub dt_check_create ($$);
|
|
sub gt_check_create ($$);
|
|
sub ds_check_create ($$);
|
|
sub g_check_create ($$);
|
|
|
|
open LOG, ">> $log_file";
|
|
|
|
sub verbose ($$) {
|
|
my $level = shift;
|
|
my $string = shift;
|
|
|
|
if ($outtype eq 'log') {
|
|
my $dt = `date +"%a %b %d %T"`; chomp $dt;
|
|
print LOG "$dt => $string", "\n" if $verbose && $verbose >= $level;
|
|
} elsif ($outtype eq 'stderr') {
|
|
print STDERR "### ", $string, "\n" if $verbose && $verbose >= $level;
|
|
}
|
|
}
|
|
|
|
sub graceful_die ($$) {
|
|
my $immediate = shift;
|
|
my $str = shift;
|
|
verbose 0, $str;
|
|
|
|
if ($immediate) {
|
|
exit 1;
|
|
} else {
|
|
lock $terminate;
|
|
$terminate = 1;
|
|
}
|
|
}
|
|
|
|
sub end_program () {
|
|
verbose (0, "Program terminate requested by user");
|
|
lock $terminate;
|
|
$terminate = 1;
|
|
}
|
|
|
|
$SIG{HUP} = \&read_config; # SIGHUP causes configuration re-read
|
|
$SIG{TERM} = \&end_program; # SIGTERM causes graceful termination
|
|
$SIG{INT} = \&end_program; # CTRL+C causes graceful termination
|
|
|
|
open LOG, ">> $log_file";
|
|
|
|
verbose 0, "###### Program Started ###########\n";
|
|
d75 1
|
|
a75 1
|
|
verbose 1, "Trying to log to Cacti...";
|
|
d77 1
|
|
a77 38
|
|
$mech->credentials($cacti_user, $cacti_pass);
|
|
|
|
## Get initial Cacti login page
|
|
$mech->get($cacti_url);
|
|
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#Console#) {
|
|
verbose 0, "Failed to login. Answer: " . $mech->status();
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
exit 1;
|
|
}
|
|
|
|
verbose 1, "Login to Cacti successful\n";
|
|
verbose 2, "Response:\n" . $mech->response()->decoded_content . "\n\n##########################";
|
|
|
|
# We read config initially. Later it can be done using SIGHUP
|
|
read_config ();
|
|
|
|
#my $clean_task = threads -> create(\&clean_perfdata);
|
|
#$clean_task -> detach();
|
|
#my $cacti_task = threads -> create(\&cacti_update);
|
|
#$cacti_task -> detach();
|
|
|
|
# This is an infinite loop, reading and parsing the perfdata pipe
|
|
read_perfdata ();
|
|
|
|
# Give the threads a chance to complete their work
|
|
#while ($cacti_task -> is_running() || $clean_task -> is_running()) {}
|
|
#while ($clean_task -> is_running()) {}
|
|
#while ($cacti_task -> is_running()) {}
|
|
|
|
# Be clean and log the **** out
|
|
$mech->get ("${cacti_url}/logout.php");
|
|
|
|
verbose 0, "########## Program terminated ##########";
|
|
|
|
close LOG;
|
|
|
|
exit 0;
|
|
d83 1
|
|
a83 1
|
|
sub cacti_update () {
|
|
d90 6
|
|
d97 8
|
|
a104 22
|
|
while (1) {
|
|
{ lock %curperfdata;
|
|
foreach my $curserv (keys %curperfdata) {
|
|
lock %{$curperfdata{$curserv}};
|
|
foreach my $curhost (keys %{$curperfdata{$curserv}}) {
|
|
lock %{$curperfdata{$curserv}->{$curhost}};
|
|
next if $curperfdata{$curserv}->{$curhost}->{cactidone} || ! exists $curperfdata{$curserv}->{$curhost}->{perf};
|
|
|
|
# Check if the host exists, first. If it's not in Cacti, there's nothing to do really
|
|
lock %cacti_hosts;
|
|
next unless grep /^${curhost}(|\.princeton\.edu)$/i, keys %cacti_hosts;
|
|
|
|
# Check if the data template exists, and if not, create it
|
|
next if dt_check_create ($curserv, $curhost);
|
|
|
|
# Check if the graph template exists, and if not, create it
|
|
next if gt_check_create ($curserv, $curhost);
|
|
#{
|
|
#lock %{$curperfdata{$curserv}->{$curhost}};
|
|
#$curperfdata{$curserv}->{$curhost}->{cactidone} = 1;
|
|
#next;
|
|
#}
|
|
d106 23
|
|
a128 2
|
|
# Check if the data source exists, and if not, create it
|
|
next if ds_check_create ($curserv, $curhost);
|
|
d130 31
|
|
a160 4
|
|
# Check if the graph exists, and if not, create it
|
|
unless (g_check_create ($curserv, $curhost)) {
|
|
lock %{$curperfdata{$curserv}->{$curhost}->{perf}};
|
|
$curperfdata{$curserv}->{$curhost}->{cactidone} = 1;
|
|
d162 2
|
|
a163 8
|
|
foreach (keys %{$curperfdata{$curserv}->{$curhost}->{perf}}) {
|
|
delete $curperfdata{$curserv}->{$curhost}->{perf}->{$_};
|
|
}
|
|
delete $curperfdata{$curserv}->{$curhost}->{perf};
|
|
}
|
|
}
|
|
} }
|
|
#sleep 5;
|
|
d168 6
|
|
d177 2
|
|
a178 1
|
|
sub dt_check_create ($$) {
|
|
d181 3
|
|
a183 7
|
|
my $data = $curperfdata{$serv}->{$host};
|
|
my $cactiserv;
|
|
{
|
|
lock %{$data->{perf}};
|
|
# We need a unique name for a template, unique for service name AND its perfdata items
|
|
$cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
}
|
|
d192 1
|
|
a192 1
|
|
verbose 0, "Failed to fetch \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
d197 1
|
|
a197 1
|
|
verbose 2, "Query of ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d209 1
|
|
a209 1
|
|
verbose 0, "Failed to submit to \"${cacti_url}/data_templates.php\". Answer: " . $mech->status();
|
|
d214 1
|
|
a214 1
|
|
verbose 2, "Submit to ${cacti_url}/data_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d219 1
|
|
a219 1
|
|
verbose (1, "Data Template already exists for \"$cactiserv\"");
|
|
d222 1
|
|
a222 1
|
|
verbose (1, "Data Template not found for \"$cactiserv\" - will create");
|
|
d229 1
|
|
a229 1
|
|
verbose 0, "Failed to go to the data template addition page. Answer: " . $mech->status();
|
|
d234 1
|
|
a234 1
|
|
verbose 2, "Accessed the page to add a data template:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d238 2
|
|
a239 4
|
|
{
|
|
lock %curperfdata; lock %{$curperfdata{$serv}}; lock %{$data}; lock %{$data->{perf}};
|
|
@@dsnames = keys %{$data->{perf}}; # Our pefdata labels, which are also internal data source names for Cacti
|
|
}
|
|
d244 5
|
|
a248 4
|
|
{
|
|
lock %curperfdata; lock %{$curperfdata{$serv}}; lock %{$data}; lock %{$data->{spec}}; lock %{$data->{spec}->{create_items}};
|
|
@@create_item_regexs = keys %{$data->{spec}->{create_items}};
|
|
$create_item_spec_match = join "", grep { $inids =~ /^$_$/i } @@create_item_regexs;
|
|
d256 12
|
|
a267 13
|
|
lock %{$data->{spec}->{create_items}->{$create_item_spec_match}};
|
|
# In Cacti, we first create an initial data template, with an initial data source name, and then we can add more
|
|
$mech->submit_form(
|
|
fields => {
|
|
template_name => "$cactiserv",
|
|
name => "|host_description| - $cactiserv",
|
|
active => 'unchecked',
|
|
data_source_name => $inids,
|
|
rrd_minimum => $data->{spec}->{create_items}->{$create_item_spec_match}->{min},
|
|
rrd_maximum => $data->{spec}->{create_items}->{$create_item_spec_match}->{max},
|
|
data_source_type_id => $typemap{$data->{spec}->{create_items}->{$create_item_spec_match}->{type}},
|
|
},
|
|
);
|
|
d271 1
|
|
a271 1
|
|
verbose 0, "Failed to save the new data template \"$serv\" -- \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
d276 1
|
|
a276 1
|
|
verbose 2, "Saved the new data template \"$serv\" to ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d288 1
|
|
a288 1
|
|
verbose 0, "Failed to create new item in the new data template \"$serv\" -- \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
d293 1
|
|
a293 1
|
|
verbose 2, "Created new component for new data template \"$serv\", ds component \"$nextds\" in ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d295 11
|
|
a305 12
|
|
{
|
|
lock %curperfdata; lock %{$curperfdata{$serv}}; lock %{$data}; lock %{$data->{spec}}; lock %{$data->{spec}->{create_items}}; lock %{$data->{spec}->{create_items}->{$create_item_spec_match}};
|
|
##################################################
|
|
# Then we save it with necessary parameters
|
|
$mech->submit_form(
|
|
fields => {
|
|
data_source_name => $nextds,
|
|
rrd_minimum => $data->{spec}->{create_items}->{$create_item_spec_match}->{min},
|
|
rrd_maximum => $data->{spec}->{create_items}->{$create_item_spec_match}->{max},
|
|
data_source_type_id => $typemap{$data->{spec}->{create_items}->{$create_item_spec_match}->{type}},
|
|
},
|
|
);
|
|
d309 1
|
|
a309 1
|
|
verbose 0, "Failed to save the new data template \"$serv\" with new item \"$nextds\" - \"${cacti_url}/data_templates.php?action=template_edit\". Answer: " . $mech->status();
|
|
d314 1
|
|
a314 1
|
|
verbose 2, "Saved new component for new data template \"$serv\", ds component \"$nextds\" in ${cacti_url}/data_templates.php?action=template_edit:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d321 1
|
|
a321 1
|
|
verbose (1, "Successfully created a new data template for service \"$cactiserv\"");
|
|
d326 2
|
|
a327 1
|
|
sub gt_check_create ($$) {
|
|
a329 1
|
|
my $data = $curperfdata{$serv}->{$host};
|
|
d332 6
|
|
a337 9
|
|
{
|
|
lock %{$data->{perf}};
|
|
$uniqname = unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
$cactiserv = "${serv}_${uniqname}";
|
|
|
|
lock %{$data->{spec}};
|
|
lock @@graph_templates;
|
|
# Does the template exist?
|
|
return 0 if grep /^$data->{spec}->{graph_template_name}_${cactiserv}$/i, @@graph_templates;
|
|
d340 1
|
|
a340 1
|
|
verbose 1, "Graph template not found. Will create";
|
|
d346 1
|
|
a346 1
|
|
verbose 0, "Failed to fetch \"${cacti_url}/graph_templates.php\". Answer: " . $mech->status();
|
|
d351 1
|
|
a351 1
|
|
verbose 2, "Query of ${cacti_url}/graph_templates.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d357 1
|
|
a357 1
|
|
verbose 0, "Failed to follow the \"Add\" link. Answer: " . $mech->status();
|
|
d362 1
|
|
a362 1
|
|
verbose 2, "Followed the \"Add\" link:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d368 1
|
|
a368 3
|
|
{
|
|
|
|
lock %{$data->{spec}};
|
|
d377 3
|
|
a379 6
|
|
{
|
|
# If the desired graph label is the same as the data source item name (for a single DS, we need to get it out)
|
|
lock %{$data->{perf}};
|
|
my ($firstlbl) = keys %{$data->{perf}};
|
|
lock @@{$data->{perf}->{$firstlbl}};
|
|
$g_templ_label = $data->{spec}->{graph_label} =~ m#\^item\^#i ? $data->{perf}->{$firstlbl}->[5] : $data->{spec}->{graph_label};
|
|
a384 2
|
|
}
|
|
|
|
d399 1
|
|
a399 1
|
|
verbose 0, "Failed to save the new graph template \"${g_templ_name}_${cactiserv}\". Answer: " . $mech->status();
|
|
d404 1
|
|
a404 1
|
|
verbose 2, "Saved new graph template \"${g_templ_name}_${cactiserv}\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
a408 3
|
|
{
|
|
lock %{$data->{perf}};
|
|
lock %{$data->{spec}};
|
|
d410 1
|
|
d422 1
|
|
a422 3
|
|
{
|
|
lock %{$data->{spec}->{graph_items}};
|
|
lock @@{$data->{spec}->{graph_items}->{$graph_item_spec_match}};
|
|
d424 1
|
|
d432 1
|
|
a432 1
|
|
verbose 0, "Failed to add a new graph component by following the \"Add\" link. Answer: " . $mech->status();
|
|
d437 1
|
|
a437 1
|
|
verbose 2, "Followed the \"Add\" link to add a new graph component:\n" . $curcont . "\n\n######################";
|
|
d454 1
|
|
a454 1
|
|
verbose 0, "Could not locate the data_template ID for \"$cactiserv\"";
|
|
a497 1
|
|
lock %{$curgritem};
|
|
a498 1
|
|
lock %{$curgritem->{$gritem}};
|
|
a524 2
|
|
{
|
|
lock %{$data->{perf}->{$curds}};
|
|
a525 1
|
|
}
|
|
d542 1
|
|
a542 1
|
|
$mech -> field (hard_return => 'on') if $gr_items[@@gr_items-1] eq $curgritem;
|
|
d552 1
|
|
a552 1
|
|
verbose 0, "Failed to save a new graph item $curds -> $func for new graph template \"$g_templ_name\". Answer: " . $mech->status();
|
|
d557 1
|
|
a557 2
|
|
verbose 2, "Saved new graph item $curds -> $func for new graph template \"$g_templ_name\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
} # end lock @@gr_items
|
|
d562 1
|
|
a562 1
|
|
verbose 1, "Graph template \"${g_templ_name}_${cactiserv}\" successfully created";
|
|
a563 1
|
|
lock @@graph_templates;
|
|
d572 2
|
|
a573 1
|
|
sub ds_check_create ($$) {
|
|
d576 1
|
|
a576 7
|
|
my $data = $curperfdata{$serv}->{$host};
|
|
my $cactiserv;
|
|
|
|
{
|
|
lock %{$data->{perf}};
|
|
$cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
}
|
|
d586 1
|
|
a586 1
|
|
verbose 0, "Failed to fetch \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
d591 1
|
|
a591 1
|
|
verbose 2, "Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d629 1
|
|
a629 1
|
|
verbose 0, "Failed to search \"${cacti_url}/data_sources.php\". Answer: " . $mech->status();
|
|
d634 1
|
|
a634 1
|
|
verbose 2, "Query of ${cacti_url}/data_sources.php resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d641 1
|
|
a641 1
|
|
verbose 1, "Data source not found for host \"$host\" and service \"$cactiserv\". Will create";
|
|
d647 1
|
|
a647 1
|
|
verbose 0, "Failed to add a new data source by following the \"Add\" link. Answer: " . $mech->status();
|
|
d652 1
|
|
a652 1
|
|
verbose 2, "Followed the \"Add\" link to add a new data source:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d682 1
|
|
a682 1
|
|
verbose 0, "Failed to create a new data source by associating a host with data template. Answer: " . $mech->status();
|
|
d687 1
|
|
a687 1
|
|
verbose 2, "Created a new data source:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d698 1
|
|
a698 1
|
|
verbose 0, "Failed to save a new data source for template \"$cactiserv\" and host \"$host\". Answer: " . $mech->status();
|
|
d706 1
|
|
a706 1
|
|
verbose 2, "Saved a new data source for template \"$cactiserv\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d708 1
|
|
a708 1
|
|
verbose 1, "Successfully created a new data source for host \"$host\" and service \"$cactiserv\"";
|
|
d714 2
|
|
a715 1
|
|
sub g_check_create ($$) {
|
|
a717 1
|
|
my $data = $curperfdata{$serv}->{$host};
|
|
a719 2
|
|
{
|
|
lock %{$data->{perf}};
|
|
a721 1
|
|
}
|
|
d732 1
|
|
a732 2
|
|
{
|
|
lock %{$data->{spec}};
|
|
d734 4
|
|
a737 1
|
|
return 0 if grep m#^\d+\s+\S+\s+\-\s+$g_templ_title\($uniqname\)\s+#i, @@host_graphs;
|
|
d740 1
|
|
a740 1
|
|
verbose 1, "Graph for host \"$host\" and service \"$serv\" could not be located. Will create";
|
|
d745 1
|
|
a745 1
|
|
verbose 0, "Failed to add a new graph for template \"$cactiserv\" and host \"$host\". Answer: " . $mech->status();
|
|
d750 1
|
|
a750 1
|
|
verbose 2, "Created a new graph for template \"$cactiserv\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d763 1
|
|
a763 2
|
|
{
|
|
lock %{$data->{spec}};
|
|
d790 1
|
|
a790 1
|
|
verbose 0, "Failed to save the new graph for graph template \"${g_templ_title}(${uniqname})\" and host \"$host\". Answer: " . $mech->status();
|
|
d795 1
|
|
a795 1
|
|
verbose 2, "Saved the new graph for graph template \"${g_templ_title}(${uniqname})\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d822 1
|
|
a822 1
|
|
verbose 0, "Failed to save the components of the new graph for graph template \"${g_templ_name}_${cactiserv}\" and host \"$host\". Answer: " . $mech->status();
|
|
d827 2
|
|
a828 2
|
|
verbose 2, "Saved the components of the new graph for graph template \"${g_templ_name}_${cactiserv}\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
verbose 1, "Successfully created a graph for host \"$host\"";
|
|
d833 54
|
|
a886 13
|
|
sub read_config () {
|
|
lock (%spec);
|
|
{
|
|
lock (@@graph_templates);
|
|
@@graph_templates = `$php -q ${cacti_path}/cli/add_graphs.php --list-graph-templates`;
|
|
shift @@graph_templates;
|
|
map { chomp; s/^\d+\s+//; } @@graph_templates;
|
|
}
|
|
|
|
{
|
|
lock (%cacti_hosts);
|
|
my @@cac_hosts = `$php -q ${cacti_path}/cli/add_graphs.php --list-hosts`;
|
|
foreach (@@cac_hosts) { /^(\d+)\s+(\S+)\s+(.*)/ and do { $cacti_hosts{$2} = $1; $cacti_hosts{$3} = $1; }; }
|
|
d889 32
|
|
a920 1
|
|
open SPECFILE, "< $spec_file" or graceful_die (1, "Unable to open the service spec file!!!\n");
|
|
d924 2
|
|
a937 1
|
|
lock %{$spec{$curservreg}};
|
|
a945 1
|
|
lock %{$spec{$curservreg}};
|
|
d948 1
|
|
a948 1
|
|
$spec{$curservreg} -> {$curhostreg} = &share ({});
|
|
a954 2
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
a961 2
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
a968 2
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
a975 2
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
a976 1
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{map_items}};
|
|
a981 2
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
a982 1
|
|
lock @@{$spec{$curservreg}->{$curhostreg}->{ignore_items}};
|
|
a988 3
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{create_items}};
|
|
a995 3
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{graph_items}};
|
|
a998 1
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{graph_items}->{$itemspec}};
|
|
a1012 4
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{create_items}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{create_items}->{$createitemspec}};
|
|
a1017 6
|
|
lock %{$spec{$curservreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}};
|
|
lock %{$spec{$curservreg}->{$curhostreg}->{graph_items}};
|
|
lock @@{$spec{$curservreg}->{$curhostreg}->{graph_items}->{$itemspec}};
|
|
lock %{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}[@@{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}} - 1]};
|
|
lock %{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}[@@{$spec{$curservreg} -> {$curhostreg} -> {graph_items} -> {$itemspec}} - 1] -> {$itemspec2}};
|
|
d1054 3
|
|
a1056 2
|
|
# The following will continuously read the perfdata Nagios pipe, and load necessary results into %curpefdata
|
|
sub read_perfdata () {
|
|
d1059 1
|
|
a1059 2
|
|
|
|
open PERFDATA, "< $perf_file" or do { graceful_die (0, "Unable to open the perfdata file \"$perf_file\"!!!"); return 1; };
|
|
d1069 26
|
|
d1102 1
|
|
a1103 2
|
|
|
|
# This skips perfdata lines not matching the spec - don't care about these
|
|
d1115 5
|
|
a1119 9
|
|
lock %curperfdata;
|
|
$curperfdata{$svc} = &share ({}) unless exists $curperfdata{$svc};
|
|
lock %{$curperfdata{$svc}};
|
|
$curperfdata{$svc}->{$hst} = &share ({}) unless exists $curperfdata{$svc}->{$hst};
|
|
lock %{$curperfdata{$svc}->{$hst}};
|
|
my $perfdata :shared = $curperfdata{$svc}->{$hst};
|
|
$perfdata->{perf} = &share ({});
|
|
$perfdata->{time} = $tm;
|
|
$perfdata->{cactidone} = 0;
|
|
d1134 2
|
|
a1135 4
|
|
lock %{$perfdata->{perf}};
|
|
$perfdata->{perf}->{$lbl} = &share ([]);
|
|
lock @@{$perfdata->{perf}->{$lbl}};
|
|
push @@{$perfdata->{perf}->{$lbl}}, $val, $wrn, $crt, $min, $max, $graphlabel;
|
|
d1138 1
|
|
a1138 1
|
|
verbose (1, "Perfdata deciphered: ###$val###$graphlabel###");
|
|
d1141 1
|
|
a1141 1
|
|
# Link the relevant part of the spec hash to the %curperfdata for further use
|
|
d1143 4
|
|
a1146 2
|
|
foreach my $nextspec (keys %spec) {
|
|
next unless $svc =~ m#${nextspec}#;
|
|
a1147 1
|
|
lock %{$spec{$nextspec}};
|
|
d1149 1
|
|
a1149 1
|
|
foreach my $hostspec (keys %{$spec{$nextspec}}) {
|
|
d1152 3
|
|
a1154 6
|
|
lock %{$spec{$nextspec}->{$hostspec}};
|
|
my $curspec :shared = $spec{$nextspec}->{$hostspec};
|
|
$curperfdata{$svc}->{$hst}->{spec} = $curspec;
|
|
|
|
#$spec{$nextspec}->{$hostspec}->{matched} = &share ({}) unless exists $spec{$nextspec}->{$hostspec}->{matched};
|
|
#$spec{$nextspec}->{$hostspec}->{matched}->{$svc} = $hst;
|
|
a1158 2
|
|
lock %{$perfdata->{perf}};
|
|
|
|
d1160 3
|
|
a1162 3
|
|
next unless exists $perfdata->{perf}->{$_};
|
|
#print Dumper ($perfdata->{perf}->{$_});
|
|
delete $perfdata->{perf}->{$_};
|
|
d1170 3
|
|
a1172 3
|
|
next unless exists $perfdata->{perf}->{$_};
|
|
$perfdata->{perf}->{$curspec->{map_items}->{$_}} = $perfdata->{perf}->{$_};
|
|
delete $perfdata->{perf}->{$_};
|
|
a1175 1
|
|
{ lock %{$perfdata->{perf}};
|
|
d1177 5
|
|
a1181 5
|
|
if (keys %{$perfdata->{perf}} == 0) {
|
|
delete $perfdata->{perf};
|
|
delete $curperfdata{$svc}->{$hst};
|
|
delete $curperfdata{$svc} unless keys %{$curperfdata{$svc}} > 0;
|
|
}}
|
|
d1213 1
|
|
a1213 1
|
|
#print "###", keys %{$curperfdata{$svc}->{$hst}}, "###\n";
|
|
d1218 1
|
|
a1218 1
|
|
foreach my $dsitem (keys %{$perfdata->{perf}}) {
|
|
d1225 1
|
|
a1225 1
|
|
|
|
d1233 3
|
|
a1235 4
|
|
lock %{$perfdata->{perf}};
|
|
my $valsupdate = join ":", (map { lock @@{$perfdata->{perf}->{$_}}; $perfdata->{perf}->{$_}->[0]; } keys %{$perfdata->{perf}});
|
|
my @@rrdout = `$rrdtool update ${rrapath}/${hst}_${svc}.rrd $perfdata->{time}:$valsupdate 2>&1`;
|
|
verbose (1, "Update RRD: $rrdtool update ${rrapath}/${hst}_${svc}.rrd $perfdata->{time}:$valsupdate\nResponse: " . join ("\n", @@rrdout));
|
|
d1237 40
|
|
d1278 1
|
|
a1278 1
|
|
} # End foreach nextspec
|
|
d1280 5
|
|
a1284 7
|
|
lock %{$perfdata->{perf}};
|
|
$perfdata->{cactidone} = 1;
|
|
|
|
foreach (keys %{$perfdata->{perf}}) {
|
|
delete $perfdata->{perf}->{$_};
|
|
}
|
|
delete $perfdata->{perf};
|
|
d1287 5
|
|
a1291 1
|
|
last if $terminate == 1;
|
|
d1294 2
|
|
a1295 1
|
|
close PERFDATA;
|
|
d1299 12
|
|
a1310 21
|
|
sub clean_perfdata () {
|
|
while (1) {
|
|
{ lock %curperfdata;
|
|
foreach my $svc (keys %curperfdata) {
|
|
lock %{$curperfdata{$svc}};
|
|
foreach my $hst (keys %{$curperfdata{$svc}}) {
|
|
lock %{$curperfdata{$svc}->{$hst}};
|
|
|
|
my $perfdata = $curperfdata{$svc}->{$hst};
|
|
|
|
# Essentially we check %curperfdata contents for entries where "cactidone" is marked as "1" (completed)
|
|
# by the "cacti_update" thread. This tells us to delete this outdated piece of perfdata
|
|
if ($perfdata->{cactidone} && exists $perfdata->{perf}) {
|
|
# change to 2 later!!!
|
|
verbose (1, "Cleaning perfdata: $svc/$hst");
|
|
#print Dumper ($perfdata);
|
|
lock %{$perfdata->{perf}};
|
|
foreach (keys %{$perfdata->{perf}}) {
|
|
delete $perfdata->{perf}->{$_};
|
|
}
|
|
delete $perfdata->{perf};
|
|
d1312 5
|
|
a1316 12
|
|
# This is commented out, because same hosts/services will come up again and again with new perfdata
|
|
# Hence no need to waste CPU cycles deallocating them
|
|
#foreach my $servhostprop (keys %{$perfdata}) {
|
|
# next if $_ eq 'spec';
|
|
# delete $perfdata->{$servhostprop};
|
|
#}
|
|
#delete $curperfdata{$svc}->{$hst};
|
|
#delete $curperfdata{$svc} if keys %{$curperfdata{$svc}} == 0;
|
|
}
|
|
}
|
|
} }
|
|
sleep 20;
|
|
d1318 7
|
|
d1326 1
|
|
a1326 1
|
|
last if $terminate == 1;
|
|
d1330 5
|
|
@
|
|
|
|
|
|
1.1
|
|
log
|
|
@Initial revision
|
|
@
|
|
text
|
|
@d7 2
|
|
d16 2
|
|
a17 2
|
|
use threads;
|
|
use threads::shared;
|
|
d48 3
|
|
a107 19
|
|
#unless ($mech -> success() && $mech->title() =~ m#Login to Cacti#) {
|
|
# verbose 0, "Failed to fetch \"$cacti_url\". Answer: " . $mech->status();
|
|
# verbose 2, "Response:\n" . $mech->response()->decoded_content;
|
|
# exit 1;
|
|
#}
|
|
|
|
#verbose 2, "Query of $cacti_url resulted in:\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
#verbose 1, "Trying to log in...";
|
|
|
|
## Send login credentials
|
|
#$mech->submit_form(
|
|
# form_name => 'login',
|
|
# fields => {
|
|
# login_username => $cacti_user,
|
|
# login_password => $cacti_pass,
|
|
# }
|
|
#);
|
|
|
|
d120 4
|
|
a123 4
|
|
my $clean_task = threads -> create(\&clean_perfdata);
|
|
$clean_task -> detach();
|
|
my $cacti_task = threads -> create(\&cacti_update);
|
|
$cacti_task -> detach();
|
|
d129 3
|
|
a131 1
|
|
while ($cacti_task -> is_running() || $clean_task -> is_running()) {}
|
|
d153 1
|
|
a153 1
|
|
#return 0;
|
|
d159 2
|
|
a160 2
|
|
{ lock %{$curperfdata{$curserv}->{$curhost}};
|
|
next if $curperfdata{$curserv}->{$curhost}->{cactidone}; }
|
|
d163 2
|
|
a164 2
|
|
{ lock %cacti_hosts;
|
|
next unless grep /^${curhost}.princeton.edu$/i, keys %cacti_hosts; }
|
|
d168 1
|
|
a168 5
|
|
{
|
|
lock %{$curperfdata{$curserv}->{$curhost}};
|
|
$curperfdata{$curserv}->{$curhost}->{cactidone} = 1;
|
|
next;
|
|
}
|
|
d171 5
|
|
d182 1
|
|
a182 1
|
|
lock %{$curperfdata{$curserv}->{$curhost}};
|
|
d184 5
|
|
d192 1
|
|
a192 1
|
|
sleep 10;
|
|
d203 1
|
|
a203 1
|
|
my $data :shared;
|
|
d206 1
|
|
a206 3
|
|
lock %curperfdata; lock %{$curperfdata{$serv}};
|
|
$data = $curperfdata{$serv}->{$host};
|
|
lock %{$data}; lock %{$data->{perf}};
|
|
d211 3
|
|
d348 2
|
|
d359 1
|
|
a359 1
|
|
my $cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
d361 12
|
|
a372 2
|
|
# Does the template exist?
|
|
return 0 if grep /^$data->{spec}->{graph_template_name}$/i, @@graph_templates;
|
|
d398 21
|
|
a418 6
|
|
my $title = "|host_description| - " . $data->{spec}->{graph_title};
|
|
my $hgt = exists $data->{spec}->{graph_height} ? $data->{spec}->{graph_height} : 120;
|
|
my $wdt = exists $data->{spec}->{graph_width} ? $data->{spec}->{graph_width} : 500;
|
|
my $t_upper = exists $data->{spec}->{graph_upper_limit} ? $data->{spec}->{graph_upper_limit} : 100;
|
|
my $t_lower = exists $data->{spec}->{graph_lower_limit} ? $data->{spec}->{graph_lower_limit} : 0;
|
|
my $t_base = exists $data->{spec}->{graph_base} ? $data->{spec}->{graph_base} : 1000;
|
|
d422 2
|
|
d426 1
|
|
a426 1
|
|
name => $data->{spec}->{graph_template_name},
|
|
d433 1
|
|
a433 1
|
|
vertical_label => $data->{spec}->{graph_label},
|
|
d436 1
|
|
a436 1
|
|
|
|
d438 1
|
|
a438 1
|
|
verbose 0, "Failed to save the new graph template \"$data->{spec}->{graph_template_name}\". Answer: " . $mech->status();
|
|
d443 1
|
|
a443 1
|
|
verbose 2, "Saved new graph template \"$data->{spec}->{graph_template_name}\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d447 10
|
|
a456 4
|
|
my @@dsnames = keys %{$data->{perf}};
|
|
my @@graph_item_regexs = keys %{$data->{spec}->{graph_items}};
|
|
my (%data_template_sel, %colors_sel, %function_type_sel, %graph_type_sel);
|
|
my ($data_template_sel_items, $colors_sel_items, $function_type_sel_items, $graph_type_sel_items);
|
|
d461 6
|
|
a466 1
|
|
my @@gr_items = @@{$data->{spec}->{graph_items}->{$graph_item_spec_match}};
|
|
d489 2
|
|
d499 2
|
|
a500 1
|
|
|
|
d525 16
|
|
d542 1
|
|
d555 18
|
|
a572 1
|
|
my $text = $curgritem -> {$gritem} -> {text} =~ m#^\^item\^$# ? $curds : $curgritem -> {$gritem} -> {text};
|
|
d580 3
|
|
a582 1
|
|
$keytemp = $colorkeys[int (rand (scalar (@@colorkeys)))] if $keytemp eq '';
|
|
d588 2
|
|
a589 1
|
|
my $hard_return = $gr_items[@@gr_items-1] eq $curgritem ? 'checked' : 'unchecked';
|
|
d594 1
|
|
a594 1
|
|
hard_return => $hard_return,
|
|
d599 1
|
|
a599 1
|
|
verbose 0, "Failed to save a new graph item $curds -> $func for new graph template \"$data->{spec}->{graph_template_name}\". Answer: " . $mech->status();
|
|
d604 2
|
|
a605 2
|
|
verbose 2, "Saved new graph item $curds -> $func for new graph template \"$data->{spec}->{graph_template_name}\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
|
|
d610 7
|
|
d625 9
|
|
a633 1
|
|
my $cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
d672 1
|
|
d695 2
|
|
d729 1
|
|
d757 3
|
|
d762 1
|
|
d772 8
|
|
a779 1
|
|
my $cactiserv = "${serv}_" . unpack ("L", md5(join "", sort keys %{$data->{perf}}));
|
|
d781 1
|
|
a781 1
|
|
my ($graph_template_sel_items, $host_sel_items);
|
|
d783 2
|
|
a784 1
|
|
my $hst = join "", grep m#^${host}\.princeton\.edu$#i, keys %cacti_hosts;
|
|
d789 7
|
|
a795 1
|
|
return 0 if grep m#^\d+\s+${host}\s+\-\s+ $data->{spec}->{graph_title}\s+${cactiserv}\s*$#i, @@host_graphs;
|
|
d816 7
|
|
a822 1
|
|
my $graphkey = join "", grep m#^$data->{spec}->{graph_template_name}$#i, keys %graph_template_sel;
|
|
d834 1
|
|
d836 5
|
|
d845 2
|
|
a846 2
|
|
unless ($mech -> success() && $mech->response()->decoded_content =~ m#${host} \- $data->{spec}->{graph_template_name}#) {
|
|
verbose 0, "Failed to save the new graph for graph template \"$data->{spec}->{graph_template_name}\" and host \"$host\". Answer: " . $mech->status();
|
|
d851 1
|
|
a851 1
|
|
verbose 2, "Saved the new graph for graph template \"$data->{spec}->{graph_template_name}\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d878 1
|
|
a878 1
|
|
verbose 0, "Failed to save the components of the new graph for graph template \"$data->{spec}->{graph_template_name}\" and host \"$host\". Answer: " . $mech->status();
|
|
d883 2
|
|
a884 1
|
|
verbose 2, "Saved the components of the new graph for graph template \"$data->{spec}->{graph_template_name}\" and host \"$host\":\n" . $mech->response()->decoded_content . "\n\n######################";
|
|
d901 1
|
|
a901 1
|
|
foreach (@@cac_hosts) { /^(\d+)\s+(\S+)/ and $cacti_hosts{$2} = $1; }
|
|
d1098 1
|
|
d1124 1
|
|
a1124 1
|
|
$val =~ s/[^0-9]//g;
|
|
d1138 1
|
|
a1138 1
|
|
next unless $svc =~ /${nextspec}/i;
|
|
d1153 3
|
|
a1155 2
|
|
lock @@{$curspec->{ignore_items}};
|
|
lock %{$perfdata->{perf}};
|
|
d1157 2
|
|
a1158 2
|
|
foreach (@@{$curspec->{ignore_items}}) {
|
|
next unless exists $perfdata->{perf}->{$_};
|
|
d1160 3
|
|
a1162 2
|
|
delete $perfdata->{perf}->{$_};
|
|
}
|
|
d1165 8
|
|
a1172 6
|
|
lock %{$curspec->{map_items}};
|
|
foreach (keys %{$curspec->{map_items}}) {
|
|
next unless exists $perfdata->{perf}->{$_};
|
|
$perfdata->{perf}->{$curspec->{map_items}->{$_}} = $perfdata->{perf}->{$_};
|
|
delete $perfdata->{perf}->{$_};
|
|
}
|
|
d1174 1
|
|
d1180 1
|
|
a1180 1
|
|
}
|
|
d1232 1
|
|
d1240 7
|
|
a1246 1
|
|
sleep 5;
|
|
d1268 1
|
|
a1268 1
|
|
$perfdata->{cactidone} and do {
|
|
d1271 1
|
|
a1271 1
|
|
|
|
d1286 1
|
|
a1286 1
|
|
};
|
|
@
|