From e5e3f6ec09be93b0d00d1f84e4c7cade6e18a802 Mon Sep 17 00:00:00 2001 From: mark Date: Fri, 28 Oct 2011 06:44:39 +0000 Subject: [PATCH] SNMP export to service ip address, #14669 --- FS/FS/part_export/broadband_snmp.pm | 183 ++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 FS/FS/part_export/broadband_snmp.pm diff --git a/FS/FS/part_export/broadband_snmp.pm b/FS/FS/part_export/broadband_snmp.pm new file mode 100644 index 000000000..d0eca3979 --- /dev/null +++ b/FS/FS/part_export/broadband_snmp.pm @@ -0,0 +1,183 @@ +package FS::part_export::broadband_snmp; + +use strict; +use vars qw(%info $DEBUG); +use base 'FS::part_export'; +use Net::SNMP qw(:asn1 :snmp); +use Tie::IxHash; + +$DEBUG = 1; + +my $me = '['.__PACKAGE__.']'; + +tie my %snmp_version, 'Tie::IxHash', + v1 => SNMP_VERSION_1, + v2c => SNMP_VERSION_2C, + # 3 => 'v3' not implemented +; + +tie my %snmp_type, 'Tie::IxHash', + i => INTEGER, + u => UNSIGNED32, + s => OCTET_STRING, + n => NULL, + o => OBJECT_IDENTIFIER, + t => TIMETICKS, + a => IPADDRESS, + # others not implemented yet +; + +tie my %options, 'Tie::IxHash', + 'version' => { label=>'SNMP version', + type => 'select', + options => [ keys %snmp_version ], + }, + 'community' => { label=>'Community', default=>'public' }, + ( + map { $_.'_command', + { label => ucfirst($_) . ' commands', + type => 'textarea', + default => '', + } + } qw( insert delete replace suspend unsuspend ) + ), + 'ip_addr_change_to_new' => { + label=>'Send IP address changes to new address', + type=>'checkbox' + }, + 'timeout' => { label=>'Timeout (seconds)' }, +; + +%info = ( + 'svc' => 'svc_broadband', + 'desc' => 'Send SNMP requests to the service IP address', + 'options' => \%options, + 'weight' => 10, + 'notes' => <<'END' +Send one or more SNMP SET requests to the IP address registered to the service. +Enter one command per line. Each command is a target OID, data type flag, +and value, separated by spaces. +The data type flag is one of the following: + +The value may interpolate fields from svc_broadband by prefixing the field +name with $, or $new_ and $old_ for replace operations. +The value may contain whitespace; quotes are not necessary.
+
+For example, to set the SNMPv2-MIB "sysName.0" object to the string +"svc_broadband" followed by the service number, use the following +command:
+
1.3.6.1.2.1.1.5.0 s svc_broadband$svcnum

+END +); + +sub export_insert { + my $self = shift; + $self->export_command('insert', @_); +} + +sub export_delete { + my $self = shift; + $self->export_command('delete', @_); +} + +sub export_replace { + my $self = shift; + $self->export_command('replace', @_); +} + +sub export_suspend { + my $self = shift; + $self->export_command('suspend', @_); +} + +sub export_unsuspend { + my $self = shift; + $self->export_command('unsuspend', @_); +} + +sub export_command { + my $self = shift; + my ($action, $svc_new, $svc_old) = @_; + + my $command_text = $self->option($action.'_command'); + return if !length($command_text); + + warn "$me parsing ${action}_command:\n" if $DEBUG; + my @commands; + foreach (split /\n/, $command_text) { + my ($oid, $type, $value) = split /\s/, $_, 3; + $oid =~ /^(\d+\.)*\d+$/ or die "invalid OID '$oid'\n"; + my $typenum = $snmp_type{$type} or die "unknown data type '$type'\n"; + $value = '' if !defined($value); # allow sending an empty string + $value = $self->substitute($value, $svc_new, $svc_old); + warn "$me $oid $type $value\n" if $DEBUG; + push @commands, $oid, $typenum, $value; + } + + my $ip_addr = $svc_new->ip_addr; + # ip address change: send to old address unless told otherwise + if ( defined $svc_old and ! $self->option('ip_addr_change_to_new') ) { + $ip_addr = $svc_old->ip_addr; + } + warn "$me opening session to $ip_addr\n" if $DEBUG; + + my %opt = ( + -hostname => $ip_addr, + -community => $self->option('community'), + -timeout => $self->option('timeout') || 20, + ); + my $version = $self->option('version'); + $opt{-version} = $snmp_version{$version} or die 'invalid version'; + $opt{-varbindlist} = \@commands; # just for now + + $self->snmp_queue( $svc_new->svcnum, %opt ); +} + +sub snmp_queue { + my $self = shift; + my $svcnum = shift; + my $queue = new FS::queue { + 'svcnum' => $svcnum, + 'job' => 'FS::part_export::broadband_snmp::snmp_request', + }; + $queue->insert(@_); +} + +sub snmp_request { + my %opt = @_; + my $varbindlist = delete $opt{-varbindlist}; + my ($session, $error) = Net::SNMP->session(%opt); + die "Couldn't create SNMP session: $error" if !$session; + + warn "$me sending SET request\n" if $DEBUG; + my $result = $session->set_request( -varbindlist => $varbindlist ); + $error = $session->error(); + $session->close(); + + if (!defined $result) { + die "SNMP request failed: $error\n"; + } +} + +sub substitute { + # double-quote-ish interpolation of service fields + # accepts old_ and new_ for replace actions, like shellcommands + my $self = shift; + my ($value, $svc_new, $svc_old) = @_; + foreach my $field ( $svc_new->fields ) { + my $new_val = $svc_new->$field; + $value =~ s/\$(new_)?$field/$new_val/g; + if ( $svc_old ) { # replace only + my $old_val = $svc_old->$field; + $value =~ s/\$old_$field/$old_val/g; + } + } + $value; +} + +1; -- 2.11.0