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? #Cisco Router - 5 Minute CPU # if (grep m#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; # # #Data Source [home]
while ($curcont =~ m#]+>Data Source \[([^\]]+)\]
#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 () { 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 () { 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 = ; 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 () { 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#
{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 () { 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 }; @