summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2015-04-30 17:28:36 -0500
committerJonathan Prykop <jonathan@freeside.biz>2015-04-30 17:28:36 -0500
commitf715c23517292a11330ab241fb13221fd89ffc37 (patch)
tree8a1ecadf07c694e5d4541f804ef40ac74eebf553
parent3acf33a87b4e8ebaac08652177d79b22721e2690 (diff)
RT#18834: Cacti integration [added graph generation]
-rw-r--r--FS/FS/part_export/cacti.pm266
-rwxr-xr-xbin/freeside_cacti.php13
-rwxr-xr-xhttemplate/browse/part_export.cgi2
-rw-r--r--httemplate/edit/elements/part_export/cacti.html42
-rw-r--r--httemplate/edit/part_export.cgi15
5 files changed, 239 insertions, 99 deletions
diff --git a/FS/FS/part_export/cacti.pm b/FS/FS/part_export/cacti.pm
index abeb5e4..eff6c52 100644
--- a/FS/FS/part_export/cacti.pm
+++ b/FS/FS/part_export/cacti.pm
@@ -50,16 +50,42 @@ tie my %options, 'Tie::IxHash',
default => '5' },
'max_graph_size' => { label => 'Maximum size per graph (MB)',
default => '5' },
-# 'delete_graphs' => { label => 'Delete associated graphs and data sources when unprovisioning',
-# type => 'checkbox',
-# },
+ 'delete_graphs' => { label => 'Delete associated graphs and data sources when unprovisioning',
+ type => 'checkbox',
+ },
+ 'cacti_graph_template_id' => {
+ 'label' => 'Graph Template',
+ 'type' => 'custom',
+ 'multiple' => 1,
+ },
+ 'cacti_snmp_query_id' => {
+ 'label' => 'SNMP Query ID',
+ 'type' => 'custom',
+ 'multiple' => 1,
+ },
+ 'cacti_snmp_query_type_id' => {
+ 'label' => 'SNMP Query Type ID',
+ 'type' => 'custom',
+ 'multiple' => 1,
+ },
+ 'cacti_snmp_field' => {
+ 'label' => 'SNMP Field',
+ 'type' => 'custom',
+ 'multiple' => 1,
+ },
+ 'cacti_snmp_value' => {
+ 'label' => 'SNMP Value',
+ 'type' => 'custom',
+ 'multiple' => 1,
+ },
;
%info = (
- 'svc' => 'svc_broadband',
- 'desc' => 'Export service to cacti server, for svc_broadband services',
- 'options' => \%options,
- 'notes' => <<'END',
+ 'svc' => 'svc_broadband',
+ 'desc' => 'Export service to cacti server, for svc_broadband services',
+ 'post_config_element' => '/edit/elements/part_export/cacti.html',
+ 'options' => \%options,
+ 'notes' => <<'END',
Add service to cacti upon provisioning, for broadband services.<BR>
See <A HREF="http://www.freeside.biz/mediawiki/index.php/Freeside:4:Documentation:Cacti#Connecting_Cacti_To_Freeside">documentation</A> for details.
END
@@ -75,8 +101,23 @@ sub _export_insert {
sub _export_delete {
my ($self, $svc_broadband) = @_;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+ foreach my $page (qsearch('cacti_page',{ svcnum => $svc_broadband->svcnum })) {
+ my $error = $page->delete;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
my ($q,$error) = _delete_queue($self, $svc_broadband);
- return $error;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
}
sub _export_replace {
@@ -133,6 +174,7 @@ sub _insert_queue {
'svc_desc' => $svc_broadband->description,
'contact' => $svc_broadband->cust_main->contact,
'svcnum' => $svc_broadband->svcnum,
+ 'self' => $self
);
return ($queue,$error);
}
@@ -148,7 +190,7 @@ sub _delete_queue {
'user' => $self->option('user'),
'hostname' => $svc_broadband->ip_addr,
'script_path' => $self->option('script_path'),
-# 'delete_graphs' => $self->option('delete_graphs'),
+ 'delete_graphs' => $self->option('delete_graphs'),
);
return ($queue,$error);
}
@@ -157,6 +199,7 @@ sub _delete_queue {
sub ssh_insert {
my %opt = @_;
+ my $self = $opt{'self'};
# Option validation
die "Non-numerical Host Template ID, check export configuration\n"
@@ -169,7 +212,10 @@ sub ssh_insert {
$desc =~ s/\$ip_addr/$opt{'hostname'}/g;
$desc =~ s/\$description/$opt{'svc_desc'}/g;
$desc =~ s/\$contact/$opt{'contact'}/g;
- $desc =~ s/'/'\\''/g;
+#for some reason, device names with apostrophes fail to export graphs in Cacti
+#just removing them for now, someday maybe dig to figure out why
+# $desc =~ s/'/'\\''/g;
+ $desc =~ s/'//g;
my $cmd = $php
. $opt{'script_path'}
. q(add_device.php --description=')
@@ -194,51 +240,94 @@ sub ssh_insert {
. $id;
$response = ssh_cmd(%opt, 'command' => $cmd);
unless ( $response =~ /Added Node node-id: \((\d+)\)/ ) {
- die "Error adding host to tree: $response";
+ die "Host added, but error adding host to tree: $response";
}
}
-# # Get list of graph templates for new id
-# $cmd = $php
-# . $opt{'script_path'}
-# . q(freeside_cacti.php --get-graph-templates --host-template=)
-# . $opt{'template_id'};
-# my @gtids = split(/\n/,ssh_cmd(%opt, 'command' => $cmd));
-# die "No graphs configured for host template"
-# unless @gtids;
-#
-# # Create graphs
-# foreach my $gtid (@gtids) {
-#
-# # sanity checks, should never happen
-# next unless $gtid;
-# die "Bad graph template: $gtid"
-# unless $gtid =~ /^\d+$/;
-#
-# # create the graph
-# $cmd = $php
-# . $opt{'script_path'}
-# . q(add_graphs.php --graph-type=cg --graph-template-id=)
-# . $gtid
-# . q( --host-id=)
-# . $id;
-# $response = ssh_cmd(%opt, 'command' => $cmd);
-# die "Error creating graph $gtid: $response"
-# unless $response =~ /Graph Added - graph-id: \((\d+)\)/;
-# my $gid = $1;
-#
-# # add the graph to the tree
-# $cmd = $php
-# . $opt{'script_path'}
-# . q(add_tree.php --type=node --node-type=graph --tree-id=)
-# . $opt{'tree_id'}
-# . q( --graph-id=)
-# . $gid;
-# $response = ssh_cmd(%opt, 'command' => $cmd);
-# die "Error adding graph $gid to tree: $response"
-# unless $response =~ /Added Node/;
-#
-# } #foreach $gtid
+ # Get list of graph templates for new id
+ $cmd = $php
+ . $opt{'script_path'}
+ . q(freeside_cacti.php --get-graph-templates --host-template=)
+ . $opt{'template_id'};
+ my $ginfo = { map { $_ ? ($_ => undef) : () } split(/\n/,ssh_cmd(%opt, 'command' => $cmd)) };
+
+ # Add extra config info
+ my @xtragid = split("\n", $self->option('cacti_graph_template_id'));
+ my @query_id = split("\n", $self->option('cacti_snmp_query_id'));
+ my @query_type_id = split("\n", $self->option('cacti_snmp_query_type_id'));
+ my @snmp_field = split("\n", $self->option('cacti_snmp_field'));
+ my @snmp_value = split("\n", $self->option('cacti_snmp_value'));
+ for (my $i = 0; $i < @xtragid; $i++) {
+ my $gtid = $xtragid[$i];
+ $ginfo->{$gtid} ||= [];
+ push(@{$ginfo->{$gtid}},{
+ 'gtid' => $gtid,
+ 'query_id' => $query_id[$i],
+ 'query_type_id' => $query_type_id[$i],
+ 'snmp_field' => $snmp_field[$i],
+ 'snmp_value' => $snmp_value[$i],
+ });
+ }
+
+ my @gdefs = map {
+ ref($ginfo->{$_}) ? @{$ginfo->{$_}} : {'gtid' => $_}
+ } keys %$ginfo;
+ warn "Host ".$opt{'hostname'}." exported to cacti, but no graphs configured"
+ unless @gdefs;
+
+ # Create graphs
+ my $gerror = '';
+ foreach my $gdef (@gdefs) {
+ # validate graph info
+ my $gtid = $gdef->{'gtid'};
+ next unless $gtid;
+ $gerror .= " Bad graph template: $gtid"
+ unless $gtid =~ /^\d+$/;
+ my $isds = $gdef->{'query_id'}
+ || $gdef->{'query_type_id'}
+ || $gdef->{'snmp_field'}
+ || $gdef->{'snmp_value'};
+ if ($isds) {
+ $gerror .= " Bad SNMP Query Id: " . $gdef->{'query_id'}
+ unless $gdef->{'query_id'} =~ /^\d+$/;
+ $gerror .= " Bad SNMP Query Type Id: " . $gdef->{'query_type_id'}
+ unless $gdef->{'query_type_id'} =~ /^\d+$/;
+ $gerror .= " SNMP Field cannot contain apostrophe"
+ if $gdef->{'snmp_field'} =~ /'/;
+ $gerror .= " SNMP Value cannot contain apostrophe"
+ if $gdef->{'snmp_value'} =~ /'/;
+ }
+ next if $gerror;
+
+ # create the graph
+ $cmd = $php
+ . $opt{'script_path'}
+ . q(add_graphs.php --graph-type=)
+ . ($isds ? 'ds' : 'cg')
+ . q( --graph-template-id=)
+ . $gtid
+ . q( --host-id=)
+ . $id;
+ if ($isds) {
+ $cmd .= q( --snmp-query-id=)
+ . $gdef->{'query_id'}
+ . q( --snmp-query-type-id=)
+ . $gdef->{'query_type_id'}
+ . q( --snmp-field=')
+ . $gdef->{'snmp_field'}
+ . q(' --snmp-value=')
+ . $gdef->{'snmp_value'}
+ . q(');
+ }
+ $response = ssh_cmd(%opt, 'command' => $cmd);
+ #might be more than one graph added, just testing success
+ $gerror .= "Error creating graph $gtid: $response"
+ unless $response =~ /Graph Added - graph-id: \((\d+)\)/;
+
+ } #foreach $gtid
+
+ # job fails, but partial export may have occurred
+ die $gerror . " Partial export occurred\n" if $gerror;
return '';
}
@@ -250,8 +339,8 @@ sub ssh_delete {
. q(freeside_cacti.php --drop-device --ip=')
. $opt{'hostname'}
. q(');
-# $cmd .= q( --delete-graphs)
-# if $opt{'delete_graphs'};
+ $cmd .= q( --delete-graphs)
+ if $opt{'delete_graphs'};
my $response = ssh_cmd(%opt, 'command' => $cmd);
die "Error removing from cacti: " . $response
if $response;
@@ -368,42 +457,43 @@ sub process_graphs {
for (my $i = 0; $i <= $#graphs; $i++) {
my $graph = $graphs[$i];
my $thumbfile = $cachedir . 'graphs/thumb_' . $$graph[0] . '.png';
- if (
- (-e $thumbfile) &&
- ( stat($thumbfile)->size() < $maxgraph )
- ) {
- $nographs = 0;
- # add graph to main file
- my $graphhead = q(<H3>) . $$graph[1] . q(</H3>);
- $svchtml .= $graphhead;
- $svchtml .= anchor_tag( $svcnum, $$graph[0], img_tag($thumbfile) );
- # create graph details file
- my $graphhtml = $svchead . $graphhead;
- my $nodetail = 1;
- my $j = 1;
- while (-e (my $graphfile = $cachedir.'graphs/graph_'.$$graph[0].'_'.$j.'.png')) {
- if ( stat($graphfile)->size() < $maxgraph ) {
- $nodetail = 0;
- $graphhtml .= img_tag($graphfile);
+ if (-e $thumbfile) {
+ if ( stat($thumbfile)->size() < $maxgraph ) {
+ $nographs = 0;
+ # add graph to main file
+ my $graphhead = q(<H3>) . $$graph[1] . q(</H3>);
+ $svchtml .= $graphhead;
+ $svchtml .= anchor_tag( $svcnum, $$graph[0], img_tag($thumbfile) );
+ # create graph details file
+ my $graphhtml = $svchead . $graphhead;
+ my $nodetail = 1;
+ my $j = 1;
+ while (-e (my $graphfile = $cachedir.'graphs/graph_'.$$graph[0].'_'.$j.'.png')) {
+ if ( stat($graphfile)->size() < $maxgraph ) {
+ $nodetail = 0;
+ $graphhtml .= img_tag($graphfile);
+ }
+ unlink($graphfile);
+ $j++;
+ }
+ $graphhtml .= '<P>No detail graphs to display for this graph</P>'
+ if $nodetail;
+ my $newobj = new FS::cacti_page {
+ 'exportnum' => $self->exportnum,
+ 'svcnum' => $svcnum,
+ 'graphnum' => $$graph[0],
+ 'imported' => $now,
+ 'content' => $graphhtml,
+ };
+ $error = $newobj->insert;
+ if ($error) {
+ $dbh->rollback if $oldAutoCommit;
+ die $error;
}
- $j++;
- }
- $graphhtml .= '<P>No detail graphs to display for this graph</P>'
- if $nodetail;
- my $newobj = new FS::cacti_page {
- 'exportnum' => $self->exportnum,
- 'svcnum' => $svcnum,
- 'graphnum' => $$graph[0],
- 'imported' => $now,
- 'content' => $graphhtml,
- };
- $error = $newobj->insert;
- if ($error) {
- $dbh->rollback if $oldAutoCommit;
- die $error;
}
+ unlink($thumbfile);
}
- $job->update_statustext(49 + int($i / $#graphs) * 50);
+ $job->update_statustext(49 + int($i / @graphs) * 50);
}
$svchtml .= '<P>No graphs to display for this service</P>'
if $nographs;
@@ -452,7 +542,7 @@ sub ssh_cmd {
my $ssh = Net::OpenSSH->new($opt->{'user'}.'@'.$opt->{'host'});
die "Couldn't establish SSH connection: ". $ssh->error if $ssh->error;
my ($output, $errput) = $ssh->capture2($opt->{'command'});
- die "Error running SSH command: ". $ssh->error if $ssh->error;
+ die "Error running SSH command: ". $opt->{'command'}. ' ERROR: ' . $ssh->error if $ssh->error;
die $errput if $errput;
return $output;
}
diff --git a/bin/freeside_cacti.php b/bin/freeside_cacti.php
index 0a9ee9c..9f8e4dd 100755
--- a/bin/freeside_cacti.php
+++ b/bin/freeside_cacti.php
@@ -39,18 +39,15 @@ but keeping commented out code for potential future development.
include(dirname(__FILE__)."/../site/include/global.php");
include_once($config["base_path"]."/lib/api_device.php");
include_once($config["base_path"]."/lib/api_automation_tools.php");
-
-/*
include_once($config["base_path"]."/lib/api_data_source.php");
include_once($config["base_path"]."/lib/api_graph.php");
include_once($config["base_path"]."/lib/functions.php");
-*/
/* process calling arguments */
$action = '';
$ip = '';
$host_template = '';
-// $delete_graphs = FALSE;
+$delete_graphs = FALSE;
$parms = $_SERVER["argv"];
array_shift($parms);
if (sizeof($parms)) {
@@ -67,21 +64,19 @@ if (sizeof($parms)) {
case "--get-device":
$action = 'get-device';
break;
+*/
case "--get-graph-templates":
$action = 'get-graph-templates';
break;
-*/
case "--ip":
$ip = trim($value);
break;
case "--host-template":
$host_template = trim($value);
break;
-/*
case "--delete-graphs":
$delete_graphs = TRUE;
break;
-*/
case "--version":
case "-V":
case "-H":
@@ -102,7 +97,6 @@ case "get-graphs":
break;
case "drop-device":
$host_id = host_id($ip);
-/*
if ($delete_graphs) {
// code copied & pasted from version 0.8.8a
// cacti/site/lib/host.php and cacti/site/graphs.php
@@ -126,7 +120,6 @@ case "drop-device":
}
}
}
-*/
api_device_remove($host_id);
if (host_id($ip,1)) {
die("Failed to remove hostname $ip");
@@ -136,6 +129,7 @@ case "drop-device":
case "get-device":
echo host_id($ip);
exit(0);
+*/
case "get-graph-templates":
if (!$host_template) {
die("No host template specified");
@@ -148,7 +142,6 @@ case "get-graph-templates":
exit(0);
}
die("No graph templates associated with this host template");
-*/
default:
die("Specified action not found, contact a developer");
}
diff --git a/httemplate/browse/part_export.cgi b/httemplate/browse/part_export.cgi
index 1f835d7..af988d3 100755
--- a/httemplate/browse/part_export.cgi
+++ b/httemplate/browse/part_export.cgi
@@ -66,7 +66,7 @@ function part_export_areyousure(href) {
% if ( $group ) {
% my @values = split("\n", $opt{$optname});
% $multiples{$group} ||= [];
-% push @{ $multiples{$group} }, [ $optname, @values ] if @values;
+% push @{ $multiples{$group} }, [ $def->{label} || $optname, @values ] if @values;
% delete $opt{$optname};
% } elsif (length($opt{$optname})) { # the normal case
% my $value = $opt{$optname};
diff --git a/httemplate/edit/elements/part_export/cacti.html b/httemplate/edit/elements/part_export/cacti.html
new file mode 100644
index 0000000..9e4a8ec
--- /dev/null
+++ b/httemplate/edit/elements/part_export/cacti.html
@@ -0,0 +1,42 @@
+<table bgcolor="#cccccc" border=0 cellspacing=3>
+<TR>
+ <TH>Graph Template ID</TH>
+ <TH>SNMP Query ID</TH>
+ <TH>SNMP Query Type ID</TH>
+ <TH>SNMP Field</TH>
+ <TH>SNMP Value</TH>
+</TR>
+<TR id="mytemplate">
+ <TD><INPUT TYPE="text" NAME="graph_template_id" ID="graph_template_id"></TD>
+ <TD><INPUT TYPE="text" NAME="snmp_query_id" ID="snmp_query_id"></TD>
+ <TD><INPUT TYPE="text" NAME="snmp_query_type_id" ID="snmp_query_type_id"></TD>
+ <TD><INPUT TYPE="text" NAME="snmp_field" ID="snmp_field"></TD>
+ <TD><INPUT TYPE="text" NAME="snmp_value" ID="snmp_value"></TD>
+</TR>
+<& /elements/auto-table.html,
+ template_row => 'mytemplate',
+ fieldorder => ['graph_template_id','snmp_query_id','snmp_query_type_id','snmp_field','snmp_value'],
+ data => \@data,
+ table => 'cacti',
+&>
+</table>
+<INPUT TYPE="hidden" name="multi_options" value="<% $multiopts %>">
+<%init>
+my %opt = @_;
+my $part_export = $opt{part_export} || die "No part_export specified";
+
+my @fields = ('cacti_graph_template_id','cacti_snmp_query_id','cacti_snmp_query_type_id','cacti_snmp_field','cacti_snmp_value');
+my $multiopts = join(',',@fields);
+my @byfield = map {
+ [ split("\n", $part_export->option($_)) ]
+} @fields;
+my @data;
+for (my $i = 0; $i < @{$byfield[0]}; $i++) {
+ my @thisrow;
+ for (my $j = 0; $j < @byfield; $j++) {
+ push(@thisrow,$byfield[$j][$i]);
+ }
+ push(@data,\@thisrow);
+}
+
+</%init>
diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi
index 0e53e29..3820931 100644
--- a/httemplate/edit/part_export.cgi
+++ b/httemplate/edit/part_export.cgi
@@ -183,6 +183,10 @@ my $widget = new HTML::Widgets::SelectLayers(
? $optinfo->{default}
: ''
);
+
+ #handle these with post_config_element
+ next if $type eq 'custom';
+
if ( $type eq 'title' ) {
$html .= qq!<TR><TH COLSPAN=1 ALIGN="right"><FONT SIZE="+1">! .
$label .
@@ -283,6 +287,17 @@ my $widget = new HTML::Widgets::SelectLayers(
$html .= '</TABLE>';
+ # false laziness with config_element above
+ # create 'post_config_element' to generate the whole layer with a Mason component
+ if ( my $include = $exports->{$layer}{post_config_element} ) {
+ # might need to adjust the scope of this at some point
+ $html .= $m->scomp($include,
+ part_export => $part_export,
+ layer => $layer,
+ export_info => $exports->{$layer}
+ );
+ }
+
$html .= '<INPUT TYPE="hidden" NAME="options" VALUE="'.
join(',', keys %{$exports->{$layer}{options}} ). '">';