Blame perl/AnyData_SNMP/Storage.pm

Packit fcad23
#########################################################################
Packit fcad23
package AnyData::Storage::SNMP;
Packit fcad23
#########################################################################
Packit fcad23
Packit fcad23
## XXX: TODO:
Packit fcad23
##   scalar sets?
Packit fcad23
##   multi-hosts
Packit fcad23
Packit fcad23
$AnyData::Storage::VERSION = '5.08';
Packit fcad23
use strict;
Packit fcad23
use warnings;
Packit fcad23
Packit fcad23
use vars qw(@basecols);
Packit fcad23
Packit fcad23
@AnyData::Storage::SNMP::basecols = qw(hostname iid);
Packit fcad23
$AnyData::Storage::SNMP::iidptr = 1; # must match array column of above
Packit fcad23
@AnyData::Storage::SNMP::basetypes = qw(OCTETSTR OBJECTID);
Packit fcad23
$AnyData::Storage::SNMP::debug = 0;
Packit fcad23
$AnyData::Storage::SNMP::debugre = undef;
Packit fcad23
Packit fcad23
use Data::Dumper;
Packit fcad23
use AnyData::Storage::File;
Packit fcad23
use SNMP;
Packit fcad23
SNMP::init_snmp("AnyData::SNMP");
Packit fcad23
Packit fcad23
sub new {
Packit fcad23
#    DEBUG("calling AnyData::Storage::SNMP new\n");
Packit fcad23
#    DEBUG("new storage: ",Dumper(\@_),"\n");
Packit fcad23
    my $class = shift;
Packit fcad23
    my $self  = shift || {};
Packit fcad23
    $self->{open_mode} = 'c';
Packit fcad23
    return bless $self, $class;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub open_table {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP open_table\n");
Packit fcad23
    my ($self, $parser, $table, $mode, $tname) = @_;
Packit fcad23
    $self->{'process_table'} = $tname;
Packit fcad23
    DEBUG("open_table: ",Dumper(\@_),"\n");
Packit fcad23
}
Packit fcad23
Packit fcad23
sub get_col_names {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP get_col_names\n");
Packit fcad23
    my ($self, $parser, $tname) = @_;
Packit fcad23
    DEBUG("get_col_names\n",Dumper(\@_),"\n");
Packit fcad23
    $tname = $self->{'process_table'} if (!$tname);
Packit fcad23
Packit fcad23
    # get cached previous results
Packit fcad23
    return $self->{col_names}{$tname} if (defined($self->{col_names}{$tname}));
Packit fcad23
Packit fcad23
    # table name
Packit fcad23
    $tname = $self->{'process_table'} if (!$tname);
Packit fcad23
Packit fcad23
    # mib node setup
Packit fcad23
    my $mib = $SNMP::MIB{$tname} || return warn "no such table $tname";
Packit fcad23
    my $entry = $mib->{'children'}[0];
Packit fcad23
Packit fcad23
    # base columns and types
Packit fcad23
    my @cols = @AnyData::Storage::SNMP::basecols;
Packit fcad23
    my @types = @AnyData::Storage::SNMP::basetypes;
Packit fcad23
    my %donecol;
Packit fcad23
    my $count = $#cols;
Packit fcad23
Packit fcad23
    foreach my $index (@{$entry->{'indexes'}}) {
Packit fcad23
	push @cols, $index;
Packit fcad23
	push @types, $SNMP::MIB{$index}{type};
Packit fcad23
	$donecol{$index} = 1;
Packit fcad23
	$count++;
Packit fcad23
	if ($SNMP::MIB{$index}{parent}{label} eq $entry->{label}) {
Packit fcad23
	    # this index is a member of this table
Packit fcad23
	    $self->{columnmap}[$count] = $index;
Packit fcad23
	    $self->{coloffset} += 1;
Packit fcad23
	}
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # search children list
Packit fcad23
    foreach my $child  ( sort { $a->{'subID'} <=> $b->{'subID'} } @{$entry->{'children'}}) {
Packit fcad23
	push @{$self->{real_cols}}, $child->{'label'};
Packit fcad23
	next if ($donecol{$child->{label}});
Packit fcad23
	push @cols, $child->{'label'};
Packit fcad23
	push @types, $child->{'type'};
Packit fcad23
	$count++;
Packit fcad23
	$self->{columnmap}[$count] = $child->{'label'};
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # save for later.
Packit fcad23
    $parser->{col_names} = \@cols;
Packit fcad23
    $self->{col_types} = \@types;
Packit fcad23
    $self->{col_names} = \@cols;
Packit fcad23
    map { $self->{col_uc_map}{uc($_)} = $_ } @cols;
Packit fcad23
    return \@cols;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub set_col_nums {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP set_col_nums\n");
Packit fcad23
    DEBUG("set_col_nums\n",Dumper(\@_),"\n");
Packit fcad23
    my ($self, $tname) = @_;
Packit fcad23
    return $self->{col_nums} if (defined($self->{col_nums}));
Packit fcad23
    my $mib = $SNMP::MIB{$tname};
Packit fcad23
    my $entry = $mib->{'children'}[0];
Packit fcad23
    my (%cols, %mibnodes);
Packit fcad23
    my $cnt = -1;
Packit fcad23
Packit fcad23
    foreach my $i (@{$self->{col_names}}) {
Packit fcad23
	$cols{$i} = ++$cnt;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    $self->{col_nums} = \%cols;
Packit fcad23
    return \%cols;
Packit fcad23
}
Packit fcad23
Packit fcad23
# not needed?
Packit fcad23
sub get_file_handle {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP get_file_handle\n");
Packit fcad23
    DEBUG("get_file_handle\n",Dumper(\@_),"\n");
Packit fcad23
    return shift;
Packit fcad23
}
Packit fcad23
Packit fcad23
# not needed?
Packit fcad23
sub get_file_name {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP get_file_name\n");
Packit fcad23
    DEBUG("get_file_name\n",Dumper(\@_),"\n");
Packit fcad23
    my $self = shift;
Packit fcad23
    return $self->{process_table} || $self->{table_name};
Packit fcad23
}
Packit fcad23
Packit fcad23
sub truncate {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP truncate\n");
Packit fcad23
    my ($self) = @_;
Packit fcad23
    DEBUG("trunacte, ", Dumper(@_));
Packit fcad23
Packit fcad23
    # We must know how to delete rows or else this is all pointless.
Packit fcad23
#     return $self->{col_nums}{$tname} if (defined($self->{col_nums}{$tname}));
Packit fcad23
    my $tablemib = $SNMP::MIB{$self->{process_table}};
Packit fcad23
    my $entrymib = $tablemib->{'children'}[0];
Packit fcad23
    my ($delcolumn, $delcolumnname, $delcolumnvalue);
Packit fcad23
Packit fcad23
    foreach my $child  (@{$entrymib->{'children'}}) {
Packit fcad23
	if ($child->{'textualConvention'} eq 'RowStatus') {
Packit fcad23
	    $delcolumn = $child->{subID};
Packit fcad23
	    $delcolumnname = $child->{label};
Packit fcad23
	    $delcolumnvalue = 6; # destroy
Packit fcad23
	    last;
Packit fcad23
	}
Packit fcad23
    }
Packit fcad23
    if (!$delcolumn) {
Packit fcad23
	return warn "Can't (or don't know how to) delete from table $self->{process_table}.  Failing.\n";
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # we should have a session, or else something is really wierd but...
Packit fcad23
    $self->{'sess'} = $self->make_session() if (!$self->{'sess'});
Packit fcad23
Packit fcad23
    # for each key left in our cache, delete it
Packit fcad23
    foreach my $key (keys(%{$self->{'existtest'}})) {
Packit fcad23
	# xxx: fullyqualified oid better
Packit fcad23
	my $vblist = new SNMP::VarList([$delcolumnname, $key,
Packit fcad23
					$delcolumnvalue]);
Packit fcad23
	DEBUG("truncate $key: \n", Dumper($vblist));
Packit fcad23
	$self->{'sess'}->set($vblist) || warn $self->{'sess'}->{ErrorStr};
Packit fcad23
    }
Packit fcad23
    return;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub make_session {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP make_session\n");
Packit fcad23
    my $self = shift;
Packit fcad23
    my @args = @_;
Packit fcad23
    my @sessions;
Packit fcad23
    foreach my $key (qw(SecName Version SecLevel AuthPass Community RemotePort Timeout Retries RetryNoSuch SecEngineId ContextEngineId Context AuthProto PrivProto PrivPass)) {
Packit fcad23
	push @args, $key, $self->{$key} if ($self->{$key});
Packit fcad23
    }
Packit fcad23
    foreach my $host (split(/,\s*/,$self->{DestHost})) {
Packit fcad23
	push @sessions, new SNMP::Session(@args, 'DestHost' => $host);
Packit fcad23
    }
Packit fcad23
    return \@sessions;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub file2str {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP file2str\n");
Packit fcad23
    my ($self, $parser, $uccols) = @_;
Packit fcad23
    my ($cols, @retcols);
Packit fcad23
    DEBUG("file2str\n",Dumper(\@_),"\n");
Packit fcad23
  restart:
Packit fcad23
    if (!$self->{lastnode}) {
Packit fcad23
#	my @vbstuff = @{$parser->{'col_names'}};
Packit fcad23
#	splice (@vbstuff,0,1+$#AnyData::Storage::SNMP::basecols);
Packit fcad23
#	map { $_ = [ $_ ] } @vbstuff;
Packit fcad23
#	$self->{lastnode} = new SNMP::VarList(@vbstuff);
Packit fcad23
#	splice (@$cols,0,1+$#AnyData::Storage::SNMP::basecols);
Packit fcad23
	map { push @$cols,$self->{col_uc_map}{$_} } @$uccols;
Packit fcad23
	if ($#$cols == -1) {
Packit fcad23
	    $cols = $self->{'col_names'};
Packit fcad23
	    # remove base columns
Packit fcad23
	    splice (@$cols,0,1+$#AnyData::Storage::SNMP::basecols);
Packit fcad23
	    # remove not accessible columns
Packit fcad23
	    foreach my $col (@$cols) {
Packit fcad23
		my $mib = $SNMP::MIB{$col};
Packit fcad23
		push @retcols, $col if ($mib->{'access'} =~ /Read|Create/);
Packit fcad23
	    }
Packit fcad23
	} else {
Packit fcad23
	    @retcols = @$cols;
Packit fcad23
	    # remove base columns
Packit fcad23
	    foreach my $c (@AnyData::Storage::SNMP::basecols) {
Packit fcad23
		@retcols = grep(!/^$c$/, @retcols);
Packit fcad23
	    }
Packit fcad23
	    # remove non-accessible columns
Packit fcad23
	    @retcols = grep {$SNMP::MIB{$_}{'access'} =~ /Read|Create/} @retcols;
Packit fcad23
	}
Packit fcad23
	map { $_ = [ $_ ] } @retcols;
Packit fcad23
	$self->{lastnode} = new SNMP::VarList(@retcols);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    if (!$self->{'sess'}) {
Packit fcad23
	$self->{'sess'} = $self->make_session();
Packit fcad23
	if ($#{$self->{'sess'}} == 0 && $self->{'silence_single_host'}) {
Packit fcad23
	    $self->{'hostname'} = '';
Packit fcad23
	} else {
Packit fcad23
	    $self->{'hostname'} = $self->{'sess'}[0]{'DestHost'};
Packit fcad23
	}
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # perform SNMP operation
Packit fcad23
    my $lastnode = $self->{'lastnode'}[0][0];
Packit fcad23
    my $result;
Packit fcad23
    $result = $self->{'sess'}[0]->getnext($self->{lastnode});
Packit fcad23
    if (!defined($result)) {
Packit fcad23
	warn " getnext of $self->{lastnode}[0][0] returned undef\n";
Packit fcad23
    }
Packit fcad23
    DEBUG(" result: ",Dumper($self->{lastnode}),"\n");
Packit fcad23
Packit fcad23
    # XXX: check for holes!
Packit fcad23
Packit fcad23
    # need proper oid compare here for all nodes
Packit fcad23
    if ($self->{'lastnode'}[0][0] ne $lastnode) {
Packit fcad23
	if ($#{$self->{'sess'}} > 0) {
Packit fcad23
	    shift @{$self->{'sess'}};
Packit fcad23
	    delete($self->{'lastnode'});
Packit fcad23
	    @$cols = ();
Packit fcad23
	    $self->{'hostname'} = $self->{'sess'}[0]{'DestHost'} if($self->{'hostname'});
Packit fcad23
	    goto restart;
Packit fcad23
	}
Packit fcad23
	return undef;
Packit fcad23
    }
Packit fcad23
    
Packit fcad23
    # add in basecols information:
Packit fcad23
    my @ret = ($self->{'hostname'}, $self->{'lastnode'}[0][1]);
Packit fcad23
    DEBUG("Dump row results: ",Dumper($self->{'lastnode'}),"\n");
Packit fcad23
Packit fcad23
    # build result array from result varbind contents
Packit fcad23
    map { $ret[$self->{'col_nums'}{$_->[0]}] = map_data($_); } @{$self->{'lastnode'}};
Packit fcad23
Packit fcad23
    # store instance ID for later use if deletion is needed later.
Packit fcad23
    $self->{'existtest'}{$self->{'lastnode'}[0][1]} = 1;
Packit fcad23
Packit fcad23
    DEBUG("Dump row results2: ",Dumper(\@ret),"\n");
Packit fcad23
    return \@ret;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub map_data {
Packit fcad23
    if ($_->[3] eq "OBJECTID") {
Packit fcad23
	$_->[2] = pretty_print_oid(@_);
Packit fcad23
    }
Packit fcad23
    return $_->[2];
Packit fcad23
}
Packit fcad23
Packit fcad23
sub pretty_print_oid {
Packit fcad23
    use NetSNMP::default_store qw(:all);
Packit fcad23
    netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
Packit fcad23
			   NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS,0);
Packit fcad23
    my $new = SNMP::translateObj($_->[2], 0);
Packit fcad23
    netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
Packit fcad23
			   NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS,1);
Packit fcad23
    my $new2 = SNMP::translateObj($_->[2], 0);
Packit fcad23
    if ($new) {
Packit fcad23
	$_->[2] = $new2 . "$new";
Packit fcad23
    } else {
Packit fcad23
	$_->[2] = $_->[0] . $_->[1];
Packit fcad23
    }
Packit fcad23
    return $_->[2];
Packit fcad23
}
Packit fcad23
Packit fcad23
sub push_row {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP push_row\n");
Packit fcad23
    DEBUG("push_row: ",Dumper(\@_),"\n");
Packit fcad23
    DEBUG("push_row\n");
Packit fcad23
    my ($self, $values, $parser, $cols) = @_;
Packit fcad23
    my @callers = caller(3);
Packit fcad23
    my $mode = $callers[3];
Packit fcad23
    if ($mode =~ /DELETE/) {
Packit fcad23
	DEBUG("not deleting $values->[$AnyData::Storage::SNMP::iidptr]\n");
Packit fcad23
	delete $self->{'existtest'}{$values->[$AnyData::Storage::SNMP::iidptr]};
Packit fcad23
	return;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    my @origvars;
Packit fcad23
    if ($#$cols == -1) {
Packit fcad23
	# no column info passed in.  Update everything (mode probably INSERTS).
Packit fcad23
#	@origvars = @{$self->{'col_names'}}};
Packit fcad23
#	splice (@origvars,0,1+$#AnyData::Storage::SNMP::basecols);
Packit fcad23
	
Packit fcad23
	map { push @origvars, $_ if $SNMP::MIB{$_}{'access'} =~ /Write|Create/; } @{$self->{'real_cols'}} ;
Packit fcad23
Packit fcad23
	DEBUG("set cols: ", Dumper(\@origvars));
Packit fcad23
    } else {
Packit fcad23
	# only update the columns in question.  (mode probably UPDATE)
Packit fcad23
	map { push @origvars, $self->{col_uc_map}{$_} } @$cols;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    my @vars;
Packit fcad23
    foreach my $var (@origvars) {
Packit fcad23
	my $access = $SNMP::MIB{$var}{'access'};
Packit fcad23
	# not in this table, probably (hopefully) an index from another:
Packit fcad23
	next if ($SNMP::MIB{$var}{'parent'}{'parent'}{'label'} ne 
Packit fcad23
		 $self->{process_table});
Packit fcad23
	DEBUG("$var -> $access\n");
Packit fcad23
	if ($access =~ /(Write|Create)/) {
Packit fcad23
	    push @vars, $var;
Packit fcad23
	} elsif ($mode eq 'insert') {
Packit fcad23
	    DEBUG("XXX: error if not index\n");
Packit fcad23
	} elsif ($mode eq 'update') {
Packit fcad23
	    DEBUG("update to non-writable column attempted (SNMP error coming)\n");	
Packit fcad23
	}
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # generate index OID component if we don't have it.
Packit fcad23
    if ($values->[$AnyData::Storage::SNMP::iidptr] eq '') {
Packit fcad23
	$values->[$AnyData::Storage::SNMP::iidptr] = 
Packit fcad23
	    $self->make_iid($self->{process_table}, $values);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    # add in values to varbind columns passed in from incoming parameters
Packit fcad23
    my @newvars;
Packit fcad23
    foreach my $v (@vars) {
Packit fcad23
	my $num = $self->{'col_nums'}{$v};
Packit fcad23
	DEBUG("types: $v -> $num -> ", $self->{'col_types'}[$num], 
Packit fcad23
	      " -> val=", $values->[$num], "\n");
Packit fcad23
	next if (!defined($values->[$num]));
Packit fcad23
	# build varbind: column-oid, instance-id, value type, value
Packit fcad23
	push @newvars, [$v, $values->[1], $values->[$num],
Packit fcad23
			$self->{'col_types'}[$num]];
Packit fcad23
    };
Packit fcad23
Packit fcad23
    # create the varbindlist
Packit fcad23
#    print STDERR Dumper(\@newvars);
Packit fcad23
    my $vblist = new SNMP::VarList(@newvars);
Packit fcad23
Packit fcad23
#    print STDERR Dumper($vblist);
Packit fcad23
    DEBUG("set: ", Dumper($vblist));
Packit fcad23
    $self->{'sess'} = $self->make_session() if (!$self->{'sess'});
Packit fcad23
    if (!$self->{'sess'}[0]) {
Packit fcad23
	warn "couldn't create SNMP session";
Packit fcad23
    } elsif (!$self->{'sess'}[0]->set($vblist)) {
Packit fcad23
	my $err = "$self->{process_table}: " . $self->{'sess'}[0]->{ErrorStr};
Packit fcad23
	if ($self->{'sess'}[0]->{ErrorInd}) {
Packit fcad23
	    $err = $err . " (at varbind #" 
Packit fcad23
		. $self->{'sess'}[0]->{ErrorInd}  . " = " ;
Packit fcad23
	    my $dump = Data::Dumper->new([$vblist->[$self->{'sess'}[0]->{ErrorInd} -1]]);
Packit fcad23
	    $err .= $dump->Indent(0)->Terse(1)->Dump;
Packit fcad23
	}
Packit fcad23
	warn $err;
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
sub seek {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP seek\n");
Packit fcad23
    my ($self, $parser) = @_;
Packit fcad23
    DEBUG("seek\n",Dumper(\@_),"\n");
Packit fcad23
}
Packit fcad23
Packit fcad23
sub make_iid {
Packit fcad23
    DEBUG("calling AnyData::Storage::SNMP make_iid\n");
Packit fcad23
    my ($self, $tname, $vals) = @_;
Packit fcad23
    
Packit fcad23
    # Get indexes
Packit fcad23
    my $mib = $SNMP::MIB{$tname};
Packit fcad23
    my $entry = $mib->{'children'}[0];
Packit fcad23
    my $indexes = $entry->{'indexes'};
Packit fcad23
    my $iid;
Packit fcad23
Packit fcad23
    # XXX: implied
Packit fcad23
Packit fcad23
#    print STDERR "INDEXES: ", Dumper($vals),"\n";
Packit fcad23
    foreach my $index (@$indexes) {
Packit fcad23
	warn "A null index value was found, which I doubt is correct." if (!defined($vals->[$self->{col_nums}{$index}]));
Packit fcad23
	my $val = $vals->[$self->{col_nums}{$index}];
Packit fcad23
	my $type = $SNMP::MIB{$index}->{'type'};
Packit fcad23
	DEBUG("index type: $index -> $type -> $val -> " . length($val) . "\n");
Packit fcad23
	if ($type eq "OCTETSTR") {
Packit fcad23
	    $iid .= "." . length($val) . "." . join(".", unpack("c*", $val));
Packit fcad23
	} elsif ($type eq "OBJID") {
Packit fcad23
	    $iid .= "." . (scalar grep(/\./,$val) + 1) . "." . $val;
Packit fcad23
	} else {
Packit fcad23
	    # should be only an INTEGER?
Packit fcad23
	    $iid .= "." . $val;
Packit fcad23
	}
Packit fcad23
    }
Packit fcad23
    DEBUG("made iid: $iid\n");
Packit fcad23
    return $iid;
Packit fcad23
}
Packit fcad23
Packit fcad23
sub DEBUG {
Packit fcad23
    my @info = caller(1);
Packit fcad23
    if ($AnyData::Storage::SNMP::debug
Packit fcad23
	|| ($AnyData::Storage::SNMP::debugre &&
Packit fcad23
	    $_[0] =~ /$AnyData::Storage::SNMP::debugre/)) {
Packit fcad23
	DEBUGIT(\@info, @_);
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
sub DEBUGIT {
Packit fcad23
    my $info;
Packit fcad23
    if (ref($_[0]) eq 'ARRAY') {
Packit fcad23
	$info = shift @_;
Packit fcad23
    } else {
Packit fcad23
	my @y;
Packit fcad23
	my $c=0;
Packit fcad23
	print STDERR "debug chain: ";
Packit fcad23
	for(@y = caller($c); $#y > -1; $c++, @y = caller($c)) {
Packit fcad23
	    print STDERR "  $c: $y[3]\n";
Packit fcad23
	}
Packit fcad23
	my @x = caller(1);
Packit fcad23
	$info = \@x;
Packit fcad23
    }
Packit fcad23
    print STDERR "$info->[3]: ";
Packit fcad23
    print STDERR @_;
Packit fcad23
}
Packit fcad23
Packit fcad23
1;