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 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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user