[freeside-commits] branch master updated. 5f4099e52bd894d644c676ea75e1b0cb588393c8

Jonathan Prykop jonathan at 420.am
Fri Mar 20 13:27:13 PDT 2015


The branch, master has been updated
       via  5f4099e52bd894d644c676ea75e1b0cb588393c8 (commit)
      from  db11c15281c8cf85c1d1ef7ffa645848cf55abdd (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5f4099e52bd894d644c676ea75e1b0cb588393c8
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Fri Mar 20 15:25:12 2015 -0500

    RT#18834 Cacti integration [phase one, simple but stable]

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index a048d3e..3cdad43 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4677,6 +4677,7 @@ sub tables_hashref {
         'suid',                    'int', 'NULL',        '', '', '',
         'shared_svcnum',           'int', 'NULL',        '', '', '',
         'serviceid',           'varchar', 'NULL',        64, '', '',#srvexport/reportfields
+        'cacti_leaf_id',           'int', 'NULL',        '', '', '',
       ],
       'primary_key'  => 'svcnum',
       'unique'       => [ [ 'ip_addr' ], [ 'mac_addr' ] ],
diff --git a/FS/FS/part_export/cacti.pm b/FS/FS/part_export/cacti.pm
new file mode 100644
index 0000000..6877c8f
--- /dev/null
+++ b/FS/FS/part_export/cacti.pm
@@ -0,0 +1,331 @@
+package FS::part_export::cacti;
+
+use strict;
+use base qw( FS::part_export );
+use FS::Record qw( qsearchs );
+use FS::UID qw( dbh );
+
+use vars qw( %info );
+
+my $php = 'php -q ';
+
+tie my %options, 'Tie::IxHash',
+  'user'              => { label   => 'User Name',
+                           default => 'freeside' },
+  'script_path'       => { label   => 'Script Path',
+                           default => '/usr/share/cacti/cli/' },
+  'base_url'          => { label   => 'Base Cacti URL',
+                           default => '' },
+  'template_id'       => { label   => 'Host Template ID',
+                           default => '' },
+  'tree_id'           => { label   => 'Graph Tree ID',
+                           default => '' },
+  'description'       => { label   => 'Description (can use $ip_addr and $description tokens)',
+                           default => 'Freeside $description $ip_addr' },
+#  'delete_graphs'     => { label   => 'Delete associated graphs and data sources when unprovisioning', 
+#                           type    => 'checkbox',
+#                         },
+;
+
+%info = (
+  'svc'             => 'svc_broadband',
+  'desc'            => 'Export service to cacti server, for svc_broadband services',
+  'options'         => \%options,
+  'notes'           => <<'END',
+Add service to cacti upon provisioning, for broadband services.<BR>
+See FS::part_export::cacti documentation for details.
+END
+);
+
+# standard hooks for provisioning/unprovisioning service
+
+sub _export_insert {
+  my ($self, $svc_broadband) = @_;
+  my ($q,$error) = _insert_queue($self, $svc_broadband);
+  return $error;
+}
+
+sub _export_delete {
+  my ($self, $svc_broadband) = @_;
+  my ($q,$error) = _delete_queue($self, $svc_broadband);
+  return $error;
+}
+
+sub _export_replace {
+  my($self, $new, $old) = @_;
+  return '' if $new->ip_addr eq $old->ip_addr; #important part didn't change
+  #delete old then insert new, with second job dependant on the first
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+  my ($dq, $iq, $error);
+  ($dq,$error) = _delete_queue($self,$old);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+  ($iq,$error) = _insert_queue($self,$new);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+  $error = $iq->depend_insert($dq->jobnum);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  return '';
+}
+
+sub _export_suspend {
+  return '';
+}
+
+sub _export_unsuspend {
+  return '';
+}
+
+# create queued jobs
+
+sub _insert_queue {
+  my ($self, $svc_broadband) = @_;
+  my $queue = new FS::queue {
+    'svcnum' => $svc_broadband->svcnum,
+    'job'    => "FS::part_export::cacti::ssh_insert",
+  };
+  my $error = $queue->insert(
+    'host'        => $self->machine,
+    'user'        => $self->option('user'),
+    'hostname'    => $svc_broadband->ip_addr,
+    'script_path' => $self->option('script_path'),
+    'template_id' => $self->option('template_id'),
+    'tree_id'     => $self->option('tree_id'),
+    'description' => $self->option('description'),
+	'svc_desc'    => $svc_broadband->description,
+    'svcnum'      => $svc_broadband->svcnum,
+  );
+  return ($queue,$error);
+}
+
+sub _delete_queue {
+  my ($self, $svc_broadband) = @_;
+  my $queue = new FS::queue {
+    'svcnum' => $svc_broadband->svcnum,
+    'job'    => "FS::part_export::cacti::ssh_delete",
+  };
+  my $error = $queue->insert(
+    'host'          => $self->machine,
+    'user'          => $self->option('user'),
+    'hostname'      => $svc_broadband->ip_addr,
+    'script_path'   => $self->option('script_path'),
+#    'delete_graphs' => $self->option('delete_graphs'),
+  );
+  return ($queue,$error);
+}
+
+# routines run by queued jobs
+
+sub ssh_insert {
+  my %opt = @_;
+
+  # Option validation
+  die "Non-numerical Host Template ID, check export configuration\n"
+    unless $opt{'template_id'} =~ /^\d+$/;
+  die "Non-numerical Graph Tree ID, check export configuration\n"
+    unless $opt{'tree_id'} =~ /^\d+$/;
+
+  # Add host to cacti
+  my $desc = $opt{'description'};
+  $desc =~ s/\$ip_addr/$opt{'hostname'}/g;
+  $desc =~ s/\$description/$opt{'svc_desc'}/g;
+  $desc =~ s/'/'\\''/g;
+  my $cmd = $php
+          . $opt{'script_path'} 
+          . q(add_device.php --description=')
+          . $desc
+          . q(' --ip=')
+          . $opt{'hostname'}
+          . q(' --template=)
+          . $opt{'template_id'};
+  my $response = ssh_cmd(%opt, 'command' => $cmd);
+  unless ( $response =~ /Success - new device-id: \((\d+)\)/ ) {
+    die "Error adding device: $response";
+  }
+  my $id = $1;
+
+  # Add host to tree
+  $cmd = $php
+       . $opt{'script_path'}
+       . q(add_tree.php --type=node --node-type=host --tree-id=)
+       . $opt{'tree_id'}
+       . q( --host-id=)
+       . $id;
+  $response = ssh_cmd(%opt, 'command' => $cmd);
+  unless ( $response =~ /Added Node node-id: \((\d+)\)/ ) {
+      die "Error adding host to tree: $response";
+  }
+  my $leaf_id = $1;
+
+  # Store id for generating graph urls
+  my $svc_broadband = qsearchs({
+    'table'   => 'svc_broadband',
+    'hashref' => { 'svcnum' => $opt{'svcnum'} },
+  });
+  die "Could not reload broadband service" unless $svc_broadband;
+  $svc_broadband->set('cacti_leaf_id',$leaf_id);
+  my $error = $svc_broadband->replace;
+  return $error if $error;
+
+#  # 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
+
+  return '';
+}
+
+sub ssh_delete {
+  my %opt = @_;
+  my $cmd = $php
+          . $opt{'script_path'} 
+          . q(freeside_cacti.php --drop-device --ip=')
+          . $opt{'hostname'}
+          . q(');
+#  $cmd .= q( --delete-graphs)
+#    if $opt{'delete_graphs'};
+  my $response = ssh_cmd(%opt, 'command' => $cmd);
+  die "Error removing from cacti: " . $response
+    if $response;
+  return '';
+}
+
+#fake false laziness, other ssh_cmds handle error/output differently
+sub ssh_cmd {
+  use Net::OpenSSH;
+  my $opt = { @_ };
+  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 $errput if $errput;
+  return $output;
+}
+
+=pod
+
+=head1 NAME
+
+FS::part_export::cacti
+
+=head1 SYNOPSIS
+
+Cacti integration for Freeside
+
+=head1 DESCRIPTION
+
+This module in particular handles FS::part_export object creation for Cacti integration;
+consult any existing L<FS::part_export> documentation for details on how that works.
+What follows is more general instructions for connecting your Cacti installation
+to your Freeside installation.
+
+=head2 Connecting Cacti To Freeside
+
+Copy the freeside_cacti.php script from the bin directory of your Freeside
+installation to the cli directory of your Cacti installation.  Give this file 
+the same permissions as the other files in that directory, and create 
+(or choose an existing) user with sufficient permission to read these scripts.
+
+In the regular Cacti interface, create a Host Template to be used by 
+devices exported by Freeside, and note the template's id number.
+
+In Freeside, go to Configuration->Services->Provisioning exports to
+add a new export.  From the Add Export page, select cacti for Export then enter...
+
+* the User Name with permission to run scripts in the cli directory
+
+* enter the full Script Path to that directory (eg /usr/share/cacti/cli/)
+
+* enter the Base Cacti URL for your cacti server (eg https://example.com/cacti/)
+
+* the Host Template ID for adding new devices
+
+* the Graph Tree ID for adding new devices
+
+* the Description for new devices;  you can use the tokens
+  $ip_addr and $description to include the equivalent fields
+  from the broadband service definition
+
+After adding the export, go to Configuration->Services->Service definitions.
+The export you just created will be available for selection when adding or
+editing broadband service definitions.
+
+When properly configured broadband services are provisioned, they should now
+be added to Cacti using the Host Template you specified, and the created device
+will also be added to the specified Graph Tree.
+
+Once added, a link to the graphs for this host will be available when viewing 
+the details of the provisioned service in Freeside (you will need to authenticate 
+into Cacti to view them.)
+
+Devices will be deleted from Cacti when the service is unprovisioned in Freeside, 
+and they will be deleted and re-added if the ip address changes.
+
+Currently, graphs themselves must still be added in cacti by hand or some
+other form of automation tailored to your specific graph inputs and data sources.
+
+=head1 AUTHOR
+
+Jonathan Prykop 
+jonathan at freeside.biz
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2015 Freeside Internet Services      
+
+This program is free software; you can redistribute it and/or           |
+modify it under the terms of the GNU General Public License             |
+as published by the Free Software Foundation.
+
+=cut
+
+1;
+
+
diff --git a/bin/freeside_cacti.php b/bin/freeside_cacti.php
new file mode 100755
index 0000000..22fb0f0
--- /dev/null
+++ b/bin/freeside_cacti.php
@@ -0,0 +1,175 @@
+#!/usr/bin/php -q
+<?php
+/*
+ +-------------------------------------------------------------------------+
+ | Copyright (C) 2015 Freeside Internet Services                           |
+ |                                                                         |
+ | This program is free software; you can redistribute it and/or           |
+ | modify it under the terms of the GNU General Public License             |
+ | as published by the Free Software Foundation; either version 2          |
+ | of the License, or (at your option) any later version.                  |
+ |                                                                         |
+ | This program is distributed in the hope that it will be useful,         |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
+ | GNU General Public License for more details.                            |
+ +-------------------------------------------------------------------------+
+ | Copy this file to the cli directory of your Cacti installation, which   |
+ | should also contain an add_device.php script.  Give this file the same  |
+ | permissions as add_device.php, and configure your Freeside installation |
+ | with the location of that directory and the name of a user who has      |
+ | permission to read these files.  See the FS::part_export::cacti docs    |
+ | for more details.                                                       |
+ +-------------------------------------------------------------------------+
+*/
+
+/* do NOT run this script through a web browser */
+if (!isset($_SERVER["argv"][0]) || isset($_SERVER['REQUEST_METHOD'])  || isset($_SERVER['REMOTE_ADDR'])) {
+	die("<br><strong>This script is only meant to run at the command line.</strong>");
+}
+
+/* We are not talking to the browser */
+$no_http_headers = true;
+
+/* 
+Currently, only drop-device is actually being used by Freeside integration,
+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;
+$parms = $_SERVER["argv"];
+array_shift($parms);
+if (sizeof($parms)) {
+	foreach($parms as $parameter) {
+		@list($arg, $value) = @explode("=", $parameter);
+		switch ($arg) {
+        case "--drop-device":
+			$action = 'drop-device';
+            break;
+/*
+        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":
+		case "--help":
+			die(default_die());
+		default:
+			die("ERROR: Invalid Argument: ($arg)");
+		}
+	}
+} else {
+  die(default_die());
+}
+
+/* Now take an action */
+switch ($action) {
+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 
+		// unfortunately no api function for this yet
+		$graphs = db_fetch_assoc("select
+			graph_local.id as local_graph_id
+			from graph_local
+			where graph_local.host_id=" . $host_id);
+		if (sizeof($graphs) > 0) {
+			foreach ($graphs as $graph) {
+				$data_sources = array_rekey(db_fetch_assoc("SELECT data_template_data.local_data_id
+					FROM (data_template_rrd, data_template_data, graph_templates_item)
+					WHERE graph_templates_item.task_item_id=data_template_rrd.id
+					AND data_template_rrd.local_data_id=data_template_data.local_data_id
+					AND graph_templates_item.local_graph_id=" . $graph["local_graph_id"] . "
+					AND data_template_data.local_data_id > 0"), "local_data_id", "local_data_id");
+				if (sizeof($data_sources)) {
+					api_data_source_remove_multi($data_sources);
+				}
+				api_graph_remove($graph["local_graph_id"]);
+			}
+		}
+	}
+*/
+	api_device_remove($host_id);
+	if (host_id($ip,1)) {
+		die("Failed to remove hostname $ip");
+	}
+	exit(0);
+/*
+case "get-device":
+	echo host_id($ip);
+	exit(0);
+case "get-graph-templates":
+	if (!$host_template) {
+		die("No host template specified");
+	}
+	$graphs = getGraphTemplatesByHostTemplate($host_template);
+	if (sizeof($graphs)) {
+		foreach (array_keys($graphs) as $gtid) {
+			echo $gtid . "\n";
+		}
+		exit(0);
+	}
+	die("No graph templates associated with this host template");
+*/
+default:
+	die("Specified action not found, contact a developer");
+}
+
+function default_die() {
+  return "Cacti interface for freeside.  Do not use for anything else.";
+}
+
+function host_id($ip_address, $nodie=0) {
+	if (!$ip_address) {
+		die("No hostname specified");
+	}
+	$devices = array();
+	$query = "select id from host";
+	$query .= " where hostname='$ip_address'";
+	$devices = db_fetch_assoc($query);
+	if (sizeof($devices) > 1) {
+        // This should never happen, just being thorough
+		die("Multiple devices found for hostname $ip_address");
+	} else if (!sizeof($devices)) {
+		if ($nodie) {
+			return '';
+		} else {
+			die("Could not find hostname $ip_address");
+		}
+	}
+	return $devices[0]['id'];
+}
+
+?>
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
index 70c0b53..9fe10bd 100644
--- a/httemplate/view/svc_broadband.cgi
+++ b/httemplate/view/svc_broadband.cgi
@@ -72,6 +72,17 @@ sub ip_addr {
   my $out = $ip_addr;
   $out .= ' (' . include('/elements/popup_link-ping.html', ip => $ip_addr) . ')'
     if $ip_addr;
+  if ($svc->cacti_leaf_id) {
+    # should only ever be one, but not sure if that is enforced
+    my ($cacti) = $svc->cust_svc->part_svc->part_export('cacti');
+    $out .= ' (<A HREF="' 
+         .  $cacti->option('base_url')
+         .  'graph_view.php?action=tree&tree_id='
+         .  $cacti->option('tree_id')
+         .  '&leaf_id='
+         .  $svc->cacti_leaf_id
+         .  '">cacti</A>)';
+  }
   if ( my $addr_block = $svc->addr_block ) {
     $out .= '<br>Netmask: ' . $addr_block->NetAddr->mask .
             '<br>Gateway: ' . $addr_block->ip_gateway;

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Schema.pm                   |    1 +
 FS/FS/part_export/cacti.pm        |  331 +++++++++++++++++++++++++++++++++++++
 bin/freeside_cacti.php            |  175 ++++++++++++++++++++
 httemplate/view/svc_broadband.cgi |   11 ++
 4 files changed, 518 insertions(+)
 create mode 100644 FS/FS/part_export/cacti.pm
 create mode 100755 bin/freeside_cacti.php




More information about the freeside-commits mailing list