implementation of find_neighbors
This commit is contained in:
@@ -5,12 +5,14 @@ use Dancer::Plugin::DBIC 'schema';
|
|||||||
|
|
||||||
use App::Netdisco::Util::DNS 'hostname_from_ip';
|
use App::Netdisco::Util::DNS 'hostname_from_ip';
|
||||||
use NetAddr::IP::Lite ':lower';
|
use NetAddr::IP::Lite ':lower';
|
||||||
|
use Try::Tiny;
|
||||||
|
|
||||||
use base 'Exporter';
|
use base 'Exporter';
|
||||||
our @EXPORT = ();
|
our @EXPORT = ();
|
||||||
our @EXPORT_OK = qw/
|
our @EXPORT_OK = qw/
|
||||||
store_device store_interfaces store_wireless
|
store_device store_interfaces store_wireless
|
||||||
store_vlans store_power store_modules
|
store_vlans store_power store_modules
|
||||||
|
find_neighbors
|
||||||
/;
|
/;
|
||||||
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||||
|
|
||||||
@@ -471,4 +473,155 @@ sub store_modules {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 find_neighbors( $device, $snmp )
|
||||||
|
|
||||||
|
Given a Device database object, and a working SNMP connection, discover and
|
||||||
|
store the device's port neighbors information.
|
||||||
|
|
||||||
|
If any neighbor is unknown to Netdisco, a discover job for it will immediately
|
||||||
|
be queued (modulo configuration file C<discover_no_type> setting).
|
||||||
|
|
||||||
|
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||||
|
not yet stored to the database.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub find_neighbors {
|
||||||
|
my ($device, $snmp) = @_;
|
||||||
|
|
||||||
|
my $c_ip = $snmp->c_ip;
|
||||||
|
unless ($snmp->hasCDP or scalar keys %$c_ip) {
|
||||||
|
# TODO log
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $interfaces = $snmp->interfaces;
|
||||||
|
my $c_if = $snmp->c_if;
|
||||||
|
my $c_port = $snmp->c_port;
|
||||||
|
my $c_id = $snmp->c_id;
|
||||||
|
my $c_platform = $snmp->c_platform;
|
||||||
|
|
||||||
|
foreach my $entry (keys %$c_ip) {
|
||||||
|
my $port = $interfaces->{ $c_ip->{$entry} };
|
||||||
|
if (!defined $port) {
|
||||||
|
# TODO log
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $remote_ip = $c_ip->{$entry};
|
||||||
|
my $remote_ipad = NetAddr::IP::Lite->new($remote_ip);
|
||||||
|
my $remote_port = undef;
|
||||||
|
my $remote_type = $c_platform->{$entry};
|
||||||
|
my $remote_id = $c_id->{$entry};
|
||||||
|
|
||||||
|
next unless length $remote_ip;
|
||||||
|
|
||||||
|
# a bunch of heuristics to search known devices if we don't have a
|
||||||
|
# useable remote IP...
|
||||||
|
|
||||||
|
if ($remote_ip eq '0.0.0.0' or
|
||||||
|
$remote_ipad->within(NetAddr::IP::Lite->new('127.0.0.0/8'))) {
|
||||||
|
|
||||||
|
if ($remote_id) {
|
||||||
|
my $devices = schema('netdisco')->resultset('Device');
|
||||||
|
my $neigh = $devices->single({name => $remote_id});
|
||||||
|
# TODO log
|
||||||
|
|
||||||
|
if (!defined $neigh) {
|
||||||
|
(my $shortid = $remote_id) =~ s/\..*//;
|
||||||
|
$neigh = $devices->single({name => { -ilike => "${shortid}%" }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($neigh) {
|
||||||
|
$remote_ip = $neigh->ip;
|
||||||
|
# TODO log
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# TODO log
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# TODO log
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# hack for devices seeing multiple neighbors on the port
|
||||||
|
if (ref [] eq ref $remote_ip) {
|
||||||
|
foreach my $n (@$remote_ip) {
|
||||||
|
# TODO log
|
||||||
|
_enqueue_discover($n, $remote_type);
|
||||||
|
}
|
||||||
|
# set loopback as remote IP to suppress any further work
|
||||||
|
$remote_ip = $device->ip;
|
||||||
|
$remote_port = $port;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$remote_port = $c_port->{$entry};
|
||||||
|
|
||||||
|
if (defined $remote_port) {
|
||||||
|
# clean weird characters
|
||||||
|
$remote_port =~ s/[^\d\/\.,()\w:-]+//gi;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# TODO log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# XXX too custom? IP Phone detection
|
||||||
|
if (defined $remote_type and $remote_type =~ m/(mitel.5\d{3})/i) {
|
||||||
|
$remote_type = 'IP Phone - '. $remote_type
|
||||||
|
if $remote_type !~ /ip phone/i;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $portrow = schema('netdisco')->resultset('DevicePort')
|
||||||
|
->single({ip => $device->ip, port => $port});
|
||||||
|
|
||||||
|
if (!defined $portrow) {
|
||||||
|
# TODO log
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$portrow->update({
|
||||||
|
remote_ip => $remote_ip,
|
||||||
|
remote_port => $remote_port,
|
||||||
|
remote_type => $remote_type,
|
||||||
|
remote_id => $remote_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
_enqueue_discover($remote_ip, $remote_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# only enqueue if device is not already discovered, and
|
||||||
|
# discover_no_type config permits the discovery
|
||||||
|
sub _enqueue_discover {
|
||||||
|
my ($ip, $remote_type) = @_;
|
||||||
|
|
||||||
|
my $device = get_device($ip);
|
||||||
|
return if $device->in_storage;
|
||||||
|
|
||||||
|
# XXX should this be checked by process _taking_ the job?
|
||||||
|
# ok.. the job will sit queued, but nothing will ever action it.
|
||||||
|
# but that could still tie up workers :-(
|
||||||
|
#
|
||||||
|
my $remote_type_match = setting('discover_no_type');
|
||||||
|
if ($remote_type and $remote_type_match
|
||||||
|
and $remote_type =~ m/$remote_type_match/) {
|
||||||
|
# TODO log
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
# could fail if queued job already exists
|
||||||
|
schema('netdisco')->resultset('Admin')->create({
|
||||||
|
device => $ip,
|
||||||
|
action => 'discover',
|
||||||
|
status => 'queued',
|
||||||
|
});
|
||||||
|
# TODO log
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
Reference in New Issue
Block a user