move macsuck and macwalk to worker plugins (macsuck needs snmp scope guard)
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
package App::Netdisco::Backend::Worker::Poller::Macsuck;
|
||||
|
||||
use App::Netdisco::Core::Macsuck 'do_macsuck';
|
||||
use App::Netdisco::Util::Device 'is_macsuckable_now';
|
||||
|
||||
use Role::Tiny;
|
||||
use namespace::clean;
|
||||
|
||||
with 'App::Netdisco::Backend::Worker::Poller::Common';
|
||||
|
||||
sub macsuck_action { \&do_macsuck }
|
||||
sub macsuck_filter { \&is_macsuckable_now }
|
||||
sub macsuck_layer { 2 }
|
||||
|
||||
sub macwalk { (shift)->_walk_body('macsuck', @_) }
|
||||
sub macsuck { (shift)->_single_body('macsuck', @_) }
|
||||
|
||||
1;
|
||||
38
lib/App/Netdisco/Worker/Plugin/Macsuck.pm
Normal file
38
lib/App/Netdisco/Worker/Plugin/Macsuck.pm
Normal file
@@ -0,0 +1,38 @@
|
||||
package App::Netdisco::Worker::Plugin::Macsuck;
|
||||
|
||||
use Dancer ':syntax';
|
||||
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) = @_;
|
||||
my $device = $job->device;
|
||||
|
||||
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")
|
||||
unless $device->in_storage;
|
||||
|
||||
return Status->done("macsuck skipped: $host is pseudo-device")
|
||||
if $device->vendor and $device->vendor eq 'netdisco';
|
||||
|
||||
return Status->defer("macsuck deferred: $host is not macsuckable")
|
||||
unless is_macsuckable_now($device);
|
||||
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device);
|
||||
return Status->defer("macsuck failed: could not SNMP connect to $host")
|
||||
unless defined $snmp;
|
||||
|
||||
return Status->done("Skipped macsuck for device $host without layer 2 capability")
|
||||
unless $snmp->has_layer(2);
|
||||
|
||||
return Status->done('Macsuck is able to run.');
|
||||
});
|
||||
|
||||
true;
|
||||
@@ -1,62 +1,24 @@
|
||||
package App::Netdisco::Core::Macsuck;
|
||||
package App::Netdisco::Worker::Plugin::Macsuck::Nodes;
|
||||
|
||||
use Dancer qw/:syntax :script/;
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
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::PortMAC 'get_port_macs';
|
||||
use App::Netdisco::Util::Device 'match_devicetype';
|
||||
use App::Netdisco::Util::Node 'check_mac';
|
||||
use App::Netdisco::Util::SNMP 'snmp_comm_reindex';
|
||||
use Dancer::Plugin::DBIC 'schema';
|
||||
use Time::HiRes 'gettimeofday';
|
||||
use Scope::Guard 'guard';
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT = ();
|
||||
our @EXPORT_OK = qw/
|
||||
do_macsuck
|
||||
store_node
|
||||
store_wireless_client_info
|
||||
/;
|
||||
our %EXPORT_TAGS = (all => \@EXPORT_OK);
|
||||
register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
App::Netdisco::Core::Macsuck
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
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 do_macsuck( $device, $snmp )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, connect to a
|
||||
device and discover the MAC addresses listed against each physical port
|
||||
without a neighbor.
|
||||
|
||||
If the device has VLANs, C<do_macsuck> will walk each VLAN to get the MAC
|
||||
addresses from there.
|
||||
|
||||
It will also gather wireless client information if C<store_wireless_clients>
|
||||
configuration setting is enabled.
|
||||
|
||||
=cut
|
||||
|
||||
sub do_macsuck {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
unless ($device->in_storage) {
|
||||
debug sprintf
|
||||
' [%s] macsuck - skipping device not yet discovered',
|
||||
$device->ip;
|
||||
return;
|
||||
}
|
||||
|
||||
my $ip = $device->ip;
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device);
|
||||
|
||||
# would be possible just to use now() on updated records, but by using this
|
||||
# same value for them all, we can if we want add a job at the end to
|
||||
@@ -65,7 +27,7 @@ sub do_macsuck {
|
||||
my $total_nodes = 0;
|
||||
|
||||
# do this before we start messing with the snmp community string
|
||||
store_wireless_client_info($device, $snmp, $now);
|
||||
my $guard = guard { }; # FIXME TODO reset snmp community
|
||||
|
||||
# cache the device ports to save hitting the database for many single rows
|
||||
my $device_ports = {map {($_->port => $_)}
|
||||
@@ -74,13 +36,13 @@ sub do_macsuck {
|
||||
my $interfaces = $snmp->interfaces;
|
||||
|
||||
# get forwarding table data via basic snmp connection
|
||||
my $fwtable = _walk_fwtable($device, $snmp, $interfaces, $port_macs, $device_ports);
|
||||
my $fwtable = walk_fwtable($device, $snmp, $interfaces, $port_macs, $device_ports);
|
||||
|
||||
# ...then per-vlan if supported
|
||||
my @vlan_list = _get_vlan_list($device, $snmp);
|
||||
my @vlan_list = get_vlan_list($device, $snmp);
|
||||
foreach my $vlan (@vlan_list) {
|
||||
snmp_comm_reindex($snmp, $device, $vlan);
|
||||
my $pv_fwtable = _walk_fwtable($device, $snmp, $interfaces, $port_macs, $device_ports, $vlan);
|
||||
my $pv_fwtable = walk_fwtable($device, $snmp, $interfaces, $port_macs, $device_ports, $vlan);
|
||||
$fwtable = {%$fwtable, %$pv_fwtable};
|
||||
}
|
||||
|
||||
@@ -91,7 +53,7 @@ sub do_macsuck {
|
||||
foreach my $vlan (reverse sort keys %$fwtable) {
|
||||
foreach my $port (keys %{ $fwtable->{$vlan} }) {
|
||||
debug sprintf ' [%s] macsuck - port %s vlan %s : %s nodes',
|
||||
$ip, $port, $vlan, scalar keys %{ $fwtable->{$vlan}->{$port} };
|
||||
$device->ip, $port, $vlan, scalar keys %{ $fwtable->{$vlan}->{$port} };
|
||||
|
||||
# make sure this port is UP in netdisco (unless it's a lag master,
|
||||
# because we can still see nodes without a functioning aggregate)
|
||||
@@ -105,30 +67,31 @@ sub do_macsuck {
|
||||
for keys %{ $fwtable->{0} };
|
||||
|
||||
++$total_nodes;
|
||||
store_node($ip, $vlan, $port, $mac, $now);
|
||||
store_node($device->ip, $vlan, $port, $mac, $now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug sprintf ' [%s] macsuck - %s updated forwarding table entries',
|
||||
$ip, $total_nodes;
|
||||
$device->ip, $total_nodes;
|
||||
|
||||
# a use for $now ... need to archive dissapeared nodes
|
||||
my $archived = 0;
|
||||
|
||||
if (setting('node_freshness')) {
|
||||
$archived = schema('netdisco')->resultset('Node')->search({
|
||||
switch => $ip,
|
||||
switch => $device->ip,
|
||||
time_last => \[ "< ($now - ?::interval)",
|
||||
setting('node_freshness') .' minutes' ],
|
||||
})->update({ active => \'false' });
|
||||
}
|
||||
|
||||
debug sprintf ' [%s] macsuck - removed %d fwd table entries to archive',
|
||||
$ip, $archived;
|
||||
$device->ip, $archived;
|
||||
|
||||
$device->update({last_macsuck => \$now});
|
||||
}
|
||||
return Status->done('Ended macsuck for '. $device->ip);
|
||||
});
|
||||
|
||||
=head2 store_node( $ip, $vlan, $port, $mac, $now? )
|
||||
|
||||
@@ -183,7 +146,7 @@ sub store_node {
|
||||
}
|
||||
|
||||
# return a list of vlan numbers which are OK to macsuck on this device
|
||||
sub _get_vlan_list {
|
||||
sub get_vlan_list {
|
||||
my ($device, $snmp) = @_;
|
||||
|
||||
return () unless $snmp->cisco_comm_indexing;
|
||||
@@ -295,7 +258,7 @@ sub _get_vlan_list {
|
||||
|
||||
# walks the forwarding table (BRIDGE-MIB) for the device and returns a
|
||||
# table of node entries.
|
||||
sub _walk_fwtable {
|
||||
sub walk_fwtable {
|
||||
my ($device, $snmp, $interfaces, $port_macs, $device_ports, $comm_vlan) = @_;
|
||||
my $skiplist = {}; # ports through which we can see another device
|
||||
my $cache = {};
|
||||
@@ -455,90 +418,4 @@ sub _walk_fwtable {
|
||||
return $cache;
|
||||
}
|
||||
|
||||
=head2 store_wireless_client_info( $device, $snmp, $now? )
|
||||
|
||||
Given a Device database object, and a working SNMP connection, connect to a
|
||||
device and discover 802.11 related information for all connected wireless
|
||||
clients.
|
||||
|
||||
If the device doesn't support the 802.11 MIBs, then this will silently return.
|
||||
|
||||
If the device does support the 802.11 MIBs but Netdisco's configuration
|
||||
does not permit polling (C<store_wireless_clients> must be true) then a debug
|
||||
message is logged and the subroutine returns.
|
||||
|
||||
Otherwise, client information is gathered and stored to the database.
|
||||
|
||||
Optionally, a third argument can be the literal string passed to the time_last
|
||||
field of the database record. If not provided, it defauls to C<now()>.
|
||||
|
||||
=cut
|
||||
|
||||
sub store_wireless_client_info {
|
||||
my ($device, $snmp, $now) = @_;
|
||||
$now ||= 'now()';
|
||||
|
||||
my $cd11_txrate = $snmp->cd11_txrate;
|
||||
return unless $cd11_txrate and scalar keys %$cd11_txrate;
|
||||
|
||||
if (setting('store_wireless_clients')) {
|
||||
debug sprintf ' [%s] macsuck - gathering wireless client info',
|
||||
$device->ip;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] macsuck - dot11 info available but skipped due to config',
|
||||
$device->ip;
|
||||
return;
|
||||
}
|
||||
|
||||
my $cd11_rateset = $snmp->cd11_rateset();
|
||||
my $cd11_uptime = $snmp->cd11_uptime();
|
||||
my $cd11_sigstrength = $snmp->cd11_sigstrength();
|
||||
my $cd11_sigqual = $snmp->cd11_sigqual();
|
||||
my $cd11_mac = $snmp->cd11_mac();
|
||||
my $cd11_port = $snmp->cd11_port();
|
||||
my $cd11_rxpkt = $snmp->cd11_rxpkt();
|
||||
my $cd11_txpkt = $snmp->cd11_txpkt();
|
||||
my $cd11_rxbyte = $snmp->cd11_rxbyte();
|
||||
my $cd11_txbyte = $snmp->cd11_txbyte();
|
||||
my $cd11_ssid = $snmp->cd11_ssid();
|
||||
|
||||
while (my ($idx, $txrates) = each %$cd11_txrate) {
|
||||
my $rates = $cd11_rateset->{$idx};
|
||||
my $mac = $cd11_mac->{$idx};
|
||||
next unless defined $mac; # avoid null entries
|
||||
# there can be more rows in txrate than other tables
|
||||
|
||||
my $txrate = defined $txrates->[$#$txrates]
|
||||
? int($txrates->[$#$txrates])
|
||||
: undef;
|
||||
|
||||
my $maxrate = defined $rates->[$#$rates]
|
||||
? int($rates->[$#$rates])
|
||||
: undef;
|
||||
|
||||
my $ssid = $cd11_ssid->{$idx} || 'unknown';
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
schema('netdisco')->resultset('NodeWireless')
|
||||
->search({ 'me.mac' => $mac, 'me.ssid' => $ssid })
|
||||
->update_or_create({
|
||||
txrate => $txrate,
|
||||
maxrate => $maxrate,
|
||||
uptime => $cd11_uptime->{$idx},
|
||||
rxpkt => $cd11_rxpkt->{$idx},
|
||||
txpkt => $cd11_txpkt->{$idx},
|
||||
rxbyte => $cd11_rxbyte->{$idx},
|
||||
txbyte => $cd11_txbyte->{$idx},
|
||||
sigqual => $cd11_sigqual->{$idx},
|
||||
sigstrength => $cd11_sigstrength->{$idx},
|
||||
time_last => \$now,
|
||||
}, {
|
||||
order_by => [qw/mac ssid/],
|
||||
for => 'update',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
true;
|
||||
88
lib/App/Netdisco/Worker/Plugin/Macsuck/WirelessNodes.pm
Normal file
88
lib/App/Netdisco/Worker/Plugin/Macsuck/WirelessNodes.pm
Normal file
@@ -0,0 +1,88 @@
|
||||
package App::Netdisco::Worker::Plugin::Macsuck::WirelessNodes;
|
||||
|
||||
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 Time::HiRes 'gettimeofday';
|
||||
|
||||
register_worker({ primary => false }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
|
||||
my $device = $job->device;
|
||||
my $snmp = App::Netdisco::Transport::SNMP->reader_for($device);
|
||||
|
||||
# would be possible just to use now() on updated records, but by using this
|
||||
# same value for them all, we can if we want add a job at the end to
|
||||
# select and do something with the updated set (see set archive, below)
|
||||
my $now = 'to_timestamp('. (join '.', gettimeofday) .')';
|
||||
|
||||
my $cd11_txrate = $snmp->cd11_txrate;
|
||||
return Status->done('Ended macsuck for '. $device->ip)
|
||||
unless $cd11_txrate and scalar keys %$cd11_txrate;
|
||||
|
||||
if (setting('store_wireless_clients')) {
|
||||
debug sprintf ' [%s] macsuck - gathering wireless client info',
|
||||
$device->ip;
|
||||
}
|
||||
else {
|
||||
debug sprintf ' [%s] macsuck - dot11 info available but skipped due to config',
|
||||
$device->ip;
|
||||
return Status->done('Ended macsuck for '. $device->ip);
|
||||
}
|
||||
|
||||
my $cd11_rateset = $snmp->cd11_rateset();
|
||||
my $cd11_uptime = $snmp->cd11_uptime();
|
||||
my $cd11_sigstrength = $snmp->cd11_sigstrength();
|
||||
my $cd11_sigqual = $snmp->cd11_sigqual();
|
||||
my $cd11_mac = $snmp->cd11_mac();
|
||||
my $cd11_port = $snmp->cd11_port();
|
||||
my $cd11_rxpkt = $snmp->cd11_rxpkt();
|
||||
my $cd11_txpkt = $snmp->cd11_txpkt();
|
||||
my $cd11_rxbyte = $snmp->cd11_rxbyte();
|
||||
my $cd11_txbyte = $snmp->cd11_txbyte();
|
||||
my $cd11_ssid = $snmp->cd11_ssid();
|
||||
|
||||
while (my ($idx, $txrates) = each %$cd11_txrate) {
|
||||
my $rates = $cd11_rateset->{$idx};
|
||||
my $mac = $cd11_mac->{$idx};
|
||||
next unless defined $mac; # avoid null entries
|
||||
# there can be more rows in txrate than other tables
|
||||
|
||||
my $txrate = defined $txrates->[$#$txrates]
|
||||
? int($txrates->[$#$txrates])
|
||||
: undef;
|
||||
|
||||
my $maxrate = defined $rates->[$#$rates]
|
||||
? int($rates->[$#$rates])
|
||||
: undef;
|
||||
|
||||
my $ssid = $cd11_ssid->{$idx} || 'unknown';
|
||||
|
||||
schema('netdisco')->txn_do(sub {
|
||||
schema('netdisco')->resultset('NodeWireless')
|
||||
->search({ 'me.mac' => $mac, 'me.ssid' => $ssid })
|
||||
->update_or_create({
|
||||
txrate => $txrate,
|
||||
maxrate => $maxrate,
|
||||
uptime => $cd11_uptime->{$idx},
|
||||
rxpkt => $cd11_rxpkt->{$idx},
|
||||
txpkt => $cd11_txpkt->{$idx},
|
||||
rxbyte => $cd11_rxbyte->{$idx},
|
||||
txbyte => $cd11_txbyte->{$idx},
|
||||
sigqual => $cd11_sigqual->{$idx},
|
||||
sigstrength => $cd11_sigstrength->{$idx},
|
||||
time_last => \$now,
|
||||
}, {
|
||||
order_by => [qw/mac ssid/],
|
||||
for => 'update',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Status->done('Ended macsuck for '. $device->ip);
|
||||
});
|
||||
|
||||
true;
|
||||
31
lib/App/Netdisco/Worker/Plugin/Macwalk.pm
Normal file
31
lib/App/Netdisco/Worker/Plugin/Macwalk.pm
Normal file
@@ -0,0 +1,31 @@
|
||||
package App::Netdisco::Worker::Plugin::Macwalk;
|
||||
|
||||
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('macsuck');
|
||||
my @devices = schema('netdisco')->resultset('Device')->search({
|
||||
-or => [ 'vendor' => undef, 'vendor' => { '!=' => 'netdisco' }],
|
||||
})->has_layer('2')->get_column('ip')->all;
|
||||
my @filtered_devices = grep {!exists $queued{$_}} @devices;
|
||||
|
||||
jq_insert([
|
||||
map {{
|
||||
device => $_,
|
||||
action => 'macsuck',
|
||||
username => $job->username,
|
||||
userip => $job->userip,
|
||||
}} (@filtered_devices)
|
||||
]);
|
||||
|
||||
return Status->done('Queued macsuck job for all devices');
|
||||
});
|
||||
|
||||
true;
|
||||
@@ -10,7 +10,7 @@ register_worker({ primary => true }, sub {
|
||||
my ($job, $workerconf) = @_;
|
||||
|
||||
return Status->error('nbtstat failed: unable to interpret device param')
|
||||
if !defined $job->device;
|
||||
unless defined $job->device;
|
||||
|
||||
return Status->defer(sprintf 'nbtstat deferred: %s is not macsuckable', $job->device->ip)
|
||||
unless is_macsuckable($job->device);
|
||||
|
||||
Reference in New Issue
Block a user