* initial work * initial work * initial work * some fixes for time and Layer3 weird spec * store again the snapshot after update for specific * resolve the enums * monkeypatch SNMP::translateObj to avoid hardware exception on macOS * only save cache to db in the late phase worker * no need to check for cache on transport, can just go ahead and try * use database only for oidmap instead of netdisco-mibs * rewrite device snapshot to gather loaded mib leafs only * remove old walker code from snapshot worker * allow snmp browser to work without snapshot * only store snapshot leafs which the device responded on * refactor to separate snapshot work from snmp transport work * refactor to separate snapshot work from snmp transport work * allow typeahead on MIB qualified leafs * fixes for snmpwalk input after previous refactor * add the extra stuff SNMP::Info device class uses into snapshot * better width for snmp search box * fix css for snmp options * add spinner while snmp loading * add spinner while snmp loading * add spinner while snmp loading * support SNMP::Info device class or named MIBs as extra on snapshot * add final tidy and bug fix
This commit is contained in:
@@ -19,6 +19,21 @@ BEGIN {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
no warnings 'redefine';
|
||||||
|
use SNMP;
|
||||||
|
|
||||||
|
# hardware exception on macOS at least when translateObj
|
||||||
|
# gets something like '.0.0' passed as arg
|
||||||
|
|
||||||
|
my $orig_translate = *SNMP::translateObj{'CODE'};
|
||||||
|
*SNMP::translateObj = sub {
|
||||||
|
my $arg = $_[0];
|
||||||
|
return undef unless defined $arg and $arg !~ m/^[.0]+$/;
|
||||||
|
return $orig_translate->(@_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
# set up database schema config from simple config vars
|
# set up database schema config from simple config vars
|
||||||
if (ref {} eq ref setting('database')) {
|
if (ref {} eq ref setting('database')) {
|
||||||
# override from env for docker
|
# override from env for docker
|
||||||
|
|||||||
@@ -47,4 +47,6 @@ __PACKAGE__->belongs_to(
|
|||||||
{ join_type => 'RIGHT' }
|
{ join_type => 'RIGHT' }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
__PACKAGE__->belongs_to( oid_fields => 'App::Netdisco::DB::Result::SNMPObject', 'oid' );
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -6,16 +6,12 @@ use Dancer::Plugin::DBIC 'schema';
|
|||||||
use App::Netdisco::Util::SNMP 'get_communities';
|
use App::Netdisco::Util::SNMP 'get_communities';
|
||||||
use App::Netdisco::Util::Device 'get_device';
|
use App::Netdisco::Util::Device 'get_device';
|
||||||
use App::Netdisco::Util::Permission 'acl_matches';
|
use App::Netdisco::Util::Permission 'acl_matches';
|
||||||
|
use App::Netdisco::Util::Snapshot qw/load_cache_for_device add_snmpinfo_aliases/;
|
||||||
|
|
||||||
use SNMP::Info;
|
use SNMP::Info;
|
||||||
use Try::Tiny;
|
use Try::Tiny;
|
||||||
use Module::Load ();
|
use Module::Load ();
|
||||||
use Storable 'thaw';
|
|
||||||
use File::Slurper 'read_text';
|
|
||||||
use MIME::Base64 'decode_base64';
|
|
||||||
use Path::Class 'dir';
|
use Path::Class 'dir';
|
||||||
use File::Path 'make_path';
|
|
||||||
use File::Spec::Functions qw(catdir catfile);
|
|
||||||
use NetAddr::IP::Lite ':lower';
|
use NetAddr::IP::Lite ':lower';
|
||||||
use List::Util qw/pairkeys pairfirst/;
|
use List::Util qw/pairkeys pairfirst/;
|
||||||
|
|
||||||
@@ -64,12 +60,6 @@ sub reader_for {
|
|||||||
my ($class, $ip, $useclass) = @_;
|
my ($class, $ip, $useclass) = @_;
|
||||||
my $device = get_device($ip) or return undef;
|
my $device = get_device($ip) or return undef;
|
||||||
|
|
||||||
my $pseudo_cache = catfile( catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots'), $device->ip );
|
|
||||||
if ($device->in_storage and $device->is_pseudo and ! -f $pseudo_cache) {
|
|
||||||
error sprintf 'transport error - cannot act on pseudo-device [%s] without offline cache', $device->ip;
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $readers = $class->instance->readers or return undef;
|
my $readers = $class->instance->readers or return undef;
|
||||||
return $readers->{$device->ip} if exists $readers->{$device->ip};
|
return $readers->{$device->ip} if exists $readers->{$device->ip};
|
||||||
|
|
||||||
@@ -171,13 +161,13 @@ sub _snmp_connect_generic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# support for offline cache
|
# support for offline cache
|
||||||
my $pseudo_cache = catfile( catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots'), $device->ip );
|
my $cache = load_cache_for_device($device);
|
||||||
if (-f $pseudo_cache and ($device->is_pseudo or ! $device->in_storage)) {
|
if (scalar keys %$cache) {
|
||||||
$snmp_args{Cache} = thaw( decode_base64( read_text($pseudo_cache) ) );
|
$snmp_args{Cache} = $cache;
|
||||||
$snmp_args{Offline} = 1;
|
$snmp_args{Offline} = 1;
|
||||||
# support pseudo/offline device renumber and also pseudo device autovivification
|
# support pseudo/offline device renumber and also pseudo device autovivification
|
||||||
$device->set_column(is_pseudo => \'true') if ! $device->is_pseudo;
|
$device->set_column(is_pseudo => \'true') if not $device->is_pseudo;
|
||||||
debug sprintf 'snmp transport running in offline mode for: [%s]', $device->ip;
|
debug sprintf 'snmp transport running in offline mode for: [%s]', $device->ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
# any net-snmp options to add or override
|
# any net-snmp options to add or override
|
||||||
@@ -229,6 +219,7 @@ sub _snmp_connect_generic {
|
|||||||
my $class = $info->device_type;
|
my $class = $info->device_type;
|
||||||
return $class->new(
|
return $class->new(
|
||||||
%snmp_args, Version => $ver,
|
%snmp_args, Version => $ver,
|
||||||
|
($info->offline ? (Cache => $info->cache) : ()),
|
||||||
_mk_info_commargs($comm),
|
_mk_info_commargs($comm),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -298,6 +289,7 @@ sub _try_connect {
|
|||||||
|
|
||||||
Module::Load::load $class;
|
Module::Load::load $class;
|
||||||
$info = $class->new(%$snmp_args, %comm_args);
|
$info = $class->new(%$snmp_args, %comm_args);
|
||||||
|
add_snmpinfo_aliases($info) if $info->offline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@@ -388,7 +380,7 @@ sub _build_mibdirs {
|
|||||||
|
|
||||||
sub _get_mibdirs_content {
|
sub _get_mibdirs_content {
|
||||||
my $home = shift;
|
my $home = shift;
|
||||||
my @list = map {s|$home/||; $_} grep {m/[a-z0-9]/} grep {-d} glob("$home/*");
|
my @list = map {s|$home/||; $_} grep { m|/[a-z0-9-]+$| } grep {-d} glob("$home/*");
|
||||||
return \@list;
|
return \@list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,9 +166,8 @@ sub is_discoverable {
|
|||||||
$remote_type ||= '';
|
$remote_type ||= '';
|
||||||
$remote_cap ||= [];
|
$remote_cap ||= [];
|
||||||
|
|
||||||
my $pseudo_cache = catfile( catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots'), $device->ip );
|
|
||||||
return _bail_msg("is_discoverable: $device is pseudo-device without offline cache")
|
return _bail_msg("is_discoverable: $device is pseudo-device without offline cache")
|
||||||
if $device->is_pseudo and ! -f $pseudo_cache;
|
if $device->is_pseudo and not $device->oids->count;
|
||||||
|
|
||||||
return _bail_msg("is_discoverable: $device matches wap_platforms but discover_waps is not enabled")
|
return _bail_msg("is_discoverable: $device matches wap_platforms but discover_waps is not enabled")
|
||||||
if ((not setting('discover_waps')) and
|
if ((not setting('discover_waps')) and
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package App::Netdisco::Util::SNMP;
|
|||||||
use Dancer qw/:syntax :script !to_json !from_json/;
|
use Dancer qw/:syntax :script !to_json !from_json/;
|
||||||
use App::Netdisco::Util::DeviceAuth 'get_external_credentials';
|
use App::Netdisco::Util::DeviceAuth 'get_external_credentials';
|
||||||
|
|
||||||
use MIME::Base64 'decode_base64';
|
use File::Spec::Functions qw/splitdir catdir catfile/;
|
||||||
use Storable 'thaw';
|
use MIME::Base64 qw/decode_base64/;
|
||||||
|
use Storable qw/thaw/;
|
||||||
|
use SNMP::Info;
|
||||||
use JSON::PP;
|
use JSON::PP;
|
||||||
|
|
||||||
use base 'Exporter';
|
use base 'Exporter';
|
||||||
@@ -12,9 +14,9 @@ our @EXPORT = ();
|
|||||||
our @EXPORT_OK = qw/
|
our @EXPORT_OK = qw/
|
||||||
get_communities
|
get_communities
|
||||||
snmp_comm_reindex
|
snmp_comm_reindex
|
||||||
sortable_oid
|
|
||||||
decode_and_munge
|
|
||||||
%ALL_MUNGERS
|
%ALL_MUNGERS
|
||||||
|
decode_and_munge
|
||||||
|
sortable_oid
|
||||||
/;
|
/;
|
||||||
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||||
|
|
||||||
@@ -31,24 +33,6 @@ subroutines.
|
|||||||
|
|
||||||
=head1 EXPORT_OK
|
=head1 EXPORT_OK
|
||||||
|
|
||||||
=head2 sortable_oid( $oid, $seglen? )
|
|
||||||
|
|
||||||
Take an OID and return a version of it which is sortable using C<cmp>
|
|
||||||
operator. Works by zero-padding the numeric parts all to be length
|
|
||||||
C<< $seglen >>, which defaults to 6.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
# take oid and make comparable
|
|
||||||
sub sortable_oid {
|
|
||||||
my ($oid, $seglen) = @_;
|
|
||||||
$seglen ||= 6;
|
|
||||||
return $oid if $oid !~ m/^[0-9.]+$/;
|
|
||||||
$oid =~ s/^(\.)//; my $leading = $1;
|
|
||||||
$oid = join '.', map { sprintf("\%0${seglen}d", $_) } (split m/\./, $oid);
|
|
||||||
return (($leading || '') . $oid);
|
|
||||||
}
|
|
||||||
|
|
||||||
=head2 get_communities( $device, $mode )
|
=head2 get_communities( $device, $mode )
|
||||||
|
|
||||||
Takes the current C<device_auth> setting and pushes onto the front of the list
|
Takes the current C<device_auth> setting and pushes onto the front of the list
|
||||||
@@ -247,4 +231,22 @@ sub decode_and_munge {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 sortable_oid( $oid, $seglen? )
|
||||||
|
|
||||||
|
Take an OID and return a version of it which is sortable using C<cmp>
|
||||||
|
operator. Works by zero-padding the numeric parts all to be length
|
||||||
|
C<< $seglen >>, which defaults to 6.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
# take oid and make comparable
|
||||||
|
sub sortable_oid {
|
||||||
|
my ($oid, $seglen) = @_;
|
||||||
|
$seglen ||= 6;
|
||||||
|
return $oid if $oid !~ m/^[0-9.]+$/;
|
||||||
|
$oid =~ s/^(\.)//; my $leading = $1;
|
||||||
|
$oid = join '.', map { sprintf("\%0${seglen}d", $_) } (split m/\./, $oid);
|
||||||
|
return (($leading || '') . $oid);
|
||||||
|
}
|
||||||
|
|
||||||
true;
|
true;
|
||||||
|
|||||||
622
lib/App/Netdisco/Util/Snapshot.pm
Normal file
622
lib/App/Netdisco/Util/Snapshot.pm
Normal file
@@ -0,0 +1,622 @@
|
|||||||
|
package App::Netdisco::Util::Snapshot;
|
||||||
|
|
||||||
|
use Dancer qw/:syntax :script !to_json !from_json/;
|
||||||
|
use Dancer::Plugin::DBIC 'schema';
|
||||||
|
|
||||||
|
use App::Netdisco::Util::SNMP 'sortable_oid';
|
||||||
|
|
||||||
|
use File::Spec::Functions qw/splitdir catdir catfile/;
|
||||||
|
use MIME::Base64 qw/encode_base64 decode_base64/;
|
||||||
|
use File::Slurper qw/read_lines read_text/;
|
||||||
|
use Sub::Util 'subname';
|
||||||
|
use Storable qw/dclone nfreeze thaw/;
|
||||||
|
use Scalar::Util 'blessed';
|
||||||
|
use Module::Load ();
|
||||||
|
use SNMP::Info;
|
||||||
|
|
||||||
|
use base 'Exporter';
|
||||||
|
our @EXPORT = ();
|
||||||
|
our @EXPORT_OK = qw/
|
||||||
|
load_cache_for_device
|
||||||
|
snmpwalk_to_cache
|
||||||
|
|
||||||
|
gather_every_mib_object
|
||||||
|
dump_cache_to_browserdata
|
||||||
|
add_snmpinfo_aliases
|
||||||
|
|
||||||
|
get_oidmap_from_database
|
||||||
|
get_oidmap_from_mibs_files
|
||||||
|
get_mibs_for
|
||||||
|
get_munges
|
||||||
|
/;
|
||||||
|
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
App::Netdisco::Util::Snapshot
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Helper functions for L<SNMP::Info> instances.
|
||||||
|
|
||||||
|
There are no default exports, however the C<:all> tag will export all
|
||||||
|
subroutines.
|
||||||
|
|
||||||
|
=head1 EXPORT_OK
|
||||||
|
|
||||||
|
=head2 load_cache_for_device( $device )
|
||||||
|
|
||||||
|
Tries to find a device cache in database or on disk, or build one from
|
||||||
|
a net-snmp snmpwalk on disk. Returns a cache.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub load_cache_for_device {
|
||||||
|
my $device = shift;
|
||||||
|
return {} unless ($device->is_pseudo or not $device->in_storage);
|
||||||
|
|
||||||
|
# ideally we have a cache in the db
|
||||||
|
if ($device->is_pseudo and my $snapshot = $device->snapshot) {
|
||||||
|
return thaw( decode_base64( $snapshot->cache ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
# or we have a file on disk - could be cache or walk
|
||||||
|
my $pseudo_cache = catfile( catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots'), $device->ip );
|
||||||
|
if (-f $pseudo_cache and not $device->in_storage) {
|
||||||
|
my $content = read_text($pseudo_cache);
|
||||||
|
|
||||||
|
if ($content =~ m/^\.1/) {
|
||||||
|
my %oids = ();
|
||||||
|
|
||||||
|
# parse the snmpwalk output which looks like
|
||||||
|
# .1.0.8802.1.1.2.1.1.1.0 = INTEGER: 30
|
||||||
|
my @lines = split /\n/, $content;
|
||||||
|
foreach my $line (@lines) {
|
||||||
|
my ($oid, $val) = $line =~ m/^(\S+) = (?:[^:]+: )?(.+)$/;
|
||||||
|
next unless $oid and $val;
|
||||||
|
|
||||||
|
# empty string makes the capture go wonky
|
||||||
|
$val = '' if $val =~ m/^[^:]+: ?$/;
|
||||||
|
|
||||||
|
# remove quotes from strings
|
||||||
|
$val =~ s/^"//;
|
||||||
|
$val =~ s/"$//;
|
||||||
|
|
||||||
|
$oids{$oid} = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snmpwalk_to_cache(%oids);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my $cache = thaw( decode_base64( $content ) );
|
||||||
|
return add_snmpinfo_aliases( $cache );
|
||||||
|
}
|
||||||
|
|
||||||
|
# there is a late phase discover worker to generate the oids
|
||||||
|
# and also to save the cache into the database, because we want
|
||||||
|
# to wait for device-specific SNMP::Info class and all its methods.
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 snmpwalk_to_cache ( %oids )
|
||||||
|
|
||||||
|
Take the snmpwalk of the device which is numeric (no MIB translateObj),
|
||||||
|
resolve to MIB identifiers using netdisco-mibs data, then return as an
|
||||||
|
SNMP::Info instance cache.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub snmpwalk_to_cache {
|
||||||
|
my %oids = @_;
|
||||||
|
return () unless scalar keys %oids;
|
||||||
|
|
||||||
|
my %oidmap = reverse get_oidmap_from_database();
|
||||||
|
my %leaves = ();
|
||||||
|
|
||||||
|
OID: foreach my $orig_oid (keys %oids) {
|
||||||
|
my $oid = $orig_oid;
|
||||||
|
my $idx = '';
|
||||||
|
|
||||||
|
while (length($oid) and !exists $oidmap{$oid}) {
|
||||||
|
$oid =~ s/\.(\d+)$//;
|
||||||
|
$idx = ((defined $idx and length $idx) ? "${1}.${idx}" : $1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exists $oidmap{$oid}) {
|
||||||
|
$idx =~ s/^\.//;
|
||||||
|
my $qleaf = $oidmap{$oid};
|
||||||
|
my $key = $oid .'~~'. $qleaf;
|
||||||
|
|
||||||
|
if ($idx eq 0) {
|
||||||
|
$leaves{$key} = $oids{$orig_oid};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# on rare occasions a vendor returns .0 and .something
|
||||||
|
delete $leaves{$key}
|
||||||
|
if defined $leaves{$key} and ref q{} eq $leaves{$key};
|
||||||
|
$leaves{$key}->{$idx} = $oids{$orig_oid};
|
||||||
|
}
|
||||||
|
|
||||||
|
# debug "snapshot $device - cached $oidmap{$oid}($idx) from $orig_oid";
|
||||||
|
next OID;
|
||||||
|
}
|
||||||
|
|
||||||
|
# this is not too surprising
|
||||||
|
# debug sprintf "cache builder - error: missing OID %s in netdisco-mibs", $orig_oid;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $info = SNMP::Info->new({
|
||||||
|
Offline => 1,
|
||||||
|
Cache => {},
|
||||||
|
AutoSpecify => 0,
|
||||||
|
Session => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach my $attr (keys %leaves) {
|
||||||
|
my ($oid, $qleaf) = split m/~~/, $attr;
|
||||||
|
my $val = $leaves{$attr};
|
||||||
|
|
||||||
|
# resolve the enums if needed
|
||||||
|
my $row = schema('netdisco')->resultset('SNMPObject')->find($oid);
|
||||||
|
if ($row and $row->enum) {
|
||||||
|
my %emap = map { reverse split m/\(/ }
|
||||||
|
map { s/\)//; $_ }
|
||||||
|
@{ $row->enum };
|
||||||
|
|
||||||
|
if (ref q{} eq ref $val) {
|
||||||
|
$val = $emap{$val} if exists $emap{$val};
|
||||||
|
}
|
||||||
|
elsif (ref {} eq ref $val) {
|
||||||
|
foreach my $k (keys %$val) {
|
||||||
|
$val->{$k} = $emap{ $val->{$k} }
|
||||||
|
if exists $emap{ $val->{$k} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $leaf = $qleaf;
|
||||||
|
$leaf =~ s/.+:://;
|
||||||
|
|
||||||
|
my $snmpqleaf = $qleaf;
|
||||||
|
$snmpqleaf =~ s/[-:]/_/g;
|
||||||
|
|
||||||
|
# do we need this ?? $info->_cache($oid, $leaves{$attr});
|
||||||
|
$info->_cache($leaf, $leaves{$attr});
|
||||||
|
$info->_cache($snmpqleaf, $leaves{$attr});
|
||||||
|
}
|
||||||
|
|
||||||
|
debug sprintf "snmpwalk_to_cache: cache size: %d", scalar keys %{ $info->cache };
|
||||||
|
|
||||||
|
# inject a basic set of SNMP::Info globals and funcs aliases
|
||||||
|
# which are needed for initial device discovery
|
||||||
|
add_snmpinfo_aliases($info);
|
||||||
|
|
||||||
|
return $info->cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 gather_every_mib_object( $device, $snmp, @extramibs? )
|
||||||
|
|
||||||
|
Gathers evey MIB Object in the MIBs loaded for the device and store
|
||||||
|
to the database for SNMP browser.
|
||||||
|
|
||||||
|
Optionally add a list of vendors, MIBs, or SNMP:Info class for extra MIB
|
||||||
|
Objects from the netdisco-mibs bundle.
|
||||||
|
|
||||||
|
The passed SNMP::Info instance has its cache update with the data.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub gather_every_mib_object {
|
||||||
|
my ($device, $snmp, @extra) = @_;
|
||||||
|
|
||||||
|
# get MIBs loaded for device
|
||||||
|
my @mibs = keys %{ $snmp->mibs() };
|
||||||
|
my @extra_mibs = get_mibs_for(@extra);
|
||||||
|
debug sprintf "-> covering %d MIBs", (scalar @mibs + scalar @extra_mibs);
|
||||||
|
SNMP::loadModules($_) for @extra_mibs;
|
||||||
|
|
||||||
|
# get qualified leafs for those MIBs from snmp_object
|
||||||
|
my %oidmap = get_oidmap_from_database(@mibs, @extra_mibs);
|
||||||
|
debug sprintf "-> gathering %d MIB Objects", scalar keys %oidmap;
|
||||||
|
|
||||||
|
foreach my $qleaf (sort {sortable_oid($oidmap{$a}) cmp sortable_oid($oidmap{$b})} keys %oidmap) {
|
||||||
|
my $leaf = $qleaf;
|
||||||
|
$leaf =~ s/.+:://;
|
||||||
|
|
||||||
|
my $snmpqleaf = $qleaf;
|
||||||
|
$snmpqleaf =~ s/[-:]/_/g;
|
||||||
|
|
||||||
|
# gather the leaf
|
||||||
|
$snmp->$snmpqleaf;
|
||||||
|
|
||||||
|
# skip any leaf which did not return data from device
|
||||||
|
# this works for both funcs and globals as funcs create stub global
|
||||||
|
next unless exists $snmp->cache->{'_'. $snmpqleaf};
|
||||||
|
|
||||||
|
# store a short name alias which is needed for netdisco actions
|
||||||
|
$snmp->_cache($leaf, $snmp->$snmpqleaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 dump_cache_to_browserdata( $device, $snmp )
|
||||||
|
|
||||||
|
Dumps any valid MIB leaf from the passed SNMP::Info instance's cache into
|
||||||
|
the Netdisco database SNMP Browser table.
|
||||||
|
|
||||||
|
Ideally the leafs are fully qualified, but if not then a best effort will
|
||||||
|
be made to find their correct MIB.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub dump_cache_to_browserdata {
|
||||||
|
my ($device, $snmp) = @_;
|
||||||
|
|
||||||
|
my %qoidmap = get_oidmap_from_database();
|
||||||
|
my %oidmap = get_leaf_to_qleaf_map();
|
||||||
|
my %munges = get_munges($snmp);
|
||||||
|
|
||||||
|
my $cache = $snmp->cache;
|
||||||
|
my %oids = ();
|
||||||
|
|
||||||
|
foreach my $key (keys %$cache) {
|
||||||
|
next unless $key and $key =~ m/^_/;
|
||||||
|
|
||||||
|
my $snmpqleaf = $key;
|
||||||
|
$snmpqleaf =~ s/^_//;
|
||||||
|
|
||||||
|
my $qleaf = $snmpqleaf;
|
||||||
|
$qleaf =~ s/__/::/;
|
||||||
|
$qleaf =~ s/_/-/g;
|
||||||
|
|
||||||
|
my $leaf = $qleaf;
|
||||||
|
$leaf =~ s/.+:://;
|
||||||
|
|
||||||
|
next unless exists $qoidmap{$qleaf}
|
||||||
|
or (exists $oidmap{$leaf} and exists $qoidmap{ $oidmap{$leaf} });
|
||||||
|
|
||||||
|
my $oid = exists $qoidmap{$qleaf} ? $qoidmap{$qleaf} : $qoidmap{ $oidmap{$leaf} };
|
||||||
|
my $data = exists $cache->{'store'}{$snmpqleaf} ? $cache->{'store'}{$snmpqleaf}
|
||||||
|
: $cache->{$key};
|
||||||
|
next unless defined $data;
|
||||||
|
|
||||||
|
push @{ $oids{$oid} }, {
|
||||||
|
oid => $oid,
|
||||||
|
oid_parts => [ grep {length} (split m/\./, $oid) ],
|
||||||
|
leaf => $leaf,
|
||||||
|
qleaf => $qleaf,
|
||||||
|
munge => ($munges{$snmpqleaf} || $munges{$leaf}),
|
||||||
|
value => encode_base64( nfreeze( [$data] ) ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
%oids = map { ($_ => [sort {length($b->{qleaf}) <=> length($a->{qleaf})} @{ $oids{$_} }]) }
|
||||||
|
keys %oids;
|
||||||
|
|
||||||
|
schema('netdisco')->txn_do(sub {
|
||||||
|
my $gone = $device->oids->delete;
|
||||||
|
debug sprintf '-> removed %d oids from db', $gone;
|
||||||
|
$device->oids->populate([ sort {sortable_oid($a->{oid}) cmp sortable_oid($b->{oid})}
|
||||||
|
map { delete $_->{qleaf}; $_ }
|
||||||
|
map { $oids{$_}->[0] } keys %oids ]);
|
||||||
|
debug sprintf '-> added %d new oids to db', scalar keys %oids;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 add_snmpinfo_aliases( $snmp_info_instance | $snmp_info_cache )
|
||||||
|
|
||||||
|
Add in any GLOBALS and FUNCS aliases from the SNMP::Info device class
|
||||||
|
or else a set of defaults that allow device discovery. Returns the cache.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub add_snmpinfo_aliases {
|
||||||
|
my $info = shift or return {};
|
||||||
|
|
||||||
|
if (not blessed $info) {
|
||||||
|
$info = SNMP::Info->new({
|
||||||
|
Offline => 1,
|
||||||
|
Cache => $info,
|
||||||
|
AutoSpecify => 0,
|
||||||
|
Session => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
my %globals = %{ $info->globals };
|
||||||
|
my %funcs = %{ $info->funcs };
|
||||||
|
|
||||||
|
while (my ($alias, $leaf) = each %globals) {
|
||||||
|
next if $leaf =~ m/\.\d+$/;
|
||||||
|
$info->_cache($alias, $info->$leaf) if $info->$leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (my ($alias, $leaf) = each %funcs) {
|
||||||
|
$info->_cache($alias, dclone $info->$leaf) if ref q{} ne ref $info->$leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SNMP::Info::Layer3 has some weird aliases we can fix here
|
||||||
|
$info->_cache('serial1', $info->chassisId->{''}) if ref {} eq ref $info->chassisId;
|
||||||
|
$info->_cache('router_ip', $info->ospfRouterId->{''}) if ref {} eq ref $info->ospfRouterId;
|
||||||
|
$info->_cache('bgp_id', $info->bgpIdentifier->{''}) if ref {} eq ref $info->bgpIdentifier;
|
||||||
|
$info->_cache('bgp_local_as', $info->bgpLocalAs->{''}) if ref {} eq ref $info->bgpLocalAs;
|
||||||
|
$info->_cache('sysUpTime', $info->sysUpTimeInstance->{''}) if ref {} eq ref $info->sysUpTimeInstance
|
||||||
|
and not $info->sysUpTime;
|
||||||
|
$info->_cache('mac', $info->ifPhysAddress->{1}) if ref {} eq ref $info->ifPhysAddress;
|
||||||
|
|
||||||
|
# now for any other SNMP::Info method in GLOBALS or FUNCS which Netdisco
|
||||||
|
# might call, but will not have data, we fake a cache entry to avoid
|
||||||
|
# throwing errors
|
||||||
|
|
||||||
|
while (my $method = <DATA>) {
|
||||||
|
$method =~ s/\s//g;
|
||||||
|
next unless length $method and not $info->$method;
|
||||||
|
|
||||||
|
$info->_cache($method, '') if exists $globals{$method};
|
||||||
|
$info->_cache($method, {}) if exists $funcs{$method};
|
||||||
|
}
|
||||||
|
|
||||||
|
debug sprintf "add_snmpinfo_aliases: cache size: %d", scalar keys %{ $info->cache };
|
||||||
|
return $info->cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 get_leaf_to_qleaf_map( )
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_leaf_to_qleaf_map {
|
||||||
|
debug "-> loading database leaf to qleaf map";
|
||||||
|
|
||||||
|
my %oidmap = map { ( $_->{leaf} => (join '::', $_->{mib}, $_->{leaf}) ) }
|
||||||
|
schema('netdisco')->resultset('SNMPObject')
|
||||||
|
->search({
|
||||||
|
num_children => 0,
|
||||||
|
leaf => { '!~' => 'anonymous#\d+$' },
|
||||||
|
-or => [
|
||||||
|
type => { '<>' => '' },
|
||||||
|
access => { '~' => '^(read|write)' },
|
||||||
|
\'oid_parts[array_length(oid_parts,1)] = 0'
|
||||||
|
],
|
||||||
|
},{columns => [qw/mib leaf/], order_by => 'oid_parts'})
|
||||||
|
->hri->all;
|
||||||
|
|
||||||
|
debug sprintf "-> loaded %d mapped objects", scalar keys %oidmap;
|
||||||
|
return %oidmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 get_oidmap_from_database( @mibs? )
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_oidmap_from_database {
|
||||||
|
my @mibs = @_;
|
||||||
|
debug "-> loading netdisco-mibs object cache (database)";
|
||||||
|
|
||||||
|
my %oidmap = map { ((join '::', $_->{mib}, $_->{leaf}) => $_->{oid}) }
|
||||||
|
schema('netdisco')->resultset('SNMPObject')
|
||||||
|
->search({
|
||||||
|
(scalar @mibs ? (mib => { -in => \@mibs }) : ()),
|
||||||
|
num_children => 0,
|
||||||
|
leaf => { '!~' => 'anonymous#\d+$' },
|
||||||
|
-or => [
|
||||||
|
type => { '<>' => '' },
|
||||||
|
access => { '~' => '^(read|write)' },
|
||||||
|
\'oid_parts[array_length(oid_parts,1)] = 0'
|
||||||
|
],
|
||||||
|
},{columns => [qw/mib oid leaf/], order_by => 'oid_parts'})
|
||||||
|
->hri->all;
|
||||||
|
|
||||||
|
if (not scalar @mibs) {
|
||||||
|
debug sprintf "-> loaded %d MIB objects", scalar keys %oidmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return %oidmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 get_oidmap_from_mibs_files( @vendors? )
|
||||||
|
|
||||||
|
Read in netdisco-mibs translation report and make an OID -> leafname map this
|
||||||
|
is an older version of get_oidmap which uses disk file test on my laptop shows
|
||||||
|
this version is four seconds and the database takes two.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_oidmap_from_mibs_files {
|
||||||
|
debug "-> loading netdisco-mibs object cache (netdisco-mibs)";
|
||||||
|
|
||||||
|
my $home = (setting('mibhome') || catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'netdisco-mibs'));
|
||||||
|
my $reports = catdir( $home, 'EXTRAS', 'reports' );
|
||||||
|
my @maps = map { (splitdir($_))[-1] }
|
||||||
|
grep { ! m/^(?:EXTRAS)$/ }
|
||||||
|
grep { ! m/\./ }
|
||||||
|
grep { -f }
|
||||||
|
glob (catfile( $reports, '*_oids' ));
|
||||||
|
|
||||||
|
my @report = ();
|
||||||
|
push @report, read_lines( catfile( $reports, $_ ), 'latin-1' )
|
||||||
|
for (qw(rfc_oids net-snmp_oids cisco_oids), @maps);
|
||||||
|
|
||||||
|
my %oidmap = ();
|
||||||
|
foreach my $line (@report) {
|
||||||
|
my ($oid, $qual_leaf, $rest) = split m/,/, $line;
|
||||||
|
next unless defined $oid and defined $qual_leaf;
|
||||||
|
next if exists $oidmap{$oid};
|
||||||
|
my ($mib, $leaf) = split m/::/, $qual_leaf;
|
||||||
|
$oidmap{$oid} = $leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug sprintf "-> loaded %d MIB objects",
|
||||||
|
scalar keys %oidmap;
|
||||||
|
return %oidmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 get_mibs_for( @extra )
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_mibs_for {
|
||||||
|
my @extra = @_;
|
||||||
|
my @mibs = ();
|
||||||
|
|
||||||
|
my $home = (setting('mibhome') || catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'netdisco-mibs'));
|
||||||
|
my $cachedir = catdir( $home, 'EXTRAS', 'indexes', 'cache' );
|
||||||
|
|
||||||
|
foreach my $item (@extra) {
|
||||||
|
next unless $item;
|
||||||
|
if ($item =~ m/^[a-z0-9-]+$/) {
|
||||||
|
push @mibs, map { (split m/\s+/)[0] }
|
||||||
|
read_lines( catfile( $cachedir, $item ), 'latin-1' );
|
||||||
|
}
|
||||||
|
elsif ($item =~ m/::/) {
|
||||||
|
Module::Load::load $item;
|
||||||
|
$item .= '::MIBS';
|
||||||
|
{
|
||||||
|
no strict 'refs';
|
||||||
|
push @mibs, keys %${item};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
push @mibs, $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @mibs;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 get_munges( $snmpinfo )
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_munges {
|
||||||
|
my $snmp = shift;
|
||||||
|
my %munge_set = ();
|
||||||
|
|
||||||
|
my %munge = %{ $snmp->munge() };
|
||||||
|
my %funcs = %{ $snmp->funcs() };
|
||||||
|
my %globals = %{ $snmp->globals() };
|
||||||
|
|
||||||
|
while (my ($alias, $leaf) = each %globals) {
|
||||||
|
$munge_set{$leaf} = subname($munge{$leaf}) if exists $munge{$leaf};
|
||||||
|
$munge_set{$leaf} = subname($munge{$alias}) if exists $munge{$alias};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (my ($alias, $leaf) = each %funcs) {
|
||||||
|
$munge_set{$leaf} = subname($munge{$leaf}) if exists $munge{$leaf};
|
||||||
|
$munge_set{$leaf} = subname($munge{$alias}) if exists $munge{$alias};
|
||||||
|
}
|
||||||
|
|
||||||
|
return %munge_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
true;
|
||||||
|
|
||||||
|
__DATA__
|
||||||
|
agg_ports
|
||||||
|
at_paddr
|
||||||
|
bgp_peer_addr
|
||||||
|
bp_index
|
||||||
|
c_cap
|
||||||
|
c_id
|
||||||
|
c_if
|
||||||
|
c_ip
|
||||||
|
c_platform
|
||||||
|
c_port
|
||||||
|
cd11_mac
|
||||||
|
cd11_port
|
||||||
|
cd11_rateset
|
||||||
|
cd11_rxbyte
|
||||||
|
cd11_rxpkt
|
||||||
|
cd11_sigqual
|
||||||
|
cd11_sigstrength
|
||||||
|
cd11_ssid
|
||||||
|
cd11_txbyte
|
||||||
|
cd11_txpkt
|
||||||
|
cd11_txrate
|
||||||
|
cd11_uptime
|
||||||
|
class
|
||||||
|
contact
|
||||||
|
docs_if_cmts_cm_status_inet_address
|
||||||
|
dot11_cur_tx_pwr_mw
|
||||||
|
e_class
|
||||||
|
e_descr
|
||||||
|
e_fru
|
||||||
|
e_fwver
|
||||||
|
e_hwver
|
||||||
|
e_index
|
||||||
|
e_model
|
||||||
|
e_name
|
||||||
|
e_parent
|
||||||
|
e_pos
|
||||||
|
e_serial
|
||||||
|
e_swver
|
||||||
|
e_type
|
||||||
|
eigrp_peers
|
||||||
|
fw_mac
|
||||||
|
fw_port
|
||||||
|
has_topo
|
||||||
|
i_80211channel
|
||||||
|
i_alias
|
||||||
|
i_description
|
||||||
|
i_duplex
|
||||||
|
i_duplex_admin
|
||||||
|
i_err_disable_cause
|
||||||
|
i_faststart_enabled
|
||||||
|
i_ignore
|
||||||
|
i_lastchange
|
||||||
|
i_mac
|
||||||
|
i_mtu
|
||||||
|
i_name
|
||||||
|
i_speed
|
||||||
|
i_speed_admin
|
||||||
|
i_speed_raw
|
||||||
|
i_ssidbcast
|
||||||
|
i_ssidlist
|
||||||
|
i_ssidmac
|
||||||
|
i_stp_state
|
||||||
|
i_type
|
||||||
|
i_up
|
||||||
|
i_up_admin
|
||||||
|
i_vlan
|
||||||
|
i_vlan_membership
|
||||||
|
i_vlan_membership_untagged
|
||||||
|
i_vlan_type
|
||||||
|
interfaces
|
||||||
|
ip_index
|
||||||
|
ip_netmask
|
||||||
|
ipv6_addr
|
||||||
|
ipv6_addr_prefixlength
|
||||||
|
ipv6_index
|
||||||
|
ipv6_n2p_mac
|
||||||
|
ipv6_type
|
||||||
|
isis_peers
|
||||||
|
lldp_ipv6
|
||||||
|
lldp_media_cap
|
||||||
|
lldp_rem_model
|
||||||
|
lldp_rem_serial
|
||||||
|
lldp_rem_sw_rev
|
||||||
|
lldp_rem_vendor
|
||||||
|
location
|
||||||
|
model
|
||||||
|
name
|
||||||
|
ospf_peer_id
|
||||||
|
ospf_peers
|
||||||
|
peth_port_admin
|
||||||
|
peth_port_class
|
||||||
|
peth_port_ifindex
|
||||||
|
peth_port_power
|
||||||
|
peth_port_status
|
||||||
|
peth_power_status
|
||||||
|
peth_power_watts
|
||||||
|
ports
|
||||||
|
qb_fw_vlan
|
||||||
|
serial
|
||||||
|
serial1
|
||||||
|
snmpEngineID
|
||||||
|
snmpEngineTime
|
||||||
|
snmp_comm
|
||||||
|
snmp_ver
|
||||||
|
v_index
|
||||||
|
v_name
|
||||||
|
vrf_name
|
||||||
|
vtp_d_name
|
||||||
|
vtp_version
|
||||||
@@ -41,7 +41,7 @@ ajax '/ajax/data/device/:ip/snmptree/:base' => require_login sub {
|
|||||||
children => \0,
|
children => \0,
|
||||||
state => { disabled => \1 },
|
state => { disabled => \1 },
|
||||||
icon => 'icon-search',
|
icon => 'icon-search',
|
||||||
}] unless schema(vars->{'tenant'})->resultset('DeviceSnapshot')->find($device->ip);
|
}] unless $device->oids->count;
|
||||||
|
|
||||||
return to_json [{
|
return to_json [{
|
||||||
text => 'No MIB data. Please run `~/bin/netdisco-do loadmibs`.',
|
text => 'No MIB data. Please run `~/bin/netdisco-do loadmibs`.',
|
||||||
@@ -62,12 +62,19 @@ ajax '/ajax/data/snmp/typeahead' => require_login sub {
|
|||||||
my $table = ($deviceonly ? 'DeviceBrowser' : 'SNMPObject');
|
my $table = ($deviceonly ? 'DeviceBrowser' : 'SNMPObject');
|
||||||
|
|
||||||
my @found = schema(vars->{'tenant'})->resultset($table)
|
my @found = schema(vars->{'tenant'})->resultset($table)
|
||||||
->search({ -or => [ oid => $term,
|
->search({ -or => [ 'me.oid' => $term,
|
||||||
oid => { -like => ($term .'.%') },
|
'me.oid' => { -like => ($term .'.%') },
|
||||||
leaf => { -ilike => ('%'. $term .'%') } ],
|
'me.leaf' => { -ilike => ('%'. $term .'%') } ],
|
||||||
(($deviceonly and $device) ? (ip => $device) : ()), },
|
(($deviceonly and $device) ? (ip => $device) : ()), },
|
||||||
{ rows => 25, columns => 'leaf', order_by => 'oid_parts' })
|
{ select => [
|
||||||
->get_column('leaf')->all;
|
(($deviceonly and $device) ? \q{ oid_fields.mib || '::' || me.leaf }
|
||||||
|
: \q{ me.mib || '::' || me.leaf }),
|
||||||
|
],
|
||||||
|
as => ['qleaf'],
|
||||||
|
(($deviceonly and $device) ? (join => 'oid_fields') : ()),
|
||||||
|
rows => 25, order_by => 'me.oid_parts' })
|
||||||
|
->get_column('qleaf')->all;
|
||||||
|
|
||||||
return to_json [] unless scalar @found;
|
return to_json [] unless scalar @found;
|
||||||
|
|
||||||
content_type 'application/json';
|
content_type 'application/json';
|
||||||
@@ -87,10 +94,12 @@ ajax '/ajax/data/snmp/nodesearch' => require_login sub {
|
|||||||
{ rows => 1, order_by => 'oid_parts' })->first;
|
{ rows => 1, order_by => 'oid_parts' })->first;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
my ($mib, $leaf) = split m/::/, $to_match;
|
||||||
$found = schema(vars->{'tenant'})->resultset('SNMPObject')
|
$found = schema(vars->{'tenant'})->resultset('SNMPObject')
|
||||||
->search({ -or => [ oid => $to_match,
|
->search({
|
||||||
leaf => $to_match ] },
|
(($mib and $leaf) ? (-and => [mib => $mib, leaf => $leaf])
|
||||||
{ rows => 1, order_by => 'oid_parts' })->first;
|
: (-or => [oid => $to_match, leaf => { -ilike => $to_match }])),
|
||||||
|
},{ rows => 1, order_by => 'oid_parts' })->first;
|
||||||
}
|
}
|
||||||
return to_json [] unless $found;
|
return to_json [] unless $found;
|
||||||
|
|
||||||
@@ -148,6 +157,8 @@ sub _get_snmp_data {
|
|||||||
|
|
||||||
my @items = map {{
|
my @items = map {{
|
||||||
id => $_,
|
id => $_,
|
||||||
|
mib => $meta{$_}->{mib}, # accessed via node.original.mib
|
||||||
|
leaf => $meta{$_}->{leaf}, # accessed via node.original.leaf
|
||||||
text => ($meta{$_}->{leaf} .' ('. $meta{$_}->{oid_parts}->[-1] .')'),
|
text => ($meta{$_}->{leaf} .' ('. $meta{$_}->{oid_parts}->[-1] .')'),
|
||||||
|
|
||||||
($meta{$_}->{browser} ? (icon => 'icon-folder-close text-info')
|
($meta{$_}->{browser} ? (icon => 'icon-folder-close text-info')
|
||||||
|
|||||||
34
lib/App/Netdisco/Worker/Plugin/Discover/Snapshot.pm
Normal file
34
lib/App/Netdisco/Worker/Plugin/Discover/Snapshot.pm
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package App::Netdisco::Worker::Plugin::Discover::Snapshot;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::DBIC 'schema';
|
||||||
|
|
||||||
|
use App::Netdisco::Worker::Plugin;
|
||||||
|
use App::Netdisco::Transport::SNMP ();
|
||||||
|
use App::Netdisco::Util::Snapshot 'dump_cache_to_browserdata';
|
||||||
|
|
||||||
|
use Storable 'nfreeze';
|
||||||
|
use MIME::Base64 'encode_base64';
|
||||||
|
|
||||||
|
use aliased 'App::Netdisco::Worker::Status';
|
||||||
|
|
||||||
|
register_worker({ phase => 'late' }, sub {
|
||||||
|
my ($job, $workerconf) = @_;
|
||||||
|
my $device = $job->device;
|
||||||
|
|
||||||
|
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||||
|
or return Status->defer("discover failed: could not SNMP connect to $device");
|
||||||
|
|
||||||
|
return unless $device->in_storage
|
||||||
|
and not $device->oids->count and $snmp->offline;
|
||||||
|
|
||||||
|
dump_cache_to_browserdata( $device, $snmp );
|
||||||
|
|
||||||
|
my $frozen = encode_base64( nfreeze( $snmp->cache ) );
|
||||||
|
$device->update_or_create_related('snapshot', { cache => $frozen });
|
||||||
|
|
||||||
|
return Status
|
||||||
|
->info(sprintf ' [%s] discover - oids and cache stored', $device);
|
||||||
|
});
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -5,16 +5,17 @@ use App::Netdisco::Worker::Plugin;
|
|||||||
use aliased 'App::Netdisco::Worker::Status';
|
use aliased 'App::Netdisco::Worker::Status';
|
||||||
|
|
||||||
use App::Netdisco::Transport::SNMP;
|
use App::Netdisco::Transport::SNMP;
|
||||||
use App::Netdisco::Util::SNMP 'sortable_oid';
|
use App::Netdisco::Util::Snapshot qw/
|
||||||
use Dancer::Plugin::DBIC 'schema';
|
gather_every_mib_object
|
||||||
|
dump_cache_to_browserdata
|
||||||
|
add_snmpinfo_aliases
|
||||||
|
/;
|
||||||
|
|
||||||
use File::Spec::Functions qw(splitdir catdir catfile);
|
use MIME::Base64 qw/encode_base64/;
|
||||||
use MIME::Base64 'encode_base64';
|
use Storable qw/nfreeze/;
|
||||||
use File::Slurper qw(read_lines write_text);
|
use File::Spec::Functions qw(catdir catfile);
|
||||||
|
use File::Slurper 'write_text';
|
||||||
use File::Path 'make_path';
|
use File::Path 'make_path';
|
||||||
use Sub::Util 'subname';
|
|
||||||
use Storable qw(dclone nfreeze);
|
|
||||||
# use DDP;
|
|
||||||
|
|
||||||
register_worker({ phase => 'check' }, sub {
|
register_worker({ phase => 'check' }, sub {
|
||||||
return Status->error('Missing device (-d).')
|
return Status->error('Missing device (-d).')
|
||||||
@@ -26,458 +27,42 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
|
|||||||
my ($job, $workerconf) = @_;
|
my ($job, $workerconf) = @_;
|
||||||
my $device = $job->device;
|
my $device = $job->device;
|
||||||
|
|
||||||
my $save_browser = $job->extra;
|
if (not ($device->in_storage
|
||||||
my $save_file = $job->port;
|
and not $device->is_pseudo)) {
|
||||||
|
return Status->error('Can only snapshot a real discovered device.');
|
||||||
|
}
|
||||||
|
|
||||||
# needed to avoid $var being returned with leafname and breaking loop checks
|
|
||||||
$SNMP::use_numeric = 1;
|
|
||||||
|
|
||||||
# might restore a cache if there's one on disk
|
|
||||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||||
or return Status->defer("snapshot failed: could not SNMP connect to $device");
|
or return Status->defer("snapshot failed: could not SNMP connect to $device");
|
||||||
|
|
||||||
my %oidmap = getoidmap($device, $snmp);
|
if ($snmp->offline) {
|
||||||
my %munges = get_munges($snmp);
|
return Status->error('Can only snapshot a real device.');
|
||||||
|
|
||||||
# only if not pseudo device
|
|
||||||
if (not $device->is_pseudo) {
|
|
||||||
my $walk_error = walk_and_store($device, $snmp, %oidmap);
|
|
||||||
return $walk_error if $walk_error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# load the cache
|
gather_every_mib_object( $device, $snmp, split m/,/, ($job->extra || '') );
|
||||||
my %cache = %{ $snmp->cache() };
|
add_snmpinfo_aliases($snmp);
|
||||||
|
dump_cache_to_browserdata( $device, $snmp );
|
||||||
|
|
||||||
# finally, freeze the cache, then base64 encode, store in the DB,
|
if ($job->port) {
|
||||||
# optionally store browsing data, and optionally save file.
|
my $frozen = encode_base64( nfreeze( $snmp->cache ) );
|
||||||
|
|
||||||
if ($save_browser) {
|
if ($job->port =~ m/^(?:both|db)$/) {
|
||||||
debug "snapshot $device - cacheing snapshot for browsing";
|
debug "snapshot $device - saving snapshot to database";
|
||||||
my %seenoid = ();
|
$device->update_or_create_related('snapshot', { cache => $frozen });
|
||||||
|
}
|
||||||
|
|
||||||
my @browser = map {{
|
if ($job->port =~ m/^(?:both|file)$/) {
|
||||||
oid => $_,
|
my $target_dir = catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots');
|
||||||
oid_parts => [ grep {length} (split m/\./, $_) ],
|
make_path($target_dir);
|
||||||
leaf => $oidmap{$_},
|
my $target_file = catfile($target_dir, $device->ip);
|
||||||
munge => $munges{ $oidmap{$_} },
|
|
||||||
value => do { my $m = $oidmap{$_}; encode_base64( nfreeze( [$snmp->$m] ) ); },
|
|
||||||
}} sort {sortable_oid($a) cmp sortable_oid($b)}
|
|
||||||
grep {not $seenoid{$_}++}
|
|
||||||
grep {m/^\.1/}
|
|
||||||
map {s/^_//; $_}
|
|
||||||
keys %cache;
|
|
||||||
|
|
||||||
schema('netdisco')->txn_do(sub {
|
debug "snapshot $device - saving snapshot to $target_file";
|
||||||
my $gone = $device->oids->delete;
|
write_text($target_file, $frozen);
|
||||||
debug sprintf 'snapshot %s - removed %d oids from db',
|
}
|
||||||
$device->ip, $gone;
|
|
||||||
$device->oids->populate(\@browser);
|
|
||||||
debug sprintf 'snapshot %s - added %d new oids to db',
|
|
||||||
$device->ip, scalar @browser;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
debug "snapshot $device - cacheing snapshot bundle";
|
|
||||||
my $frozen = encode_base64( nfreeze( \%cache ) );
|
|
||||||
$device->update_or_create_related('snapshot', {cache => $frozen});
|
|
||||||
|
|
||||||
if ($save_file) {
|
|
||||||
my $target_dir = catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'logs', 'snapshots');
|
|
||||||
make_path($target_dir);
|
|
||||||
my $target_file = catfile($target_dir, $device->ip);
|
|
||||||
debug "snapshot $device - saving snapshot to $target_file";
|
|
||||||
write_text($target_file, $frozen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status->done(
|
return Status->done(
|
||||||
sprintf "Snapshot data captured from %s", $device->ip);
|
sprintf "Snapshot data captured from %s", $device->ip);
|
||||||
});
|
});
|
||||||
|
|
||||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
# read in netdisco-mibs translation report and make an OID -> leafname map
|
|
||||||
sub getoidmap {
|
|
||||||
my ($device, $snmp) = @_;
|
|
||||||
debug "snapshot $device - loading netdisco-mibs object cache";
|
|
||||||
|
|
||||||
my $home = (setting('mibhome') || catdir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'netdisco-mibs'));
|
|
||||||
my $reports = catdir( $home, 'EXTRAS', 'reports' );
|
|
||||||
my @maps = map { (splitdir($_))[-1] }
|
|
||||||
grep { ! m/^(?:EXTRAS)$/ }
|
|
||||||
grep { ! m/\./ }
|
|
||||||
grep { -f }
|
|
||||||
glob (catfile( $reports, '*_oids' ));
|
|
||||||
|
|
||||||
my @report = ();
|
|
||||||
push @report, read_lines( catfile( $reports, $_ ), 'latin-1' )
|
|
||||||
for (qw(rfc_oids net-snmp_oids cisco_oids), @maps);
|
|
||||||
|
|
||||||
my %oidmap = ();
|
|
||||||
foreach my $line (@report) {
|
|
||||||
my ($oid, $qual_leaf, $rest) = split m/,/, $line;
|
|
||||||
next unless defined $oid and defined $qual_leaf;
|
|
||||||
next if exists $oidmap{$oid};
|
|
||||||
my ($mib, $leaf) = split m/::/, $qual_leaf;
|
|
||||||
$oidmap{$oid} = $leaf;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug sprintf "snapshot $device - loaded %d objects from netdisco-mibs",
|
|
||||||
scalar keys %oidmap;
|
|
||||||
return %oidmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_munges {
|
|
||||||
my $snmp = shift;
|
|
||||||
my %munge_set = ();
|
|
||||||
|
|
||||||
my %munge = %{ $snmp->munge() };
|
|
||||||
my %funcs = %{ $snmp->funcs() };
|
|
||||||
my %globals = %{ $snmp->globals() };
|
|
||||||
|
|
||||||
while (my ($alias, $leaf) = each %globals) {
|
|
||||||
$munge_set{$leaf} = subname($munge{$leaf}) if exists $munge{$leaf};
|
|
||||||
$munge_set{$leaf} = subname($munge{$alias}) if exists $munge{$alias};
|
|
||||||
}
|
|
||||||
|
|
||||||
while (my ($alias, $leaf) = each %funcs) {
|
|
||||||
$munge_set{$leaf} = subname($munge{$leaf}) if exists $munge{$leaf};
|
|
||||||
$munge_set{$leaf} = subname($munge{$alias}) if exists $munge{$alias};
|
|
||||||
}
|
|
||||||
|
|
||||||
return %munge_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub walk_and_store {
|
|
||||||
my ($device, $snmp, %oidmap) = @_;
|
|
||||||
|
|
||||||
my $walk = {
|
|
||||||
%{ walker($device, $snmp, '.1.0.8802.1.1') },
|
|
||||||
%{ walker($device, $snmp, '.1.2.840.10006.300.43') },
|
|
||||||
%{ walker($device, $snmp, '.1.3.6.1') },
|
|
||||||
%{ walker($device, $snmp, '.1.3.111.2.802') },
|
|
||||||
};
|
|
||||||
# my %walk = walker($device, $snmp, '.1.3.6.1.2.1.2.2.1.6'); # 22 rows, i_mac/ifPhysAddress
|
|
||||||
|
|
||||||
# something went wrong - error
|
|
||||||
return $walk if ref {} ne ref $walk;
|
|
||||||
|
|
||||||
# take the snmpwalk of the device which is numeric (no MIB translateObj),
|
|
||||||
# resolve to MIB identifiers using netdisco-mibs, then store in SNMP::Info
|
|
||||||
# instance cache
|
|
||||||
|
|
||||||
my (%tables, %leaves, @realoids) = ((), (), ());
|
|
||||||
OID: foreach my $orig_oid (keys %$walk) {
|
|
||||||
my $oid = $orig_oid;
|
|
||||||
my $idx = '';
|
|
||||||
|
|
||||||
while (length($oid) and !exists $oidmap{$oid}) {
|
|
||||||
$oid =~ s/\.(\d+)$//;
|
|
||||||
$idx = ((defined $idx and length $idx) ? "${1}.${idx}" : $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exists $oidmap{$oid}) {
|
|
||||||
$idx =~ s/^\.//;
|
|
||||||
my $leaf = $oidmap{$oid};
|
|
||||||
|
|
||||||
if ($idx eq 0) {
|
|
||||||
push @realoids, $oid;
|
|
||||||
$leaves{ $leaf } = $walk->{$orig_oid};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
push @realoids, $oid if !exists $tables{ $leaf };
|
|
||||||
$tables{ $leaf }->{$idx} = $walk->{$orig_oid};
|
|
||||||
}
|
|
||||||
|
|
||||||
# debug "snapshot $device - cached $oidmap{$oid}($idx) from $orig_oid";
|
|
||||||
next OID;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug "snapshot $device - missing OID $orig_oid in netdisco-mibs";
|
|
||||||
}
|
|
||||||
|
|
||||||
$snmp->_cache($_, $leaves{$_}) for keys %leaves;
|
|
||||||
$snmp->_cache($_, $tables{$_}) for keys %tables;
|
|
||||||
|
|
||||||
# add in any GLOBALS and FUNCS aliases which users have created in the
|
|
||||||
# SNMP::Info device class, with binary copy of data so that it can be frozen
|
|
||||||
|
|
||||||
my %cache = %{ $snmp->cache() };
|
|
||||||
my %funcs = %{ $snmp->funcs() };
|
|
||||||
my %globals = %{ $snmp->globals() };
|
|
||||||
|
|
||||||
while (my ($alias, $leaf) = each %globals) {
|
|
||||||
if (exists $cache{"_$leaf"} and !exists $cache{"_$alias"}) {
|
|
||||||
$snmp->_cache($alias, $cache{"_$leaf"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (my ($alias, $leaf) = each %funcs) {
|
|
||||||
if (exists $cache{store}->{$leaf} and !exists $cache{store}->{$alias}) {
|
|
||||||
$snmp->_cache($alias, dclone $cache{store}->{$leaf});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# now for any other SNMP::Info method in GLOBALS or FUNCS which Netdisco
|
|
||||||
# might call, but will not have data, we fake a cache entry to avoid
|
|
||||||
# throwing errors
|
|
||||||
|
|
||||||
# refresh the cache
|
|
||||||
%cache = %{ $snmp->cache() };
|
|
||||||
|
|
||||||
while (my $method = <DATA>) {
|
|
||||||
$method =~ s/\s//g;
|
|
||||||
next unless length $method and !exists $cache{"_$method"};
|
|
||||||
|
|
||||||
$snmp->_cache($method, {}) if exists $funcs{$method};
|
|
||||||
$snmp->_cache($method, '') if exists $globals{$method};
|
|
||||||
}
|
|
||||||
|
|
||||||
# put into the cache an oid ref to each leaf name
|
|
||||||
# this allows rebuild of browser data from a frozen cache
|
|
||||||
foreach my $oid (@realoids) {
|
|
||||||
my $leaf = $oidmap{$oid} or next;
|
|
||||||
$snmp->_cache($oid, $snmp->$leaf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
# taken from SNMP::Info and adjusted to work on walks outside a single table
|
|
||||||
sub walker {
|
|
||||||
my ($device, $snmp, $base) = @_;
|
|
||||||
$base ||= '.1';
|
|
||||||
|
|
||||||
my $sess = $snmp->session();
|
|
||||||
return unless defined $sess;
|
|
||||||
|
|
||||||
my $REPEATERS = 20;
|
|
||||||
my $ver = $snmp->snmp_ver();
|
|
||||||
|
|
||||||
# debug "snapshot $device - $base translated as $qual_leaf";
|
|
||||||
my $var = SNMP::Varbind->new( [$base] );
|
|
||||||
|
|
||||||
# So devices speaking SNMP v.1 are not supposed to give out
|
|
||||||
# data from SNMP2, but most do. Net-SNMP, being very precise
|
|
||||||
# will tell you that the SNMP OID doesn't exist for the device.
|
|
||||||
# They have a flag RetryNoSuch that is used for get() operations,
|
|
||||||
# but not for getnext(). We set this flag normally, and if we're
|
|
||||||
# using V1, let's try and fetch the data even if we get one of those.
|
|
||||||
|
|
||||||
my %localstore = ();
|
|
||||||
my $errornum = 0;
|
|
||||||
my %seen = ();
|
|
||||||
|
|
||||||
my $vars = [];
|
|
||||||
my $bulkwalk_no
|
|
||||||
= $snmp->can('bulkwalk_no') ? $snmp->bulkwalk_no() : 0;
|
|
||||||
my $bulkwalk_on = defined $snmp->{BulkWalk} ? $snmp->{BulkWalk} : 1;
|
|
||||||
my $can_bulkwalk = $bulkwalk_on && !$bulkwalk_no;
|
|
||||||
my $repeaters = $snmp->{BulkRepeaters} || $REPEATERS;
|
|
||||||
my $bulkwalk = $can_bulkwalk && $ver != 1;
|
|
||||||
my $loopdetect
|
|
||||||
= defined $snmp->{LoopDetect} ? $snmp->{LoopDetect} : 1;
|
|
||||||
|
|
||||||
debug "snapshot $device - starting walk from $base";
|
|
||||||
|
|
||||||
# Use BULKWALK if we can because its faster
|
|
||||||
if ( $bulkwalk && @$vars == 0 ) {
|
|
||||||
($vars) = $sess->bulkwalk( 0, $repeaters, $var );
|
|
||||||
if ( $sess->{ErrorNum} ) {
|
|
||||||
debug "snapshot $device BULKWALK " . $sess->{ErrorStr};
|
|
||||||
debug "snapshot $device disabling BULKWALK and trying again...";
|
|
||||||
$vars = [];
|
|
||||||
$bulkwalk = 0;
|
|
||||||
$snmp->{BulkWalk} = 0;
|
|
||||||
undef $sess->{ErrorNum};
|
|
||||||
undef $sess->{ErrorStr};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( !$errornum ) {
|
|
||||||
if ($bulkwalk) {
|
|
||||||
$var = shift @$vars or last;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# GETNEXT instead of BULKWALK
|
|
||||||
# debug "snapshot $device GETNEXT $var";
|
|
||||||
my @x = $sess->getnext($var);
|
|
||||||
$errornum = $sess->{ErrorNum};
|
|
||||||
}
|
|
||||||
|
|
||||||
my $iid = $var->[1];
|
|
||||||
my $val = $var->[2];
|
|
||||||
my $oid = $var->[0] . (defined $iid ? ".${iid}" : '');
|
|
||||||
|
|
||||||
# debug "snapshot $device reading $oid";
|
|
||||||
# use DDP; p $var;
|
|
||||||
|
|
||||||
unless ( defined $iid ) {
|
|
||||||
error "snapshot $device not here";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if last element, V2 devices may report ENDOFMIBVIEW even if
|
|
||||||
# instance or object doesn't exist.
|
|
||||||
if ( $val eq 'ENDOFMIBVIEW' ) {
|
|
||||||
debug "snapshot $device : ENDOFMIBVIEW";
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Similarly for SNMPv1 - noSuchName return results in both $iid
|
|
||||||
# and $val being empty strings.
|
|
||||||
if ( $val eq '' and $iid eq '' ) {
|
|
||||||
debug "snapshot $device : v1 noSuchName (1)";
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Another check for SNMPv1 - noSuchName return may results in an $oid
|
|
||||||
# we've already seen and $val an empty string. If we don't catch
|
|
||||||
# this here we erroneously report a loop below.
|
|
||||||
if ( defined $seen{$oid} and $seen{$oid} and $val eq '' ) {
|
|
||||||
debug "snapshot $device : v1 noSuchName (2)";
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($loopdetect) {
|
|
||||||
# Check to see if we've already seen this IID (looping)
|
|
||||||
if ( defined $seen{$oid} and $seen{$oid} ) {
|
|
||||||
debug "snapshot $device : looping on $oid";
|
|
||||||
shift @$vars;
|
|
||||||
$var = shift @$vars or last;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$seen{$oid}++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $val eq 'NOSUCHOBJECT' ) {
|
|
||||||
error "snapshot $device : NOSUCHOBJECT";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
if ( $val eq 'NOSUCHINSTANCE' ) {
|
|
||||||
error "snapshot $device : NOSUCHINSTANCE";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
# debug "snapshot $device - retreived $oid : $val";
|
|
||||||
$localstore{$oid} = $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug sprintf "snapshot $device - walked %d rows from $base",
|
|
||||||
scalar keys %localstore;
|
|
||||||
return \%localstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
true;
|
true;
|
||||||
|
|
||||||
__DATA__
|
|
||||||
agg_ports
|
|
||||||
at_paddr
|
|
||||||
bgp_peer_addr
|
|
||||||
bp_index
|
|
||||||
c_cap
|
|
||||||
c_id
|
|
||||||
c_if
|
|
||||||
c_ip
|
|
||||||
c_platform
|
|
||||||
c_port
|
|
||||||
cd11_mac
|
|
||||||
cd11_port
|
|
||||||
cd11_rateset
|
|
||||||
cd11_rxbyte
|
|
||||||
cd11_rxpkt
|
|
||||||
cd11_sigqual
|
|
||||||
cd11_sigstrength
|
|
||||||
cd11_ssid
|
|
||||||
cd11_txbyte
|
|
||||||
cd11_txpkt
|
|
||||||
cd11_txrate
|
|
||||||
cd11_uptime
|
|
||||||
class
|
|
||||||
contact
|
|
||||||
docs_if_cmts_cm_status_inet_address
|
|
||||||
dot11_cur_tx_pwr_mw
|
|
||||||
e_class
|
|
||||||
e_descr
|
|
||||||
e_fru
|
|
||||||
e_fwver
|
|
||||||
e_hwver
|
|
||||||
e_index
|
|
||||||
e_model
|
|
||||||
e_name
|
|
||||||
e_parent
|
|
||||||
e_pos
|
|
||||||
e_serial
|
|
||||||
e_swver
|
|
||||||
e_type
|
|
||||||
eigrp_peers
|
|
||||||
fw_mac
|
|
||||||
fw_port
|
|
||||||
has_topo
|
|
||||||
i_80211channel
|
|
||||||
i_alias
|
|
||||||
i_description
|
|
||||||
i_duplex
|
|
||||||
i_duplex_admin
|
|
||||||
i_err_disable_cause
|
|
||||||
i_faststart_enabled
|
|
||||||
i_ignore
|
|
||||||
i_lastchange
|
|
||||||
i_mac
|
|
||||||
i_mtu
|
|
||||||
i_name
|
|
||||||
i_speed
|
|
||||||
i_speed_admin
|
|
||||||
i_speed_raw
|
|
||||||
i_ssidbcast
|
|
||||||
i_ssidlist
|
|
||||||
i_ssidmac
|
|
||||||
i_stp_state
|
|
||||||
i_type
|
|
||||||
i_up
|
|
||||||
i_up_admin
|
|
||||||
i_vlan
|
|
||||||
i_vlan_membership
|
|
||||||
i_vlan_membership_untagged
|
|
||||||
i_vlan_type
|
|
||||||
interfaces
|
|
||||||
ip_index
|
|
||||||
ip_netmask
|
|
||||||
ipv6_addr
|
|
||||||
ipv6_addr_prefixlength
|
|
||||||
ipv6_index
|
|
||||||
ipv6_n2p_mac
|
|
||||||
ipv6_type
|
|
||||||
isis_peers
|
|
||||||
lldp_ipv6
|
|
||||||
lldp_media_cap
|
|
||||||
lldp_rem_model
|
|
||||||
lldp_rem_serial
|
|
||||||
lldp_rem_sw_rev
|
|
||||||
lldp_rem_vendor
|
|
||||||
location
|
|
||||||
model
|
|
||||||
name
|
|
||||||
ospf_peer_id
|
|
||||||
ospf_peers
|
|
||||||
peth_port_admin
|
|
||||||
peth_port_class
|
|
||||||
peth_port_ifindex
|
|
||||||
peth_port_power
|
|
||||||
peth_port_status
|
|
||||||
peth_power_status
|
|
||||||
peth_power_watts
|
|
||||||
ports
|
|
||||||
qb_fw_vlan
|
|
||||||
serial
|
|
||||||
serial1
|
|
||||||
snmpEngineID
|
|
||||||
snmpEngineTime
|
|
||||||
snmp_comm
|
|
||||||
snmp_ver
|
|
||||||
v_index
|
|
||||||
v_name
|
|
||||||
vrf_name
|
|
||||||
vtp_d_name
|
|
||||||
vtp_version
|
|
||||||
|
|||||||
@@ -525,6 +525,7 @@ worker_plugins:
|
|||||||
- 'Discover::PortProperties::PortAccessEntity'
|
- 'Discover::PortProperties::PortAccessEntity'
|
||||||
- 'Discover::Properties'
|
- 'Discover::Properties'
|
||||||
- 'Discover::Properties::Tags'
|
- 'Discover::Properties::Tags'
|
||||||
|
- 'Discover::Snapshot'
|
||||||
- 'Discover::VLANs'
|
- 'Discover::VLANs'
|
||||||
- 'Discover::Wireless'
|
- 'Discover::Wireless'
|
||||||
- 'Discover::WithNodes'
|
- 'Discover::WithNodes'
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ div.content > div.tab-content table.nd_floatinghead thead {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#snmpPartialSearch {
|
.nd_snmp_search_param {
|
||||||
margin-top: -3px;
|
margin-top: -3px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fake looks for form submit buttons embedded in bootstrap dropdowns */
|
/* fake looks for form submit buttons embedded in bootstrap dropdowns */
|
||||||
|
|||||||
@@ -5,17 +5,31 @@
|
|||||||
<div id="snmpnodecontainer" class="span8">
|
<div id="snmpnodecontainer" class="span8">
|
||||||
<form id="nd_snmp_search_form" class="form-inline col-md-4">
|
<form id="nd_snmp_search_form" class="form-inline col-md-4">
|
||||||
<span class="form-group">
|
<span class="form-group">
|
||||||
<input id="nd_snmp_search_text" type="text" class="form-control nd_snmp_search_param" name="term" size="30" required placeholder="Search for label or OID">
|
<i id="nd_snmp_loading_spinner" class="icon-circle-blank icon-large"></i>
|
||||||
|
|
||||||
|
|
||||||
|
<input id="nd_snmp_search_text" type="text"
|
||||||
|
class="form-control nd_snmp_search_param span5"
|
||||||
|
name="term" required placeholder="Search for label or OID">
|
||||||
|
|
||||||
<button type="submit" class="btn btn-default">Search</button>
|
<button type="submit" class="btn btn-default">Search</button>
|
||||||
|
|
||||||
|
|
||||||
<label class="checkbox-inline"
|
<label class="checkbox-inline"
|
||||||
rel="tooltip" data-placement="top" data-offset="5" data-title="Anchored to the beginning">
|
rel="tooltip" data-placement="top" data-offset="5"
|
||||||
<input type="checkbox" id="nd_snmp_search_partial" name="partial" class="nd_snmp_search_param"> Partial </input>
|
data-title="Anchored to the beginning">
|
||||||
|
|
||||||
|
<input type="checkbox" id="nd_snmp_search_partial"
|
||||||
|
name="partial" class="nd_snmp_search_param"> Partial </input>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input type="checkbox" id="nd_snmp_search_deviceonly" name="deviceonly" class="nd_snmp_search_param" checked="checked"> Only this device </input>
|
|
||||||
<input type="hidden" id="nd_snmp_search_ip" name="ip" class="nd_snmp_search_param" value="[% device %]" />
|
<input type="checkbox" id="nd_snmp_search_deviceonly"
|
||||||
|
name="deviceonly" class="nd_snmp_search_param"
|
||||||
|
checked="checked"> Only this device </input>
|
||||||
|
|
||||||
|
<input type="hidden" id="nd_snmp_search_ip"
|
||||||
|
name="ip" class="nd_snmp_search_param" value="[% device %]" />
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</form>
|
||||||
<div id="node">
|
<div id="node">
|
||||||
@@ -74,15 +88,23 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function () {
|
$(function () {
|
||||||
var jstree_search_callback = function(str, node) {
|
var jstree_search_callback = function(str, node) {
|
||||||
var pattern = str.toLowerCase();
|
var mib = str.replace(/::.+/,'');
|
||||||
|
var leaf = str.replace(/.+::/,'');
|
||||||
|
|
||||||
|
var pattern = str.replace(/.+::/,'').toLowerCase();
|
||||||
var name = node.text.toLowerCase();
|
var name = node.text.toLowerCase();
|
||||||
var oid = node.id.toLowerCase();
|
var oid = node.id.toLowerCase();
|
||||||
|
|
||||||
if (document.getElementById('nd_snmp_search_partial').checked) {
|
if (document.getElementById('nd_snmp_search_partial').checked) {
|
||||||
if ((name.indexOf(pattern) == 0) || (oid.indexOf(pattern) == 0)) {
|
if ((name.indexOf(pattern) == 0) || (oid.indexOf(pattern) == 0)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (mib.length && leaf.length && mib != leaf) {
|
||||||
|
if ((mib == node.original.mib) && (leaf == node.original.leaf)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if ((name.indexOf(pattern + ' ') == 0) || (oid == pattern)) {
|
if ((name.indexOf(pattern + ' ') == 0) || (oid == pattern)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -110,10 +132,18 @@
|
|||||||
'ajax' : {
|
'ajax' : {
|
||||||
'url' : '[% uri_base | none %]/ajax/data/snmp/nodesearch',
|
'url' : '[% uri_base | none %]/ajax/data/snmp/nodesearch',
|
||||||
'beforeSend' : function(jqXHR, settings) {
|
'beforeSend' : function(jqXHR, settings) {
|
||||||
|
$('#nd_snmp_loading_spinner').removeClass('icon-circle-blank icon-exclamation-sign text-success')
|
||||||
|
.addClass('icon-spinner text-warning icon-spin');
|
||||||
|
|
||||||
if (document.getElementById('nd_snmp_search_partial').checked) {
|
if (document.getElementById('nd_snmp_search_partial').checked) {
|
||||||
settings.url = settings.url + '&partial=on';
|
settings.url = settings.url + '&partial=on';
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
'error' : function() {
|
||||||
|
$('#nd_snmp_loading_spinner').removeClass('icon-spinner text-warning icon-spin')
|
||||||
|
.addClass('icon-exclamation-sign');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'search_callback' : jstree_search_callback
|
'search_callback' : jstree_search_callback
|
||||||
@@ -140,6 +170,9 @@
|
|||||||
var path = $('#jstree').jstree().get_path(node[0], false, true);
|
var path = $('#jstree').jstree().get_path(node[0], false, true);
|
||||||
var parent = path[path.length - 2];
|
var parent = path[path.length - 2];
|
||||||
document.getElementById( parent ).scrollIntoView();
|
document.getElementById( parent ).scrollIntoView();
|
||||||
|
|
||||||
|
$('#nd_snmp_loading_spinner').removeClass('icon-spinner text-warning icon-spin')
|
||||||
|
.addClass('icon-circle-blank text-success');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("#nd_snmp_search_form").submit(function(e) {
|
$("#nd_snmp_search_form").submit(function(e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user