implementation of find_neighbors

This commit is contained in:
Oliver Gorwits
2013-04-04 22:22:50 +01:00
parent 03c4d8ef09
commit 965990786f

View File

@@ -5,12 +5,14 @@ use Dancer::Plugin::DBIC 'schema';
use App::Netdisco::Util::DNS 'hostname_from_ip';
use NetAddr::IP::Lite ':lower';
use Try::Tiny;
use base 'Exporter';
our @EXPORT = ();
our @EXPORT_OK = qw/
store_device store_interfaces store_wireless
store_vlans store_power store_modules
find_neighbors
/;
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;