move discover and discoverall to worker plugins
This commit is contained in:
@@ -1,100 +0,0 @@
|
||||
package App::Netdisco::Backend::Worker::Poller::Device;
|
||||
|
||||
use Dancer qw/:moose :syntax :script/;
|
||||
|
||||
use App::Netdisco::Transport::SNMP;
|
||||
use App::Netdisco::Util::Device qw/get_device is_discoverable_now/;
|
||||
use App::Netdisco::Core::Discover ':all';
|
||||
use App::Netdisco::Backend::Util ':all';
|
||||
use App::Netdisco::JobQueue qw/jq_queued jq_insert/;
|
||||
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use NetAddr::IP::Lite ':lower';
|
||||
|
||||
use Role::Tiny;
|
||||
use namespace::clean;
|
||||
|
||||
# queue a discover job for all devices known to Netdisco
|
||||
sub discoverall {
|
||||
my ($self, $job) = @_;
|
||||
|
||||
my %queued = map {$_ => 1} jq_queued('discover');
|
||||
my @devices = schema('netdisco')->resultset('Device')->search({
|
||||
-or => [ 'vendor' => undef, 'vendor' => { '!=' => 'netdisco' }],
|
||||
})->get_column('ip')->all;
|
||||
my @filtered_devices = grep {!exists $queued{$_}} @devices;
|
||||
|
||||
jq_insert([
|
||||
map {{
|
||||
device => $_,
|
||||
action => 'discover',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
}} (@filtered_devices)
|
||||
]);
|
||||
|
||||
return job_done("Queued discover job for all devices");
|
||||
}
|
||||
|
||||
# run a discover job for one device, and its *new* neighbors
|
||||
sub discover {
|
||||
my ($self, $job) = @_;
|
||||
|
||||
my $device = get_device($job->device)
|
||||
or return job_error(
|
||||
"discover failed: unable to interpret device parameter: "
|
||||
. ($job->device || "''"));
|
||||
my $host = $device->ip;
|
||||
|
||||
if ($device->ip eq '0.0.0.0') {
|
||||
return job_error("discover failed: no device param (need -d ?)");
|
||||
}
|
||||
|
||||
if ($device->in_storage
|
||||
and $device->vendor and $device->vendor eq 'netdisco') {
|
||||
return job_done("discover skipped: $host is pseudo-device");
|
||||
}
|
||||
|
||||
unless (is_discoverable_now($device)) {
|
||||
return job_defer("discover deferred: $host is not discoverable");
|
||||
}
|
||||
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device);
|
||||
if (!defined $snmp) {
|
||||
return job_defer("discover failed: could not SNMP connect to $host");
|
||||
}
|
||||
|
||||
store_device($device, $snmp);
|
||||
set_canonical_ip($device, $snmp); # must come after store_device
|
||||
store_interfaces($device, $snmp);
|
||||
store_wireless($device, $snmp);
|
||||
store_vlans($device, $snmp);
|
||||
store_power($device, $snmp);
|
||||
store_modules($device, $snmp) if setting('store_modules');
|
||||
discover_new_neighbors($device, $snmp);
|
||||
|
||||
# if requested, and the device has not yet been arpniped/macsucked, queue now
|
||||
if ($device->in_storage and $job->subaction and $job->subaction eq 'with-nodes') {
|
||||
if (!defined $device->last_macsuck) {
|
||||
jq_insert({
|
||||
device => $device->ip,
|
||||
action => 'macsuck',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
});
|
||||
}
|
||||
|
||||
if (!defined $device->last_arpnip) {
|
||||
jq_insert({
|
||||
device => $device->ip,
|
||||
action => 'arpnip',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return job_done("Ended discover for $host");
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,996 +0,0 @@
|
||||
package App::Netdisco::Core::Discover;
|
||||
|
||||
use Dancer qw/:syntax :script/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
use App::Netdisco::Util::Device
|
||||
qw/get_device match_devicetype is_discoverable/;
|
||||
use App::Netdisco::Util::Permission qw/check_acl_only check_acl_no/;
|
||||
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
|
||||
use App::Netdisco::Util::DNS ':all';
|
||||
use App::Netdisco::JobQueue qw/jq_queued jq_insert/;
|
||||
use NetAddr::IP::Lite ':lower';
|
||||
use List::MoreUtils ();
|
||||
use Scalar::Util 'blessed';
|
||||
use Encode;
|
||||
use Try::Tiny;
|
||||
use NetAddr::MAC;
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
our @EXPORT_OK = qw/
|
||||
set_canonical_ip
|
||||
store_device store_interfaces store_wireless
|
||||
store_vlans store_power store_modules
|
||||
store_neighbors discover_new_neighbors
|
||||
/;
|
||||
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
App::Netdisco::Core::Discover
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
A set of helper subroutines to support parts of the Netdisco application.
|
||||
|
||||
There are no default exports, however the C<:all> tag will export all
|
||||
subroutines.
|
||||
|
||||
=head1 EXPORT_OK
|
||||
|
||||
=head2 set_canonical_ip( $device, $snmp )
|
||||
|
||||
Returns: C<$device>
|
||||
|
||||
Given a Device database object, and a working SNMP connection, check whether
|
||||
the database object's IP is the best choice for that device. If not, update
|
||||
the IP and hostname in the device object for the canonical IP.
|
||||
|
||||
=cut
|
||||
|
||||
sub set_canonical_ip {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $old_ip = $device->ip;
|
||||
my $new_ip = $old_ip;
|
||||
my $revofname = ipv4_from_hostname($snmp->name);
|
||||
|
||||
if (setting('reverse_sysname') and $revofname) {
|
||||
if ($snmp->snmp_connect_ip( $new_ip )) {
|
||||
$new_ip = $revofname;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
|
||||
$old_ip, $revofname;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar @{ setting('device_identity') }) {
|
||||
my @idmaps = @{ setting('device_identity') };
|
||||
my $devips = $device->device_ips->order_by('alias');
|
||||
|
||||
ALIAS: while (my $alias = $devips->next) {
|
||||
next if $alias->alias eq $old_ip;
|
||||
|
||||
foreach my $map (@idmaps) {
|
||||
next unless ref {} eq ref $map;
|
||||
|
||||
foreach my $key (sort keys %$map) {
|
||||
# lhs matches device, rhs matches device_ip
|
||||
if (check_acl_only($device, $key)
|
||||
and check_acl_only($alias, $map->{$key})) {
|
||||
|
||||
if ($snmp->snmp_connect_ip( $alias->alias )) {
|
||||
$new_ip = $alias->alias;
|
||||
last ALIAS;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
|
||||
$old_ip, $alias->alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} # ALIAS
|
||||
}
|
||||
|
||||
return if $new_ip eq $old_ip;
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# delete target device with the same vendor and serial number
|
||||
schema('netdisco')->resultset('Device')->search({
|
||||
ip => $new_ip, vendor => $device->vendor, serial => $device->serial,
|
||||
})->delete;
|
||||
|
||||
# if target device exists then this will die
|
||||
$device->renumber($new_ip)
|
||||
or die "cannot renumber to: $new_ip"; # rollback
|
||||
|
||||
debug sprintf ' [%s] device - changed IP to %s (%s)',
|
||||
$old_ip, $device->ip, ($device->dns || '');
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_device( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store basic device information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_device {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $ip_index = $snmp->ip_index;
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $ip_netmask = $snmp->ip_netmask;
|
||||
|
||||
# build device aliases suitable for DBIC
|
||||
my @aliases;
|
||||
foreach my $entry (keys %$ip_index) {
|
||||
my $ip = NetAddr::IP::Lite->new($entry)
|
||||
or next;
|
||||
my $addr = $ip->addr;
|
||||
|
||||
next if $addr eq '0.0.0.0';
|
||||
next if check_acl_no($ip, 'group:__LOCAL_ADDRESSES__');
|
||||
next if setting('ignore_private_nets') and $ip->is_rfc1918;
|
||||
|
||||
my $iid = $ip_index->{$addr};
|
||||
my $port = $interfaces->{$iid};
|
||||
my $subnet = $ip_netmask->{$addr}
|
||||
? NetAddr::IP::Lite->new($addr, $ip_netmask->{$addr})->network->cidr
|
||||
: undef;
|
||||
|
||||
debug sprintf ' [%s] device - aliased as %s', $device->ip, $addr;
|
||||
push @aliases, {
|
||||
alias => $addr,
|
||||
port => $port,
|
||||
subnet => $subnet,
|
||||
dns => undef,
|
||||
};
|
||||
}
|
||||
|
||||
debug sprintf ' resolving %d aliases with max %d outstanding requests',
|
||||
scalar @aliases, $ENV{'PERL_ANYEVENT_MAX_OUTSTANDING_DNS'};
|
||||
my $resolved_aliases = hostnames_resolve_async(\@aliases);
|
||||
|
||||
# fake one aliases entry for devices not providing ip_index
|
||||
push @$resolved_aliases, { alias => $device->ip, dns => $device->dns }
|
||||
if 0 == scalar @aliases;
|
||||
|
||||
# VTP Management Domain -- assume only one.
|
||||
my $vtpdomains = $snmp->vtp_d_name;
|
||||
my $vtpdomain;
|
||||
if (defined $vtpdomains and scalar values %$vtpdomains) {
|
||||
$device->set_column( vtp_domain => (values %$vtpdomains)[-1] );
|
||||
}
|
||||
|
||||
my $hostname = hostname_from_ip($device->ip);
|
||||
$device->set_column( dns => $hostname ) if $hostname;
|
||||
|
||||
my @properties = qw/
|
||||
snmp_ver
|
||||
description uptime name
|
||||
layers ports mac
|
||||
ps1_type ps2_type ps1_status ps2_status
|
||||
fan slots
|
||||
vendor os os_ver
|
||||
/;
|
||||
|
||||
foreach my $property (@properties) {
|
||||
$device->set_column( $property => $snmp->$property );
|
||||
}
|
||||
|
||||
$device->set_column( model => Encode::decode('UTF-8', $snmp->model) );
|
||||
$device->set_column( serial => Encode::decode('UTF-8', $snmp->serial) );
|
||||
$device->set_column( contact => Encode::decode('UTF-8', $snmp->contact) );
|
||||
$device->set_column( location => Encode::decode('UTF-8', $snmp->location) );
|
||||
|
||||
|
||||
$device->set_column( snmp_class => $snmp->class );
|
||||
$device->set_column( last_discover => \'now()' );
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->device_ips->delete;
|
||||
debug sprintf ' [%s] device - removed %d aliases',
|
||||
$device->ip, $gone;
|
||||
$device->update_or_insert(undef, {for => 'update'});
|
||||
$device->device_ips->populate($resolved_aliases);
|
||||
debug sprintf ' [%s] device - added %d new aliases',
|
||||
$device->ip, scalar @aliases;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_interfaces( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's interface/port information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_interfaces {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $i_type = $snmp->i_type;
|
||||
my $i_ignore = $snmp->i_ignore;
|
||||
my $i_descr = $snmp->i_description;
|
||||
my $i_mtu = $snmp->i_mtu;
|
||||
my $i_speed = $snmp->i_speed;
|
||||
my $i_mac = $snmp->i_mac;
|
||||
my $i_up = $snmp->i_up;
|
||||
my $i_up_admin = $snmp->i_up_admin;
|
||||
my $i_name = $snmp->i_name;
|
||||
my $i_duplex = $snmp->i_duplex;
|
||||
my $i_duplex_admin = $snmp->i_duplex_admin;
|
||||
my $i_stp_state = $snmp->i_stp_state;
|
||||
my $i_vlan = $snmp->i_vlan;
|
||||
my $i_lastchange = $snmp->i_lastchange;
|
||||
my $agg_ports = $snmp->agg_ports;
|
||||
|
||||
# clear the cached uptime and get a new one
|
||||
my $dev_uptime = $snmp->load_uptime;
|
||||
if (!defined $dev_uptime) {
|
||||
error sprintf ' [%s] interfaces - Error! Failed to get uptime from device!',
|
||||
$device->ip;
|
||||
return;
|
||||
}
|
||||
|
||||
# used to track how many times the device uptime wrapped
|
||||
my $dev_uptime_wrapped = 0;
|
||||
|
||||
# use SNMP-FRAMEWORK-MIB::snmpEngineTime if available to
|
||||
# fix device uptime if wrapped
|
||||
if (defined $snmp->snmpEngineTime) {
|
||||
$dev_uptime_wrapped = int( $snmp->snmpEngineTime * 100 / 2**32 );
|
||||
if ($dev_uptime_wrapped > 0) {
|
||||
info sprintf ' [%s] interface - device uptime wrapped %d times - correcting',
|
||||
$device->ip, $dev_uptime_wrapped;
|
||||
$device->uptime( $dev_uptime + $dev_uptime_wrapped * 2**32 );
|
||||
}
|
||||
}
|
||||
|
||||
# build device interfaces suitable for DBIC
|
||||
my %interfaces;
|
||||
foreach my $entry (keys %$interfaces) {
|
||||
my $port = $interfaces->{$entry};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] interfaces - ignoring %s (no port mapping)',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
if (scalar grep {$port =~ m/^$_$/} @{setting('ignore_interfaces') || []}) {
|
||||
debug sprintf
|
||||
' [%s] interfaces - ignoring %s (%s) (config:ignore_interfaces)',
|
||||
$device->ip, $entry, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
if (exists $i_ignore->{$entry}) {
|
||||
debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s)',
|
||||
$device->ip, $entry, $port, $i_type->{$entry};
|
||||
next;
|
||||
}
|
||||
|
||||
my $lc = $i_lastchange->{$entry} || 0;
|
||||
if (not $dev_uptime_wrapped and $lc > $dev_uptime) {
|
||||
info sprintf ' [%s] interfaces - device uptime wrapped (%s) - correcting',
|
||||
$device->ip, $port;
|
||||
$device->uptime( $dev_uptime + 2**32 );
|
||||
$dev_uptime_wrapped = 1;
|
||||
}
|
||||
|
||||
if ($device->is_column_changed('uptime') and $lc) {
|
||||
if ($lc < $dev_uptime) {
|
||||
# ambiguous: lastchange could be sysUptime before or after wrap
|
||||
if ($dev_uptime > 30000 and $lc < 30000) {
|
||||
# uptime wrap more than 5min ago but lastchange within 5min
|
||||
# assume lastchange was directly after boot -> no action
|
||||
}
|
||||
else {
|
||||
# uptime wrap less than 5min ago or lastchange > 5min ago
|
||||
# to be on safe side, assume lastchange after counter wrap
|
||||
debug sprintf
|
||||
' [%s] interfaces - correcting LastChange for %s, assuming sysUptime wrap',
|
||||
$device->ip, $port;
|
||||
$lc += $dev_uptime_wrapped * 2**32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$interfaces{$port} = {
|
||||
port => $port,
|
||||
descr => $i_descr->{$entry},
|
||||
up => $i_up->{$entry},
|
||||
up_admin => $i_up_admin->{$entry},
|
||||
mac => $i_mac->{$entry},
|
||||
speed => $i_speed->{$entry},
|
||||
mtu => $i_mtu->{$entry},
|
||||
name => Encode::decode('UTF-8', $i_name->{$entry}),
|
||||
duplex => $i_duplex->{$entry},
|
||||
duplex_admin => $i_duplex_admin->{$entry},
|
||||
stp => $i_stp_state->{$entry},
|
||||
type => $i_type->{$entry},
|
||||
vlan => $i_vlan->{$entry},
|
||||
pvid => $i_vlan->{$entry},
|
||||
is_master => 'false',
|
||||
slave_of => undef,
|
||||
lastchange => $lc,
|
||||
};
|
||||
}
|
||||
|
||||
# must do this after building %interfaces so that we can set is_master
|
||||
foreach my $sidx (keys %$agg_ports) {
|
||||
my $slave = $interfaces->{$sidx} or next;
|
||||
my $master = $interfaces->{ $agg_ports->{$sidx} } or next;
|
||||
next unless exists $interfaces{$slave} and exists $interfaces{$master};
|
||||
|
||||
$interfaces{$slave}->{slave_of} = $master;
|
||||
$interfaces{$master}->{is_master} = 'true';
|
||||
}
|
||||
|
||||
schema('netdisco')->resultset('DevicePort')->txn_do_locked(sub {
|
||||
my $gone = $device->ports->delete({keep_nodes => 1});
|
||||
debug sprintf ' [%s] interfaces - removed %d interfaces',
|
||||
$device->ip, $gone;
|
||||
$device->update_or_insert(undef, {for => 'update'});
|
||||
$device->ports->populate([values %interfaces]);
|
||||
debug sprintf ' [%s] interfaces - added %d new interfaces',
|
||||
$device->ip, scalar values %interfaces;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_wireless( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's wireless interface information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_wireless {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $ssidlist = $snmp->i_ssidlist;
|
||||
return unless scalar keys %$ssidlist;
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $ssidbcast = $snmp->i_ssidbcast;
|
||||
my $ssidmac = $snmp->i_ssidmac;
|
||||
my $channel = $snmp->i_80211channel;
|
||||
my $power = $snmp->dot11_cur_tx_pwr_mw;
|
||||
|
||||
# build device ssid list suitable for DBIC
|
||||
my @ssids;
|
||||
foreach my $entry (keys %$ssidlist) {
|
||||
(my $iid = $entry) =~ s/\.\d+$//;
|
||||
my $port = $interfaces->{$iid};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] wireless - ignoring %s (no port mapping)',
|
||||
$device->ip, $iid;
|
||||
next;
|
||||
}
|
||||
|
||||
push @ssids, {
|
||||
port => $port,
|
||||
ssid => $ssidlist->{$entry},
|
||||
broadcast => $ssidbcast->{$entry},
|
||||
bssid => $ssidmac->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->ssids->delete;
|
||||
debug sprintf ' [%s] wireless - removed %d SSIDs',
|
||||
$device->ip, $gone;
|
||||
$device->ssids->populate(\@ssids);
|
||||
debug sprintf ' [%s] wireless - added %d new SSIDs',
|
||||
$device->ip, scalar @ssids;
|
||||
});
|
||||
|
||||
# build device channel list suitable for DBIC
|
||||
my @channels;
|
||||
foreach my $entry (keys %$channel) {
|
||||
my $port = $interfaces->{$entry};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] wireless - ignoring %s (no port mapping)',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
push @channels, {
|
||||
port => $port,
|
||||
channel => $channel->{$entry},
|
||||
power => $power->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->wireless_ports->delete;
|
||||
debug sprintf ' [%s] wireless - removed %d wireless channels',
|
||||
$device->ip, $gone;
|
||||
$device->wireless_ports->populate(\@channels);
|
||||
debug sprintf ' [%s] wireless - added %d new wireless channels',
|
||||
$device->ip, scalar @channels;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_vlans( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's vlan information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_vlans {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $v_name = $snmp->v_name;
|
||||
my $v_index = $snmp->v_index;
|
||||
|
||||
# build device vlans suitable for DBIC
|
||||
my %v_seen = ();
|
||||
my @devicevlans;
|
||||
foreach my $entry (keys %$v_name) {
|
||||
my $vlan = $v_index->{$entry};
|
||||
next unless defined $vlan and $vlan;
|
||||
++$v_seen{$vlan};
|
||||
|
||||
push @devicevlans, {
|
||||
vlan => $vlan,
|
||||
description => $v_name->{$entry},
|
||||
last_discover => \'now()',
|
||||
};
|
||||
}
|
||||
|
||||
my $i_vlan = $snmp->i_vlan;
|
||||
my $i_vlan_membership = $snmp->i_vlan_membership;
|
||||
my $i_vlan_type = $snmp->i_vlan_type;
|
||||
my $interfaces = $snmp->interfaces;
|
||||
|
||||
# build device port vlans suitable for DBIC
|
||||
my @portvlans = ();
|
||||
foreach my $entry (keys %$i_vlan_membership) {
|
||||
my %port_vseen = ();
|
||||
my $port = $interfaces->{$entry};
|
||||
next unless defined $port;
|
||||
|
||||
my $type = $i_vlan_type->{$entry};
|
||||
|
||||
foreach my $vlan (@{ $i_vlan_membership->{$entry} }) {
|
||||
next unless defined $vlan and $vlan;
|
||||
next if ++$port_vseen{$vlan} > 1;
|
||||
|
||||
my $native = ((defined $i_vlan->{$entry}) and ($vlan eq $i_vlan->{$entry})) ? "t" : "f";
|
||||
push @portvlans, {
|
||||
port => $port,
|
||||
vlan => $vlan,
|
||||
native => $native,
|
||||
vlantype => $type,
|
||||
last_discover => \'now()',
|
||||
};
|
||||
|
||||
next if $v_seen{$vlan};
|
||||
|
||||
# also add an unnamed vlan to the device
|
||||
push @devicevlans, {
|
||||
vlan => $vlan,
|
||||
description => (sprintf "VLAN %d", $vlan),
|
||||
last_discover => \'now()',
|
||||
};
|
||||
++$v_seen{$vlan};
|
||||
}
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->vlans->delete;
|
||||
debug sprintf ' [%s] vlans - removed %d device VLANs',
|
||||
$device->ip, $gone;
|
||||
$device->vlans->populate(\@devicevlans);
|
||||
debug sprintf ' [%s] vlans - added %d new device VLANs',
|
||||
$device->ip, scalar @devicevlans;
|
||||
});
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->port_vlans->delete;
|
||||
debug sprintf ' [%s] vlans - removed %d port VLANs',
|
||||
$device->ip, $gone;
|
||||
$device->port_vlans->populate(\@portvlans);
|
||||
debug sprintf ' [%s] vlans - added %d new port VLANs',
|
||||
$device->ip, scalar @portvlans;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_power( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's PoE information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_power {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $p_watts = $snmp->peth_power_watts;
|
||||
my $p_status = $snmp->peth_power_status;
|
||||
|
||||
if (!defined $p_watts) {
|
||||
debug sprintf ' [%s] power - 0 power modules', $device->ip;
|
||||
return;
|
||||
}
|
||||
|
||||
# build device module power info suitable for DBIC
|
||||
my @devicepower;
|
||||
foreach my $entry (keys %$p_watts) {
|
||||
push @devicepower, {
|
||||
module => $entry,
|
||||
power => $p_watts->{$entry},
|
||||
status => $p_status->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $p_ifindex = $snmp->peth_port_ifindex;
|
||||
my $p_admin = $snmp->peth_port_admin;
|
||||
my $p_pstatus = $snmp->peth_port_status;
|
||||
my $p_class = $snmp->peth_port_class;
|
||||
my $p_power = $snmp->peth_port_power;
|
||||
|
||||
# build device port power info suitable for DBIC
|
||||
my @portpower;
|
||||
foreach my $entry (keys %$p_ifindex) {
|
||||
my $port = $interfaces->{ $p_ifindex->{$entry} };
|
||||
next unless $port;
|
||||
|
||||
my ($module) = split m/\./, $entry;
|
||||
|
||||
push @portpower, {
|
||||
port => $port,
|
||||
module => $module,
|
||||
admin => $p_admin->{$entry},
|
||||
status => $p_pstatus->{$entry},
|
||||
class => $p_class->{$entry},
|
||||
power => $p_power->{$entry},
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->power_modules->delete;
|
||||
debug sprintf ' [%s] power - removed %d power modules',
|
||||
$device->ip, $gone;
|
||||
$device->power_modules->populate(\@devicepower);
|
||||
debug sprintf ' [%s] power - added %d new power modules',
|
||||
$device->ip, scalar @devicepower;
|
||||
});
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->powered_ports->delete;
|
||||
debug sprintf ' [%s] power - removed %d PoE capable ports',
|
||||
$device->ip, $gone;
|
||||
$device->powered_ports->populate(\@portpower);
|
||||
debug sprintf ' [%s] power - added %d new PoE capable ports',
|
||||
$device->ip, scalar @portpower;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_modules( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's module information.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_modules {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
my $e_index = $snmp->e_index;
|
||||
|
||||
if (!defined $e_index) {
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->modules->delete;
|
||||
debug sprintf ' [%s] modules - removed %d chassis modules',
|
||||
$device->ip, $gone;
|
||||
|
||||
$device->modules->update_or_create({
|
||||
ip => $device->ip,
|
||||
index => 1,
|
||||
parent => 0,
|
||||
name => 'chassis',
|
||||
class => 'chassis',
|
||||
pos => -1,
|
||||
# too verbose and link doesn't work anyway
|
||||
# description => $device->description,
|
||||
sw_ver => $device->os_ver,
|
||||
serial => $device->serial,
|
||||
model => $device->model,
|
||||
fru => \'false',
|
||||
last_discover => \'now()',
|
||||
});
|
||||
});
|
||||
|
||||
debug
|
||||
sprintf ' [%s] modules - 0 chassis components (added one pseudo for chassis)',
|
||||
$device->ip;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
my $e_descr = $snmp->e_descr;
|
||||
my $e_type = $snmp->e_type;
|
||||
my $e_parent = $snmp->e_parent;
|
||||
my $e_name = $snmp->e_name;
|
||||
my $e_class = $snmp->e_class;
|
||||
my $e_pos = $snmp->e_pos;
|
||||
my $e_hwver = $snmp->e_hwver;
|
||||
my $e_fwver = $snmp->e_fwver;
|
||||
my $e_swver = $snmp->e_swver;
|
||||
my $e_model = $snmp->e_model;
|
||||
my $e_serial = $snmp->e_serial;
|
||||
my $e_fru = $snmp->e_fru;
|
||||
|
||||
# build device modules list for DBIC
|
||||
my @modules;
|
||||
foreach my $entry (keys %$e_index) {
|
||||
push @modules, {
|
||||
index => $e_index->{$entry},
|
||||
type => $e_type->{$entry},
|
||||
parent => $e_parent->{$entry},
|
||||
name => Encode::decode('UTF-8', $e_name->{$entry}),
|
||||
class => $e_class->{$entry},
|
||||
pos => $e_pos->{$entry},
|
||||
hw_ver => Encode::decode('UTF-8', $e_hwver->{$entry}),
|
||||
fw_ver => Encode::decode('UTF-8', $e_fwver->{$entry}),
|
||||
sw_ver => Encode::decode('UTF-8', $e_swver->{$entry}),
|
||||
model => Encode::decode('UTF-8', $e_model->{$entry}),
|
||||
serial => Encode::decode('UTF-8', $e_serial->{$entry}),
|
||||
fru => $e_fru->{$entry},
|
||||
description => Encode::decode('UTF-8', $e_descr->{$entry}),
|
||||
last_discover => \'now()',
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->modules->delete;
|
||||
debug sprintf ' [%s] modules - removed %d chassis modules',
|
||||
$device->ip, $gone;
|
||||
$device->modules->populate(\@modules);
|
||||
debug sprintf ' [%s] modules - added %d new chassis modules',
|
||||
$device->ip, scalar @modules;
|
||||
});
|
||||
}
|
||||
|
||||
=head2 store_neighbors( $device, $snmp )
|
||||
|
||||
returns: C<@to_discover>
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's port neighbors information.
|
||||
|
||||
Entries in the Topology database table will override any discovered device
|
||||
port relationships.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
A list of discovererd neighbors will be returned as [C<$ip>, C<$type>] tuples.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_neighbors {
|
||||
my ($device, $snmp) = @_;
|
||||
my @to_discover = ();
|
||||
|
||||
# first allow any manually configured topology to be set
|
||||
_set_manual_topology($device, $snmp);
|
||||
|
||||
if (!defined $snmp->has_topo) {
|
||||
debug sprintf ' [%s] neigh - neighbor protocols are not enabled', $device->ip;
|
||||
return @to_discover;
|
||||
}
|
||||
|
||||
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;
|
||||
my $c_cap = $snmp->c_cap;
|
||||
|
||||
# v4 and v6 neighbor tables
|
||||
my $c_ip = ($snmp->c_ip || {});
|
||||
my %c_ipv6 = %{ ($snmp->can('hasLLDP') and $snmp->hasLLDP)
|
||||
? ($snmp->lldp_ipv6 || {}) : {} };
|
||||
|
||||
# remove keys with undef values, as c_ip does
|
||||
delete @c_ipv6{ grep { not defined $c_ipv6{$_} } keys %c_ipv6 };
|
||||
# now combine them, v6 wins
|
||||
$c_ip = { %$c_ip, %c_ipv6 };
|
||||
|
||||
foreach my $entry (sort (List::MoreUtils::uniq( (keys %$c_ip), (keys %$c_cap) ))) {
|
||||
if (!defined $c_if->{$entry} or !defined $interfaces->{ $c_if->{$entry} }) {
|
||||
debug sprintf ' [%s] neigh - port for IID:%s not resolved, skipping',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
my $port = $interfaces->{ $c_if->{$entry} };
|
||||
my $portrow = schema('netdisco')->resultset('DevicePort')
|
||||
->single({ip => $device->ip, port => $port});
|
||||
|
||||
if (!defined $portrow) {
|
||||
info sprintf ' [%s] neigh - local port %s not in database!',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
if (ref $c_ip->{$entry}) {
|
||||
error sprintf ' [%s] neigh - Error! port %s has multiple neighbors - skipping',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
my $remote_ip = $c_ip->{$entry};
|
||||
my $remote_port = undef;
|
||||
my $remote_type = Encode::decode('UTF-8', $c_platform->{$entry} || '');
|
||||
my $remote_id = Encode::decode('UTF-8', $c_id->{$entry});
|
||||
my $remote_cap = $c_cap->{$entry} || [];
|
||||
|
||||
# IP Phone and WAP detection type fixup
|
||||
if (scalar @$remote_cap or $remote_type) {
|
||||
my $phone_flag = grep {match_devicetype($_, 'phone_capabilities')}
|
||||
@$remote_cap;
|
||||
my $ap_flag = grep {match_devicetype($_, 'wap_capabilities')}
|
||||
@$remote_cap;
|
||||
|
||||
if ($phone_flag or match_devicetype($remote_type, 'phone_platforms')) {
|
||||
$remote_type = 'IP Phone: '. $remote_type
|
||||
if $remote_type !~ /ip.phone/i;
|
||||
}
|
||||
elsif ($ap_flag or match_devicetype($remote_type, 'wap_platforms')) {
|
||||
$remote_type = 'AP: '. $remote_type;
|
||||
}
|
||||
|
||||
$portrow->update({remote_type => $remote_type});
|
||||
}
|
||||
|
||||
if ($portrow->manual_topo) {
|
||||
info sprintf ' [%s] neigh - %s has manually defined topology',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
next unless $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
|
||||
check_acl_no($remote_ip, 'group:__LOCAL_ADDRESSES__')) {
|
||||
|
||||
if ($remote_id) {
|
||||
my $devices = schema('netdisco')->resultset('Device');
|
||||
my $neigh = $devices->single({name => $remote_id});
|
||||
info sprintf
|
||||
' [%s] neigh - bad address %s on port %s, searching for %s instead',
|
||||
$device->ip, $remote_ip, $port, $remote_id;
|
||||
|
||||
if (!defined $neigh) {
|
||||
my $mac = NetAddr::MAC->new(mac => $remote_id);
|
||||
if ($mac and not $mac->errstr) {
|
||||
$neigh = $devices->single({mac => $mac->as_ieee});
|
||||
}
|
||||
}
|
||||
|
||||
# some HP switches send 127.0.0.1 as remote_ip if no ip address
|
||||
# on default vlan for HP switches remote_ip looks like
|
||||
# "myswitchname(012345-012345)"
|
||||
if (!defined $neigh) {
|
||||
(my $tmpid = $remote_id) =~ s/.*\(([0-9a-f]{6})-([0-9a-f]{6})\).*/$1$2/;
|
||||
my $mac = NetAddr::MAC->new(mac => $tmpid);
|
||||
if ($mac and not $mac->errstr) {
|
||||
info sprintf
|
||||
'[%s] neigh - found neighbor %s by MAC %s',
|
||||
$device->ip, $remote_id, $mac->as_ieee;
|
||||
$neigh = $devices->single({mac => $mac->as_ieee});
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $neigh) {
|
||||
(my $shortid = $remote_id) =~ s/\..*//;
|
||||
$neigh = $devices->single({name => { -ilike => "${shortid}%" }});
|
||||
}
|
||||
|
||||
if ($neigh) {
|
||||
$remote_ip = $neigh->ip;
|
||||
info sprintf ' [%s] neigh - found %s with IP %s',
|
||||
$device->ip, $remote_id, $remote_ip;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - could not find %s, skipping',
|
||||
$device->ip, $remote_id;
|
||||
next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - skipping unuseable address %s on port %s',
|
||||
$device->ip, $remote_ip, $port;
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
# what we came here to do.... discover the neighbor
|
||||
debug sprintf
|
||||
' [%s] neigh - adding neighbor %s, type [%s], on %s to discovery queue',
|
||||
$device->ip, $remote_ip, ($remote_type || ''), $port;
|
||||
push @to_discover, [$remote_ip, $remote_type];
|
||||
|
||||
$remote_port = $c_port->{$entry};
|
||||
if (defined $remote_port) {
|
||||
# clean weird characters
|
||||
$remote_port =~ s/[^\d\/\.,()\w:-]+//gi;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - no remote port found for port %s at %s',
|
||||
$device->ip, $port, $remote_ip;
|
||||
}
|
||||
|
||||
$portrow->update({
|
||||
remote_ip => $remote_ip,
|
||||
remote_port => $remote_port,
|
||||
remote_type => $remote_type,
|
||||
remote_id => $remote_id,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"false",
|
||||
});
|
||||
|
||||
# update master of our aggregate to be a neighbor of
|
||||
# the master on our peer device (a lot of iffs to get there...).
|
||||
# & cannot use ->neighbor prefetch because this is the port insert!
|
||||
if (defined $portrow->slave_of) {
|
||||
|
||||
my $peer_device = get_device($remote_ip);
|
||||
my $master = schema('netdisco')->resultset('DevicePort')->single({
|
||||
ip => $device->ip,
|
||||
port => $portrow->slave_of
|
||||
});
|
||||
|
||||
if ($peer_device and $peer_device->in_storage and $master
|
||||
and not ($portrow->is_master or defined $master->slave_of)) {
|
||||
|
||||
my $peer_port = schema('netdisco')->resultset('DevicePort')->single({
|
||||
ip => $peer_device->ip,
|
||||
port => $portrow->remote_port,
|
||||
});
|
||||
|
||||
$master->update({
|
||||
remote_ip => ($peer_device->ip || $remote_ip),
|
||||
remote_port => ($peer_port ? $peer_port->slave_of : undef ),
|
||||
is_uplink => \"true",
|
||||
is_master => \"true",
|
||||
manual_topo => \"false",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @to_discover;
|
||||
}
|
||||
|
||||
# take data from the topology table and update remote_ip and remote_port
|
||||
# in the devices table. only use root_ips and skip any bad topo entries.
|
||||
sub _set_manual_topology {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# clear manual topology flags
|
||||
schema('netdisco')->resultset('DevicePort')
|
||||
->search({ip => $device->ip})->update({manual_topo => \'false'});
|
||||
|
||||
my $topo_links = schema('netdisco')->resultset('Topology')
|
||||
->search({-or => [dev1 => $device->ip, dev2 => $device->ip]});
|
||||
debug sprintf ' [%s] neigh - setting manual topology links', $device->ip;
|
||||
|
||||
while (my $link = $topo_links->next) {
|
||||
# could fail for broken topo, but we ignore to try the rest
|
||||
try {
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# only work on root_ips
|
||||
my $left = get_device($link->dev1);
|
||||
my $right = get_device($link->dev2);
|
||||
|
||||
# skip bad entries
|
||||
return unless ($left->in_storage and $right->in_storage);
|
||||
|
||||
$left->ports
|
||||
->single({port => $link->port1})
|
||||
->update({
|
||||
remote_ip => $right->ip,
|
||||
remote_port => $link->port2,
|
||||
remote_type => undef,
|
||||
remote_id => undef,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"true",
|
||||
});
|
||||
|
||||
$right->ports
|
||||
->single({port => $link->port2})
|
||||
->update({
|
||||
remote_ip => $left->ip,
|
||||
remote_port => $link->port1,
|
||||
remote_type => undef,
|
||||
remote_id => undef,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"true",
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
=head2 discover_new_neighbors( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's port neighbors information.
|
||||
|
||||
Entries in the Topology database table will override any discovered device
|
||||
port relationships.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
Any discovered neighbor unknown to Netdisco will have a C<discover> job
|
||||
immediately queued (subject to the filtering by the C<discover_*> settings).
|
||||
|
||||
=cut
|
||||
|
||||
sub discover_new_neighbors {
|
||||
my @to_discover = store_neighbors(@_);
|
||||
|
||||
# only enqueue if device is not already discovered,
|
||||
# discover_* config permits the discovery
|
||||
foreach my $neighbor (@to_discover) {
|
||||
my ($ip, $remote_type) = @$neighbor;
|
||||
|
||||
my $device = get_device($ip);
|
||||
next if $device->in_storage;
|
||||
|
||||
if (not is_discoverable($device, $remote_type)) {
|
||||
debug sprintf
|
||||
' queue - %s, type [%s] excluded by discover_* config',
|
||||
$ip, ($remote_type || '');
|
||||
next;
|
||||
}
|
||||
|
||||
jq_insert({
|
||||
device => $ip,
|
||||
action => 'discover',
|
||||
subaction => 'with-nodes',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -10,6 +10,8 @@ use warnings;
|
||||
use NetAddr::IP::Lite ':lower';
|
||||
use App::Netdisco::Util::DNS 'hostname_from_ip';
|
||||
|
||||
use overload '""' => sub { shift->ip }, fallback => 1;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
__PACKAGE__->table("device");
|
||||
__PACKAGE__->add_columns(
|
||||
|
||||
@@ -5,7 +5,6 @@ use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Util::Device 'is_arpnipable_now';
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
|
||||
register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
@@ -14,15 +13,13 @@ register_worker({ primary => true }, sub {
|
||||
return Status->error('arpnip failed: unable to interpret device param')
|
||||
unless defined $device;
|
||||
|
||||
my $host = $device->ip;
|
||||
|
||||
return Status->done("arpnip skipped: $host not yet discovered")
|
||||
return Status->done("arpnip skipped: $device not yet discovered")
|
||||
unless $device->in_storage;
|
||||
|
||||
return Status->defer("arpnip skipped: $host is pseudo-device")
|
||||
return Status->defer("arpnip skipped: $device is pseudo-device")
|
||||
if $device->vendor and $device->vendor eq 'netdisco';
|
||||
|
||||
return Status->defer("arpnip deferred: $host is not arpnipable")
|
||||
return Status->defer("arpnip deferred: $device is not arpnipable")
|
||||
unless is_arpnipable_now($device);
|
||||
|
||||
return Status->done('Arpnip is able to run.');
|
||||
|
||||
@@ -4,6 +4,7 @@ use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Node 'check_mac';
|
||||
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
@@ -15,9 +16,9 @@ register_worker({ primary => true, driver => 'snmp' }, sub {
|
||||
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||
or return Status->defer("arpnip failed: could not SNMP connect to $host");
|
||||
or return Status->defer("arpnip failed: could not SNMP connect to $device");
|
||||
|
||||
return Status->defer("Skipped arpnip for device $host without layer 3 capability")
|
||||
return Status->defer("Skipped arpnip for device $device without layer 3 capability")
|
||||
unless $snmp->has_layer(3);
|
||||
|
||||
# get v4 arp table
|
||||
|
||||
@@ -4,6 +4,7 @@ use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use NetAddr::IP::Lite ':lower';
|
||||
@@ -14,9 +15,9 @@ register_worker({ primary => false, driver => 'snmp' }, sub {
|
||||
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||
or return Status->defer("arpnip failed: could not SNMP connect to $host");
|
||||
or return Status->defer("arpnip failed: could not SNMP connect to $device");
|
||||
|
||||
return Status->defer("Skipped arpnip for device $host without layer 3 capability")
|
||||
return Status->defer("Skipped arpnip for device $device without layer 3 capability")
|
||||
unless $snmp->has_layer(3);
|
||||
|
||||
# get directly connected networks
|
||||
|
||||
30
lib/App/Netdisco/Worker/Plugin/Discover.pm
Normal file
30
lib/App/Netdisco/Worker/Plugin/Discover.pm
Normal file
@@ -0,0 +1,30 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Util::Device 'is_discoverable_now';
|
||||
|
||||
register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
my $device = $job->device;
|
||||
|
||||
return Status->error('discover failed: unable to interpret device param')
|
||||
unless defined $device;
|
||||
|
||||
my $host = $device->ip;
|
||||
|
||||
return Status->error("discover failed: no device param (need -d ?)")
|
||||
if $host eq '0.0.0.0';
|
||||
|
||||
return Status->defer("discover skipped: $host is pseudo-device")
|
||||
if $device->vendor and $device->vendor eq 'netdisco';
|
||||
|
||||
return Status->defer("discover deferred: $host is not discoverable")
|
||||
unless is_discoverable_now($device);
|
||||
|
||||
return Status->done('discover is able to run.');
|
||||
});
|
||||
|
||||
true;
|
||||
82
lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm
Normal file
82
lib/App/Netdisco/Worker/Plugin/Discover/CanonicalIP.pm
Normal file
@@ -0,0 +1,82 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::CanonicalIP;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Permission 'check_acl_only';
|
||||
use App::Netdisco::Util::DNS 'ipv4_from_hostname';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $old_ip = $device->ip;
|
||||
my $new_ip = $old_ip;
|
||||
my $revofname = ipv4_from_hostname($snmp->name);
|
||||
|
||||
if (setting('reverse_sysname') and $revofname) {
|
||||
if ($snmp->snmp_connect_ip( $new_ip )) {
|
||||
$new_ip = $revofname;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
|
||||
$old_ip, $revofname;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar @{ setting('device_identity') }) {
|
||||
my @idmaps = @{ setting('device_identity') };
|
||||
my $devips = $device->device_ips->order_by('alias');
|
||||
|
||||
ALIAS: while (my $alias = $devips->next) {
|
||||
next if $alias->alias eq $old_ip;
|
||||
|
||||
foreach my $map (@idmaps) {
|
||||
next unless ref {} eq ref $map;
|
||||
|
||||
foreach my $key (sort keys %$map) {
|
||||
# lhs matches device, rhs matches device_ip
|
||||
if (check_acl_only($device, $key)
|
||||
and check_acl_only($alias, $map->{$key})) {
|
||||
|
||||
if ($snmp->snmp_connect_ip( $alias->alias )) {
|
||||
$new_ip = $alias->alias;
|
||||
last ALIAS;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] device - cannot renumber to %s - SNMP connect failed',
|
||||
$old_ip, $alias->alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} # ALIAS
|
||||
}
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip)
|
||||
if $new_ip eq $old_ip;
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# delete target device with the same vendor and serial number
|
||||
schema('netdisco')->resultset('Device')->search({
|
||||
ip => $new_ip, vendor => $device->vendor, serial => $device->serial,
|
||||
})->delete;
|
||||
|
||||
# if target device exists then this will die
|
||||
$device->renumber($new_ip)
|
||||
or die "cannot renumber to: $new_ip"; # rollback
|
||||
|
||||
debug sprintf ' [%s] device - changed IP to %s (%s)',
|
||||
$old_ip, $device->ip, ($device->dns || '');
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
96
lib/App/Netdisco/Worker/Plugin/Discover/Entities.pm
Normal file
96
lib/App/Netdisco/Worker/Plugin/Discover/Entities.pm
Normal file
@@ -0,0 +1,96 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::Entities;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use Encode;
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $e_index = $snmp->e_index;
|
||||
|
||||
if (!defined $e_index) {
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->modules->delete;
|
||||
debug sprintf ' [%s] modules - removed %d chassis modules',
|
||||
$device->ip, $gone;
|
||||
|
||||
$device->modules->update_or_create({
|
||||
ip => $device->ip,
|
||||
index => 1,
|
||||
parent => 0,
|
||||
name => 'chassis',
|
||||
class => 'chassis',
|
||||
pos => -1,
|
||||
# too verbose and link doesn't work anyway
|
||||
# description => $device->description,
|
||||
sw_ver => $device->os_ver,
|
||||
serial => $device->serial,
|
||||
model => $device->model,
|
||||
fru => \'false',
|
||||
last_discover => \'now()',
|
||||
});
|
||||
});
|
||||
|
||||
debug
|
||||
sprintf ' [%s] modules - 0 chassis components (added one pseudo for chassis)',
|
||||
$device->ip;
|
||||
|
||||
return Status->done("Ended discover for $device");
|
||||
}
|
||||
|
||||
my $e_descr = $snmp->e_descr;
|
||||
my $e_type = $snmp->e_type;
|
||||
my $e_parent = $snmp->e_parent;
|
||||
my $e_name = $snmp->e_name;
|
||||
my $e_class = $snmp->e_class;
|
||||
my $e_pos = $snmp->e_pos;
|
||||
my $e_hwver = $snmp->e_hwver;
|
||||
my $e_fwver = $snmp->e_fwver;
|
||||
my $e_swver = $snmp->e_swver;
|
||||
my $e_model = $snmp->e_model;
|
||||
my $e_serial = $snmp->e_serial;
|
||||
my $e_fru = $snmp->e_fru;
|
||||
|
||||
# build device modules list for DBIC
|
||||
my @modules;
|
||||
foreach my $entry (keys %$e_index) {
|
||||
push @modules, {
|
||||
index => $e_index->{$entry},
|
||||
type => $e_type->{$entry},
|
||||
parent => $e_parent->{$entry},
|
||||
name => Encode::decode('UTF-8', $e_name->{$entry}),
|
||||
class => $e_class->{$entry},
|
||||
pos => $e_pos->{$entry},
|
||||
hw_ver => Encode::decode('UTF-8', $e_hwver->{$entry}),
|
||||
fw_ver => Encode::decode('UTF-8', $e_fwver->{$entry}),
|
||||
sw_ver => Encode::decode('UTF-8', $e_swver->{$entry}),
|
||||
model => Encode::decode('UTF-8', $e_model->{$entry}),
|
||||
serial => Encode::decode('UTF-8', $e_serial->{$entry}),
|
||||
fru => $e_fru->{$entry},
|
||||
description => Encode::decode('UTF-8', $e_descr->{$entry}),
|
||||
last_discover => \'now()',
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->modules->delete;
|
||||
debug sprintf ' [%s] modules - removed %d chassis modules',
|
||||
$device->ip, $gone;
|
||||
$device->modules->populate(\@modules);
|
||||
debug sprintf ' [%s] modules - added %d new chassis modules',
|
||||
$device->ip, scalar @modules;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
151
lib/App/Netdisco/Worker/Plugin/Discover/Interfaces.pm
Normal file
151
lib/App/Netdisco/Worker/Plugin/Discover/Interfaces.pm
Normal file
@@ -0,0 +1,151 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::Interfaces;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use Encode;
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $i_type = $snmp->i_type;
|
||||
my $i_ignore = $snmp->i_ignore;
|
||||
my $i_descr = $snmp->i_description;
|
||||
my $i_mtu = $snmp->i_mtu;
|
||||
my $i_speed = $snmp->i_speed;
|
||||
my $i_mac = $snmp->i_mac;
|
||||
my $i_up = $snmp->i_up;
|
||||
my $i_up_admin = $snmp->i_up_admin;
|
||||
my $i_name = $snmp->i_name;
|
||||
my $i_duplex = $snmp->i_duplex;
|
||||
my $i_duplex_admin = $snmp->i_duplex_admin;
|
||||
my $i_stp_state = $snmp->i_stp_state;
|
||||
my $i_vlan = $snmp->i_vlan;
|
||||
my $i_lastchange = $snmp->i_lastchange;
|
||||
my $agg_ports = $snmp->agg_ports;
|
||||
|
||||
# clear the cached uptime and get a new one
|
||||
my $dev_uptime = $snmp->load_uptime;
|
||||
if (!defined $dev_uptime) {
|
||||
error sprintf ' [%s] interfaces - Error! Failed to get uptime from device!',
|
||||
$device->ip;
|
||||
return Status->error("discover failed: no uptime from device $device!");
|
||||
}
|
||||
|
||||
# used to track how many times the device uptime wrapped
|
||||
my $dev_uptime_wrapped = 0;
|
||||
|
||||
# use SNMP-FRAMEWORK-MIB::snmpEngineTime if available to
|
||||
# fix device uptime if wrapped
|
||||
if (defined $snmp->snmpEngineTime) {
|
||||
$dev_uptime_wrapped = int( $snmp->snmpEngineTime * 100 / 2**32 );
|
||||
if ($dev_uptime_wrapped > 0) {
|
||||
info sprintf ' [%s] interface - device uptime wrapped %d times - correcting',
|
||||
$device->ip, $dev_uptime_wrapped;
|
||||
$device->uptime( $dev_uptime + $dev_uptime_wrapped * 2**32 );
|
||||
}
|
||||
}
|
||||
|
||||
# build device interfaces suitable for DBIC
|
||||
my %interfaces;
|
||||
foreach my $entry (keys %$interfaces) {
|
||||
my $port = $interfaces->{$entry};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] interfaces - ignoring %s (no port mapping)',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
if (scalar grep {$port =~ m/^$_$/} @{setting('ignore_interfaces') || []}) {
|
||||
debug sprintf
|
||||
' [%s] interfaces - ignoring %s (%s) (config:ignore_interfaces)',
|
||||
$device->ip, $entry, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
if (exists $i_ignore->{$entry}) {
|
||||
debug sprintf ' [%s] interfaces - ignoring %s (%s) (%s)',
|
||||
$device->ip, $entry, $port, $i_type->{$entry};
|
||||
next;
|
||||
}
|
||||
|
||||
my $lc = $i_lastchange->{$entry} || 0;
|
||||
if (not $dev_uptime_wrapped and $lc > $dev_uptime) {
|
||||
info sprintf ' [%s] interfaces - device uptime wrapped (%s) - correcting',
|
||||
$device->ip, $port;
|
||||
$device->uptime( $dev_uptime + 2**32 );
|
||||
$dev_uptime_wrapped = 1;
|
||||
}
|
||||
|
||||
if ($device->is_column_changed('uptime') and $lc) {
|
||||
if ($lc < $dev_uptime) {
|
||||
# ambiguous: lastchange could be sysUptime before or after wrap
|
||||
if ($dev_uptime > 30000 and $lc < 30000) {
|
||||
# uptime wrap more than 5min ago but lastchange within 5min
|
||||
# assume lastchange was directly after boot -> no action
|
||||
}
|
||||
else {
|
||||
# uptime wrap less than 5min ago or lastchange > 5min ago
|
||||
# to be on safe side, assume lastchange after counter wrap
|
||||
debug sprintf
|
||||
' [%s] interfaces - correcting LastChange for %s, assuming sysUptime wrap',
|
||||
$device->ip, $port;
|
||||
$lc += $dev_uptime_wrapped * 2**32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$interfaces{$port} = {
|
||||
port => $port,
|
||||
descr => $i_descr->{$entry},
|
||||
up => $i_up->{$entry},
|
||||
up_admin => $i_up_admin->{$entry},
|
||||
mac => $i_mac->{$entry},
|
||||
speed => $i_speed->{$entry},
|
||||
mtu => $i_mtu->{$entry},
|
||||
name => Encode::decode('UTF-8', $i_name->{$entry}),
|
||||
duplex => $i_duplex->{$entry},
|
||||
duplex_admin => $i_duplex_admin->{$entry},
|
||||
stp => $i_stp_state->{$entry},
|
||||
type => $i_type->{$entry},
|
||||
vlan => $i_vlan->{$entry},
|
||||
pvid => $i_vlan->{$entry},
|
||||
is_master => 'false',
|
||||
slave_of => undef,
|
||||
lastchange => $lc,
|
||||
};
|
||||
}
|
||||
|
||||
# must do this after building %interfaces so that we can set is_master
|
||||
foreach my $sidx (keys %$agg_ports) {
|
||||
my $slave = $interfaces->{$sidx} or next;
|
||||
my $master = $interfaces->{ $agg_ports->{$sidx} } or next;
|
||||
next unless exists $interfaces{$slave} and exists $interfaces{$master};
|
||||
|
||||
$interfaces{$slave}->{slave_of} = $master;
|
||||
$interfaces{$master}->{is_master} = 'true';
|
||||
}
|
||||
|
||||
schema('netdisco')->resultset('DevicePort')->txn_do_locked(sub {
|
||||
my $gone = $device->ports->delete({keep_nodes => 1});
|
||||
debug sprintf ' [%s] interfaces - removed %d interfaces',
|
||||
$device->ip, $gone;
|
||||
$device->update_or_insert(undef, {for => 'update'});
|
||||
$device->ports->populate([values %interfaces]);
|
||||
debug sprintf ' [%s] interfaces - added %d new interfaces',
|
||||
$device->ip, scalar values %interfaces;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
335
lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm
Normal file
335
lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm
Normal file
@@ -0,0 +1,335 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::Neighbors;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Device
|
||||
qw/get_device match_devicetype is_discoverable/;
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
use App::Netdisco::JobQueue 'jq_insert';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use List::MoreUtils ();
|
||||
use NetAddr::MAC;
|
||||
use Encode;
|
||||
|
||||
=head2 discover_new_neighbors( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's port neighbors information.
|
||||
|
||||
Entries in the Topology database table will override any discovered device
|
||||
port relationships.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
Any discovered neighbor unknown to Netdisco will have a C<discover> job
|
||||
immediately queued (subject to the filtering by the C<discover_*> settings).
|
||||
|
||||
=cut
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my @to_discover = store_neighbors($device, $snmp);
|
||||
|
||||
# only enqueue if device is not already discovered,
|
||||
# discover_* config permits the discovery
|
||||
foreach my $neighbor (@to_discover) {
|
||||
my ($ip, $remote_type) = @$neighbor;
|
||||
|
||||
my $device = get_device($ip);
|
||||
next if $device->in_storage;
|
||||
|
||||
if (not is_discoverable($device, $remote_type)) {
|
||||
debug sprintf
|
||||
' queue - %s, type [%s] excluded by discover_* config',
|
||||
$ip, ($remote_type || '');
|
||||
next;
|
||||
}
|
||||
|
||||
jq_insert({
|
||||
device => $ip,
|
||||
action => 'discover',
|
||||
subaction => 'with-nodes',
|
||||
});
|
||||
}
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
=head2 store_neighbors( $device, $snmp )
|
||||
|
||||
returns: C<@to_discover>
|
||||
|
||||
Given a Device database object, and a working SNMP connection, discover and
|
||||
store the device's port neighbors information.
|
||||
|
||||
Entries in the Topology database table will override any discovered device
|
||||
port relationships.
|
||||
|
||||
The Device database object can be a fresh L<DBIx::Class::Row> object which is
|
||||
not yet stored to the database.
|
||||
|
||||
A list of discovererd neighbors will be returned as [C<$ip>, C<$type>] tuples.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_neighbors {
|
||||
my ($device, $snmp) = @_;
|
||||
my @to_discover = ();
|
||||
|
||||
# first allow any manually configured topology to be set
|
||||
set_manual_topology($device, $snmp);
|
||||
|
||||
if (!defined $snmp->has_topo) {
|
||||
debug sprintf ' [%s] neigh - neighbor protocols are not enabled', $device->ip;
|
||||
return @to_discover;
|
||||
}
|
||||
|
||||
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;
|
||||
my $c_cap = $snmp->c_cap;
|
||||
|
||||
# v4 and v6 neighbor tables
|
||||
my $c_ip = ($snmp->c_ip || {});
|
||||
my %c_ipv6 = %{ ($snmp->can('hasLLDP') and $snmp->hasLLDP)
|
||||
? ($snmp->lldp_ipv6 || {}) : {} };
|
||||
|
||||
# remove keys with undef values, as c_ip does
|
||||
delete @c_ipv6{ grep { not defined $c_ipv6{$_} } keys %c_ipv6 };
|
||||
# now combine them, v6 wins
|
||||
$c_ip = { %$c_ip, %c_ipv6 };
|
||||
|
||||
foreach my $entry (sort (List::MoreUtils::uniq( (keys %$c_ip), (keys %$c_cap) ))) {
|
||||
if (!defined $c_if->{$entry} or !defined $interfaces->{ $c_if->{$entry} }) {
|
||||
debug sprintf ' [%s] neigh - port for IID:%s not resolved, skipping',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
my $port = $interfaces->{ $c_if->{$entry} };
|
||||
my $portrow = schema('netdisco')->resultset('DevicePort')
|
||||
->single({ip => $device->ip, port => $port});
|
||||
|
||||
if (!defined $portrow) {
|
||||
info sprintf ' [%s] neigh - local port %s not in database!',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
if (ref $c_ip->{$entry}) {
|
||||
error sprintf ' [%s] neigh - Error! port %s has multiple neighbors - skipping',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
my $remote_ip = $c_ip->{$entry};
|
||||
my $remote_port = undef;
|
||||
my $remote_type = Encode::decode('UTF-8', $c_platform->{$entry} || '');
|
||||
my $remote_id = Encode::decode('UTF-8', $c_id->{$entry});
|
||||
my $remote_cap = $c_cap->{$entry} || [];
|
||||
|
||||
# IP Phone and WAP detection type fixup
|
||||
if (scalar @$remote_cap or $remote_type) {
|
||||
my $phone_flag = grep {match_devicetype($_, 'phone_capabilities')}
|
||||
@$remote_cap;
|
||||
my $ap_flag = grep {match_devicetype($_, 'wap_capabilities')}
|
||||
@$remote_cap;
|
||||
|
||||
if ($phone_flag or match_devicetype($remote_type, 'phone_platforms')) {
|
||||
$remote_type = 'IP Phone: '. $remote_type
|
||||
if $remote_type !~ /ip.phone/i;
|
||||
}
|
||||
elsif ($ap_flag or match_devicetype($remote_type, 'wap_platforms')) {
|
||||
$remote_type = 'AP: '. $remote_type;
|
||||
}
|
||||
|
||||
$portrow->update({remote_type => $remote_type});
|
||||
}
|
||||
|
||||
if ($portrow->manual_topo) {
|
||||
info sprintf ' [%s] neigh - %s has manually defined topology',
|
||||
$device->ip, $port;
|
||||
next;
|
||||
}
|
||||
|
||||
next unless $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
|
||||
check_acl_no($remote_ip, 'group:__LOCAL_ADDRESSES__')) {
|
||||
|
||||
if ($remote_id) {
|
||||
my $devices = schema('netdisco')->resultset('Device');
|
||||
my $neigh = $devices->single({name => $remote_id});
|
||||
info sprintf
|
||||
' [%s] neigh - bad address %s on port %s, searching for %s instead',
|
||||
$device->ip, $remote_ip, $port, $remote_id;
|
||||
|
||||
if (!defined $neigh) {
|
||||
my $mac = NetAddr::MAC->new(mac => $remote_id);
|
||||
if ($mac and not $mac->errstr) {
|
||||
$neigh = $devices->single({mac => $mac->as_ieee});
|
||||
}
|
||||
}
|
||||
|
||||
# some HP switches send 127.0.0.1 as remote_ip if no ip address
|
||||
# on default vlan for HP switches remote_ip looks like
|
||||
# "myswitchname(012345-012345)"
|
||||
if (!defined $neigh) {
|
||||
(my $tmpid = $remote_id) =~ s/.*\(([0-9a-f]{6})-([0-9a-f]{6})\).*/$1$2/;
|
||||
my $mac = NetAddr::MAC->new(mac => $tmpid);
|
||||
if ($mac and not $mac->errstr) {
|
||||
info sprintf
|
||||
'[%s] neigh - found neighbor %s by MAC %s',
|
||||
$device->ip, $remote_id, $mac->as_ieee;
|
||||
$neigh = $devices->single({mac => $mac->as_ieee});
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $neigh) {
|
||||
(my $shortid = $remote_id) =~ s/\..*//;
|
||||
$neigh = $devices->single({name => { -ilike => "${shortid}%" }});
|
||||
}
|
||||
|
||||
if ($neigh) {
|
||||
$remote_ip = $neigh->ip;
|
||||
info sprintf ' [%s] neigh - found %s with IP %s',
|
||||
$device->ip, $remote_id, $remote_ip;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - could not find %s, skipping',
|
||||
$device->ip, $remote_id;
|
||||
next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - skipping unuseable address %s on port %s',
|
||||
$device->ip, $remote_ip, $port;
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
# what we came here to do.... discover the neighbor
|
||||
debug sprintf
|
||||
' [%s] neigh - adding neighbor %s, type [%s], on %s to discovery queue',
|
||||
$device->ip, $remote_ip, ($remote_type || ''), $port;
|
||||
push @to_discover, [$remote_ip, $remote_type];
|
||||
|
||||
$remote_port = $c_port->{$entry};
|
||||
if (defined $remote_port) {
|
||||
# clean weird characters
|
||||
$remote_port =~ s/[^\d\/\.,()\w:-]+//gi;
|
||||
}
|
||||
else {
|
||||
info sprintf ' [%s] neigh - no remote port found for port %s at %s',
|
||||
$device->ip, $port, $remote_ip;
|
||||
}
|
||||
|
||||
$portrow->update({
|
||||
remote_ip => $remote_ip,
|
||||
remote_port => $remote_port,
|
||||
remote_type => $remote_type,
|
||||
remote_id => $remote_id,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"false",
|
||||
});
|
||||
|
||||
# update master of our aggregate to be a neighbor of
|
||||
# the master on our peer device (a lot of iffs to get there...).
|
||||
# & cannot use ->neighbor prefetch because this is the port insert!
|
||||
if (defined $portrow->slave_of) {
|
||||
|
||||
my $peer_device = get_device($remote_ip);
|
||||
my $master = schema('netdisco')->resultset('DevicePort')->single({
|
||||
ip => $device->ip,
|
||||
port => $portrow->slave_of
|
||||
});
|
||||
|
||||
if ($peer_device and $peer_device->in_storage and $master
|
||||
and not ($portrow->is_master or defined $master->slave_of)) {
|
||||
|
||||
my $peer_port = schema('netdisco')->resultset('DevicePort')->single({
|
||||
ip => $peer_device->ip,
|
||||
port => $portrow->remote_port,
|
||||
});
|
||||
|
||||
$master->update({
|
||||
remote_ip => ($peer_device->ip || $remote_ip),
|
||||
remote_port => ($peer_port ? $peer_port->slave_of : undef ),
|
||||
is_uplink => \"true",
|
||||
is_master => \"true",
|
||||
manual_topo => \"false",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @to_discover;
|
||||
}
|
||||
|
||||
# take data from the topology table and update remote_ip and remote_port
|
||||
# in the devices table. only use root_ips and skip any bad topo entries.
|
||||
sub set_manual_topology {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# clear manual topology flags
|
||||
schema('netdisco')->resultset('DevicePort')
|
||||
->search({ip => $device->ip})->update({manual_topo => \'false'});
|
||||
|
||||
my $topo_links = schema('netdisco')->resultset('Topology')
|
||||
->search({-or => [dev1 => $device->ip, dev2 => $device->ip]});
|
||||
debug sprintf ' [%s] neigh - setting manual topology links', $device->ip;
|
||||
|
||||
while (my $link = $topo_links->next) {
|
||||
# could fail for broken topo, but we ignore to try the rest
|
||||
try {
|
||||
schema('netdisco')->txn_do(sub {
|
||||
# only work on root_ips
|
||||
my $left = get_device($link->dev1);
|
||||
my $right = get_device($link->dev2);
|
||||
|
||||
# skip bad entries
|
||||
return unless ($left->in_storage and $right->in_storage);
|
||||
|
||||
$left->ports
|
||||
->single({port => $link->port1})
|
||||
->update({
|
||||
remote_ip => $right->ip,
|
||||
remote_port => $link->port2,
|
||||
remote_type => undef,
|
||||
remote_id => undef,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"true",
|
||||
});
|
||||
|
||||
$right->ports
|
||||
->single({port => $link->port2})
|
||||
->update({
|
||||
remote_ip => $left->ip,
|
||||
remote_port => $link->port1,
|
||||
remote_type => undef,
|
||||
remote_id => undef,
|
||||
is_uplink => \"true",
|
||||
manual_topo => \"true",
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
true;
|
||||
82
lib/App/Netdisco/Worker/Plugin/Discover/PortPower.pm
Normal file
82
lib/App/Netdisco/Worker/Plugin/Discover/PortPower.pm
Normal file
@@ -0,0 +1,82 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::PortPower;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $p_watts = $snmp->peth_power_watts;
|
||||
my $p_status = $snmp->peth_power_status;
|
||||
|
||||
if (!defined $p_watts) {
|
||||
debug sprintf ' [%s] power - 0 power modules', $device->ip;
|
||||
return Status->done("Ended discover for $device");
|
||||
}
|
||||
|
||||
# build device module power info suitable for DBIC
|
||||
my @devicepower;
|
||||
foreach my $entry (keys %$p_watts) {
|
||||
push @devicepower, {
|
||||
module => $entry,
|
||||
power => $p_watts->{$entry},
|
||||
status => $p_status->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $p_ifindex = $snmp->peth_port_ifindex;
|
||||
my $p_admin = $snmp->peth_port_admin;
|
||||
my $p_pstatus = $snmp->peth_port_status;
|
||||
my $p_class = $snmp->peth_port_class;
|
||||
my $p_power = $snmp->peth_port_power;
|
||||
|
||||
# build device port power info suitable for DBIC
|
||||
my @portpower;
|
||||
foreach my $entry (keys %$p_ifindex) {
|
||||
my $port = $interfaces->{ $p_ifindex->{$entry} };
|
||||
next unless $port;
|
||||
|
||||
my ($module) = split m/\./, $entry;
|
||||
|
||||
push @portpower, {
|
||||
port => $port,
|
||||
module => $module,
|
||||
admin => $p_admin->{$entry},
|
||||
status => $p_pstatus->{$entry},
|
||||
class => $p_class->{$entry},
|
||||
power => $p_power->{$entry},
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->power_modules->delete;
|
||||
debug sprintf ' [%s] power - removed %d power modules',
|
||||
$device->ip, $gone;
|
||||
$device->power_modules->populate(\@devicepower);
|
||||
debug sprintf ' [%s] power - added %d new power modules',
|
||||
$device->ip, scalar @devicepower;
|
||||
});
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->powered_ports->delete;
|
||||
debug sprintf ' [%s] power - removed %d PoE capable ports',
|
||||
$device->ip, $gone;
|
||||
$device->powered_ports->populate(\@portpower);
|
||||
debug sprintf ' [%s] power - added %d new PoE capable ports',
|
||||
$device->ip, scalar @portpower;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
105
lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm
Normal file
105
lib/App/Netdisco/Worker/Plugin/Discover/Properties.pm
Normal file
@@ -0,0 +1,105 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::Properties;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use App::Netdisco::Util::Permission 'check_acl_no';
|
||||
use App::Netdisco::Util::FastResolver 'hostnames_resolve_async';
|
||||
use App::Netdisco::Util::DNS 'hostname_from_ip';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use NetAddr::IP::Lite ':lower';
|
||||
use Encode;
|
||||
|
||||
register_worker({ primary => true, driver => 'snmp' }, 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");
|
||||
|
||||
my $ip_index = $snmp->ip_index;
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $ip_netmask = $snmp->ip_netmask;
|
||||
|
||||
# build device aliases suitable for DBIC
|
||||
my @aliases;
|
||||
foreach my $entry (keys %$ip_index) {
|
||||
my $ip = NetAddr::IP::Lite->new($entry)
|
||||
or next;
|
||||
my $addr = $ip->addr;
|
||||
|
||||
next if $addr eq '0.0.0.0';
|
||||
next if check_acl_no($ip, 'group:__LOCAL_ADDRESSES__');
|
||||
next if setting('ignore_private_nets') and $ip->is_rfc1918;
|
||||
|
||||
my $iid = $ip_index->{$addr};
|
||||
my $port = $interfaces->{$iid};
|
||||
my $subnet = $ip_netmask->{$addr}
|
||||
? NetAddr::IP::Lite->new($addr, $ip_netmask->{$addr})->network->cidr
|
||||
: undef;
|
||||
|
||||
debug sprintf ' [%s] device - aliased as %s', $device->ip, $addr;
|
||||
push @aliases, {
|
||||
alias => $addr,
|
||||
port => $port,
|
||||
subnet => $subnet,
|
||||
dns => undef,
|
||||
};
|
||||
}
|
||||
|
||||
debug sprintf ' resolving %d aliases with max %d outstanding requests',
|
||||
scalar @aliases, $ENV{'PERL_ANYEVENT_MAX_OUTSTANDING_DNS'};
|
||||
my $resolved_aliases = hostnames_resolve_async(\@aliases);
|
||||
|
||||
# fake one aliases entry for devices not providing ip_index
|
||||
push @$resolved_aliases, { alias => $device->ip, dns => $device->dns }
|
||||
if 0 == scalar @aliases;
|
||||
|
||||
# VTP Management Domain -- assume only one.
|
||||
my $vtpdomains = $snmp->vtp_d_name;
|
||||
my $vtpdomain;
|
||||
if (defined $vtpdomains and scalar values %$vtpdomains) {
|
||||
$device->set_column( vtp_domain => (values %$vtpdomains)[-1] );
|
||||
}
|
||||
|
||||
my $hostname = hostname_from_ip($device->ip);
|
||||
$device->set_column( dns => $hostname ) if $hostname;
|
||||
|
||||
my @properties = qw/
|
||||
snmp_ver
|
||||
description uptime name
|
||||
layers ports mac
|
||||
ps1_type ps2_type ps1_status ps2_status
|
||||
fan slots
|
||||
vendor os os_ver
|
||||
/;
|
||||
|
||||
foreach my $property (@properties) {
|
||||
$device->set_column( $property => $snmp->$property );
|
||||
}
|
||||
|
||||
$device->set_column( model => Encode::decode('UTF-8', $snmp->model) );
|
||||
$device->set_column( serial => Encode::decode('UTF-8', $snmp->serial) );
|
||||
$device->set_column( contact => Encode::decode('UTF-8', $snmp->contact) );
|
||||
$device->set_column( location => Encode::decode('UTF-8', $snmp->location) );
|
||||
|
||||
|
||||
$device->set_column( snmp_class => $snmp->class );
|
||||
$device->set_column( last_discover => \'now()' );
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->device_ips->delete;
|
||||
debug sprintf ' [%s] device - removed %d aliases',
|
||||
$device->ip, $gone;
|
||||
$device->update_or_insert(undef, {for => 'update'});
|
||||
$device->device_ips->populate($resolved_aliases);
|
||||
debug sprintf ' [%s] device - added %d new aliases',
|
||||
$device->ip, scalar @aliases;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
95
lib/App/Netdisco/Worker/Plugin/Discover/VLANs.pm
Normal file
95
lib/App/Netdisco/Worker/Plugin/Discover/VLANs.pm
Normal file
@@ -0,0 +1,95 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::VLANs;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $v_name = $snmp->v_name;
|
||||
my $v_index = $snmp->v_index;
|
||||
|
||||
# build device vlans suitable for DBIC
|
||||
my %v_seen = ();
|
||||
my @devicevlans;
|
||||
foreach my $entry (keys %$v_name) {
|
||||
my $vlan = $v_index->{$entry};
|
||||
next unless defined $vlan and $vlan;
|
||||
++$v_seen{$vlan};
|
||||
|
||||
push @devicevlans, {
|
||||
vlan => $vlan,
|
||||
description => $v_name->{$entry},
|
||||
last_discover => \'now()',
|
||||
};
|
||||
}
|
||||
|
||||
my $i_vlan = $snmp->i_vlan;
|
||||
my $i_vlan_membership = $snmp->i_vlan_membership;
|
||||
my $i_vlan_type = $snmp->i_vlan_type;
|
||||
my $interfaces = $snmp->interfaces;
|
||||
|
||||
# build device port vlans suitable for DBIC
|
||||
my @portvlans = ();
|
||||
foreach my $entry (keys %$i_vlan_membership) {
|
||||
my %port_vseen = ();
|
||||
my $port = $interfaces->{$entry};
|
||||
next unless defined $port;
|
||||
|
||||
my $type = $i_vlan_type->{$entry};
|
||||
|
||||
foreach my $vlan (@{ $i_vlan_membership->{$entry} }) {
|
||||
next unless defined $vlan and $vlan;
|
||||
next if ++$port_vseen{$vlan} > 1;
|
||||
|
||||
my $native = ((defined $i_vlan->{$entry}) and ($vlan eq $i_vlan->{$entry})) ? "t" : "f";
|
||||
push @portvlans, {
|
||||
port => $port,
|
||||
vlan => $vlan,
|
||||
native => $native,
|
||||
vlantype => $type,
|
||||
last_discover => \'now()',
|
||||
};
|
||||
|
||||
next if $v_seen{$vlan};
|
||||
|
||||
# also add an unnamed vlan to the device
|
||||
push @devicevlans, {
|
||||
vlan => $vlan,
|
||||
description => (sprintf "VLAN %d", $vlan),
|
||||
last_discover => \'now()',
|
||||
};
|
||||
++$v_seen{$vlan};
|
||||
}
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->vlans->delete;
|
||||
debug sprintf ' [%s] vlans - removed %d device VLANs',
|
||||
$device->ip, $gone;
|
||||
$device->vlans->populate(\@devicevlans);
|
||||
debug sprintf ' [%s] vlans - added %d new device VLANs',
|
||||
$device->ip, scalar @devicevlans;
|
||||
});
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->port_vlans->delete;
|
||||
debug sprintf ' [%s] vlans - removed %d port VLANs',
|
||||
$device->ip, $gone;
|
||||
$device->port_vlans->populate(\@portvlans);
|
||||
debug sprintf ' [%s] vlans - added %d new port VLANs',
|
||||
$device->ip, scalar @portvlans;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
86
lib/App/Netdisco/Worker/Plugin/Discover/Wireless.pm
Normal file
86
lib/App/Netdisco/Worker/Plugin/Discover/Wireless.pm
Normal file
@@ -0,0 +1,86 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::Wireless;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => false, driver => 'snmp' }, 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");
|
||||
|
||||
my $ssidlist = $snmp->i_ssidlist;
|
||||
return Status->done("Ended discover for $device")
|
||||
unless scalar keys %$ssidlist;
|
||||
|
||||
my $interfaces = $snmp->interfaces;
|
||||
my $ssidbcast = $snmp->i_ssidbcast;
|
||||
my $ssidmac = $snmp->i_ssidmac;
|
||||
my $channel = $snmp->i_80211channel;
|
||||
my $power = $snmp->dot11_cur_tx_pwr_mw;
|
||||
|
||||
# build device ssid list suitable for DBIC
|
||||
my @ssids;
|
||||
foreach my $entry (keys %$ssidlist) {
|
||||
(my $iid = $entry) =~ s/\.\d+$//;
|
||||
my $port = $interfaces->{$iid};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] wireless - ignoring %s (no port mapping)',
|
||||
$device->ip, $iid;
|
||||
next;
|
||||
}
|
||||
|
||||
push @ssids, {
|
||||
port => $port,
|
||||
ssid => $ssidlist->{$entry},
|
||||
broadcast => $ssidbcast->{$entry},
|
||||
bssid => $ssidmac->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->ssids->delete;
|
||||
debug sprintf ' [%s] wireless - removed %d SSIDs',
|
||||
$device->ip, $gone;
|
||||
$device->ssids->populate(\@ssids);
|
||||
debug sprintf ' [%s] wireless - added %d new SSIDs',
|
||||
$device->ip, scalar @ssids;
|
||||
});
|
||||
|
||||
# build device channel list suitable for DBIC
|
||||
my @channels;
|
||||
foreach my $entry (keys %$channel) {
|
||||
my $port = $interfaces->{$entry};
|
||||
|
||||
if (not $port) {
|
||||
debug sprintf ' [%s] wireless - ignoring %s (no port mapping)',
|
||||
$device->ip, $entry;
|
||||
next;
|
||||
}
|
||||
|
||||
push @channels, {
|
||||
port => $port,
|
||||
channel => $channel->{$entry},
|
||||
power => $power->{$entry},
|
||||
};
|
||||
}
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
my $gone = $device->wireless_ports->delete;
|
||||
debug sprintf ' [%s] wireless - removed %d wireless channels',
|
||||
$device->ip, $gone;
|
||||
$device->wireless_ports->populate(\@channels);
|
||||
debug sprintf ' [%s] wireless - added %d new wireless channels',
|
||||
$device->ip, scalar @channels;
|
||||
});
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
39
lib/App/Netdisco/Worker/Plugin/Discover/WithNodes.pm
Normal file
39
lib/App/Netdisco/Worker/Plugin/Discover/WithNodes.pm
Normal file
@@ -0,0 +1,39 @@
|
||||
package App::Netdisco::Worker::Plugin::Discover::WithNodes;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => false }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
my $device = $job->device;
|
||||
|
||||
# if requested, and the device has not yet been
|
||||
# arpniped/macsucked, queue those jobs now
|
||||
if ($device->in_storage
|
||||
and $job->subaction and $job->subaction eq 'with-nodes') {
|
||||
if (!defined $device->last_macsuck) {
|
||||
jq_insert({
|
||||
device => $device->ip,
|
||||
action => 'macsuck',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
});
|
||||
}
|
||||
|
||||
if (!defined $device->last_arpnip) {
|
||||
jq_insert({
|
||||
device => $device->ip,
|
||||
action => 'arpnip',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Status->done('Ended discover for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
31
lib/App/Netdisco/Worker/Plugin/DiscoverAll.pm
Normal file
31
lib/App/Netdisco/Worker/Plugin/DiscoverAll.pm
Normal file
@@ -0,0 +1,31 @@
|
||||
package App::Netdisco::Worker::Plugin::DiscoverAll;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::JobQueue qw/jq_queued jq_insert/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
|
||||
register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
|
||||
my %queued = map {$_ => 1} jq_queued('discover');
|
||||
my @devices = schema('netdisco')->resultset('Device')->search({
|
||||
-or => [ 'vendor' => undef, 'vendor' => { '!=' => 'netdisco' }],
|
||||
})->get_column('ip')->all;
|
||||
my @filtered_devices = grep {!exists $queued{$_}} @devices;
|
||||
|
||||
jq_insert([
|
||||
map {{
|
||||
device => $_,
|
||||
action => 'discover',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
}} (@filtered_devices)
|
||||
]);
|
||||
|
||||
return Status->done('Queued discover job for all devices');
|
||||
});
|
||||
|
||||
true;
|
||||
@@ -5,7 +5,6 @@ use App::Netdisco::Worker::Plugin;
|
||||
use aliased 'App::Netdisco::Worker::Status';
|
||||
|
||||
use App::Netdisco::Util::Device 'is_macsuckable_now';
|
||||
use App::Netdisco::Transport::SNMP ();
|
||||
|
||||
register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
@@ -14,15 +13,13 @@ register_worker({ primary => true }, sub {
|
||||
return Status->error('macsuck failed: unable to interpret device param')
|
||||
unless defined $device;
|
||||
|
||||
my $host = $device->ip;
|
||||
|
||||
return Status->done("macsuck skipped: $host not yet discovered")
|
||||
return Status->done("macsuck skipped: $device not yet discovered")
|
||||
unless $device->in_storage;
|
||||
|
||||
return Status->defer("macsuck skipped: $host is pseudo-device")
|
||||
return Status->defer("macsuck skipped: $device is pseudo-device")
|
||||
if $device->vendor and $device->vendor eq 'netdisco';
|
||||
|
||||
return Status->defer("macsuck deferred: $host is not macsuckable")
|
||||
return Status->defer("macsuck deferred: $device is not macsuckable")
|
||||
unless is_macsuckable_now($device);
|
||||
|
||||
return Status->done('Macsuck is able to run.');
|
||||
|
||||
@@ -19,9 +19,9 @@ register_worker({ primary => true, driver => 'snmp' }, sub {
|
||||
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||
or return Status->defer("macsuck failed: could not SNMP connect to $host");
|
||||
or return Status->defer("macsuck failed: could not SNMP connect to $device");
|
||||
|
||||
return Status->defer("Skipped macsuck for device $host without layer 2 capability")
|
||||
return Status->defer("Skipped macsuck for device $device without layer 2 capability")
|
||||
unless $snmp->has_layer(2);
|
||||
|
||||
# would be possible just to use now() on updated records, but by using this
|
||||
|
||||
@@ -13,9 +13,9 @@ register_worker({ primary => false, driver => 'snmp' }, sub {
|
||||
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device)
|
||||
or return Status->defer("macsuck failed: could not SNMP connect to $host");
|
||||
or return Status->defer("macsuck failed: could not SNMP connect to $device");
|
||||
|
||||
return Status->defer("Skipped macsuck for device $host without layer 2 capability")
|
||||
return Status->defer("Skipped macsuck for device $device without layer 2 capability")
|
||||
unless $snmp->has_layer(2);
|
||||
|
||||
my $now = 'to_timestamp('. (join '.', gettimeofday) .')';
|
||||
|
||||
Reference in New Issue
Block a user