diff --git a/Netdisco/Changes b/Netdisco/Changes index 3dba9f21..d4b7299e 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -12,6 +12,7 @@ * Run daemons as target binary's owning user (supports run control) * Clean up library path fiddling across all scripts * Rename housekeeping expiry task to be expire + * Refactor nbtstat to group probes by device where node was arped [BUG FIXES] diff --git a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm index 581e70f5..8d91171a 100644 --- a/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/Core/Nbtstat.pm @@ -5,7 +5,6 @@ use Dancer::Plugin::DBIC 'schema'; use App::Netdisco::Util::Node 'check_mac'; use NetAddr::IP::Lite ':lower'; -use Time::HiRes 'gettimeofday'; use Net::NBName; use base 'Exporter'; @@ -36,8 +35,7 @@ Returns whether a node is answering netbios calls or not. =cut sub do_nbtstat { - my $host = shift; - + my ($host, $now) = @_; my $ip = NetAddr::IP::Lite->new($host) or return; unless ( $ip->version() == 4 ) { @@ -46,7 +44,6 @@ sub do_nbtstat { } my $nb = Net::NBName->new; - my $ns = $nb->node_status( $ip->addr ); # Check for NetBIOS Info @@ -55,7 +52,7 @@ sub do_nbtstat { my $nbname = _filter_nbname( $ip->addr, $ns ); if ($nbname) { - store_nbt($nbname); + store_nbt($nbname, $now); } return 1; diff --git a/Netdisco/lib/App/Netdisco/DB/ResultSet/NodeIp.pm b/Netdisco/lib/App/Netdisco/DB/ResultSet/NodeIp.pm index 3557de7e..405dd01f 100644 --- a/Netdisco/lib/App/Netdisco/DB/ResultSet/NodeIp.pm +++ b/Netdisco/lib/App/Netdisco/DB/ResultSet/NodeIp.pm @@ -212,7 +212,7 @@ sub ip_version { die "ip_version input must be either 4 or 6\n" unless $version && ( $version == 4 || $version == 6 ); - return $rs->search_rs( \[ 'family(ip) = ?', $version ] ); + return $rs->search_rs( \[ 'family(me.ip) = ?', $version ] ); } 1; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Common.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Common.pm index b14b0ab6..9f3f5cd2 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Common.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Common.pm @@ -88,80 +88,23 @@ sub _single_body { return job_done("Ended $job_type for ". $host->addr); } -# _walk_nodes_body -# Queue a job for all active nodes that have been seen in the last -# configured days. -# -sub _walk_nodes_body { - my ($self, $job_type) = @_; - - my $action_method = $job_type .'_action'; - my $job_action = $self->$action_method; - - my $jobqueue = schema('netdisco')->resultset('Admin'); - my $rs = schema('netdisco')->resultset('NodeIp') - ->search({ip => { -not_in => - $jobqueue->search({ - device => { '!=' => undef}, - action => $job_type, - status => { -like => 'queued%' }, - })->get_column('device')->as_query - }, -bool => 'active'}); - - my $ip_version = $job_type .'_ip_version'; - my $job_ip_ver = $self->$ip_version; - - if ($job_ip_ver) { - $rs = $rs->ip_version($job_ip_ver) - } - - my $config_max_age = $job_type . '_max_age'; - my $max_age = setting($config_max_age); - - if ($max_age) { - my $interval = "$max_age day"; - $rs = $rs->search( - { time_last => \[ '>= now() - ?::interval', $interval ] } ); - } - - my @nodes = $rs->get_column('ip')->all; - - my $filter_method = $job_type .'_filter'; - my $job_filter = $self->$filter_method; - - my @filtered_nodes = grep {$job_filter->($_)} @nodes; - - schema('netdisco')->resultset('Admin')->txn_do_locked(sub { - $jobqueue->populate([ - map {{ - device => $_, - action => $job_type, - status => 'queued', - }} (@filtered_nodes) - ]); - }); - - return job_done("Queued $job_type job for all nodes"); -} - sub _single_node_body { - my ($self, $job_type, $job) = @_; + my ($self, $job_type, $node, $now) = @_; my $action_method = $job_type .'_action'; my $job_action = $self->$action_method; - my $host = NetAddr::IP::Lite->new($job->device); - my $filter_method = $job_type .'_filter'; my $job_filter = $self->$filter_method; - unless ($job_filter->($host->addr)) { - return job_defer("$job_type deferred: $host is not ${job_type}able"); + unless ($job_filter->($node)) { + return job_defer("$job_type deferred: $node is not ${job_type}able"); } - $job_action->($host->addr); + $job_action->($node, $now); - return job_done("Ended $job_type for ". $host->addr); + # would be ignored if wrapped in a loop + return job_done("Ended $job_type for $node"); } 1; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm index 53af8dcc..f787d3fd 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Nbtstat.pm @@ -1,7 +1,15 @@ package App::Netdisco::Daemon::Worker::Poller::Nbtstat; +use Dancer qw/:moose :syntax :script/; +use Dancer::Plugin::DBIC 'schema'; + use App::Netdisco::Core::Nbtstat 'do_nbtstat'; use App::Netdisco::Util::Node 'is_nbtstatable'; +use App::Netdisco::Util::Device qw/get_device is_discoverable/; +use App::Netdisco::Daemon::Util ':all'; + +use NetAddr::IP::Lite ':lower'; +use Time::HiRes 'gettimeofday'; use Role::Tiny; use namespace::clean; @@ -10,9 +18,40 @@ with 'App::Netdisco::Daemon::Worker::Poller::Common'; sub nbtstat_action { \&do_nbtstat } sub nbtstat_filter { \&is_nbtstatable } -sub nbtstat_ip_version { 4 } +sub nbtstat_layer { 2 } -sub nbtwalk { (shift)->_walk_nodes_body('nbtstat', @_) } -sub nbtstat { (shift)->_single_node_body('nbtstat', @_) } +sub nbtwalk { (shift)->_walk_body('nbtstat', @_) } + +sub nbtstat { + my ($self, $job) = @_; + + my $host = NetAddr::IP::Lite->new($job->device); + my $device = get_device($host->addr); + + unless (is_discoverable($device->ip)) { + return job_defer("nbtstat deferred: $host is not discoverable"); + } + + # get list of nodes on device + my $interval = (setting('nbt_max_age') || 7) . ' day'; + my $rs = schema('netdisco')->resultset('NodeIp')->search({ + -bool => 'me.active', + -bool => 'nodes.active', + 'nodes.switch' => $device->ip, + 'me.time_last' => \[ '>= now() - ?::interval', $interval ], + },{ + join => 'nodes', + columns => 'ip', + distinct => 1, + })->ip_version(4); + + my @nodes = $rs->get_column('ip')->all; + my $now = 'to_timestamp('. (join '.', gettimeofday) .')'; + + $self->_single_node_body('nbtstat', $_, $now) + for @nodes; + + return job_done("Ended nbtstat for ". $host->addr); +} 1; diff --git a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm index cd1216b8..f5234b15 100644 --- a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm +++ b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm @@ -43,6 +43,21 @@ sub add_job { }; } +# we have a separate list for jobs needing a device to avoid queueing +# such a job when there's no device param (it could still be duff, tho). +my %jobs = map { $_ => 1} qw/ + discover + macsuck + arpnip + nbtstat +/; +my %jobs_all = map {$_ => 1} qw/ + discoverall + macwalk + arpwalk + nbtwalk +/; + foreach my $jobtype (keys %jobs_all, keys %jobs) { ajax "/ajax/control/admin/$jobtype" => require_role admin => sub { send_error('Missing device', 400) diff --git a/Netdisco/share/views/ajax/device/details.tt b/Netdisco/share/views/ajax/device/details.tt index 1990aab1..1f7d48d6 100644 --- a/Netdisco/share/views/ajax/device/details.tt +++ b/Netdisco/share/views/ajax/device/details.tt @@ -145,6 +145,8 @@ + + diff --git a/Netdisco/share/views/layouts/main.tt b/Netdisco/share/views/layouts/main.tt index f081ee87..87f5ee02 100644 --- a/Netdisco/share/views/layouts/main.tt +++ b/Netdisco/share/views/layouts/main.tt @@ -108,7 +108,7 @@