@@ -3,7 +3,9 @@ package App::Netdisco::Backend::Role::Scheduler;
|
|||||||
use Dancer qw/:moose :syntax :script/;
|
use Dancer qw/:moose :syntax :script/;
|
||||||
|
|
||||||
use NetAddr::IP;
|
use NetAddr::IP;
|
||||||
|
use JSON::PP ();
|
||||||
use Algorithm::Cron;
|
use Algorithm::Cron;
|
||||||
|
|
||||||
use App::Netdisco::Util::MCE;
|
use App::Netdisco::Util::MCE;
|
||||||
use App::Netdisco::JobQueue qw/jq_insert/;
|
use App::Netdisco::JobQueue qw/jq_insert/;
|
||||||
|
|
||||||
@@ -23,6 +25,12 @@ sub worker_begin {
|
|||||||
my $config = setting('schedule')->{$action}
|
my $config = setting('schedule')->{$action}
|
||||||
or next;
|
or next;
|
||||||
|
|
||||||
|
if (not $config->{when}) {
|
||||||
|
error sprintf 'sch (%s): schedule %s is missing time spec',
|
||||||
|
$wid, $action;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
# accept either single crontab format, or individual time fields
|
# accept either single crontab format, or individual time fields
|
||||||
$config->{when} = Algorithm::Cron->new(
|
$config->{when} = Algorithm::Cron->new(
|
||||||
base => 'local',
|
base => 'local',
|
||||||
@@ -44,6 +52,8 @@ sub worker_body {
|
|||||||
return debug "sch ($wid): no need for scheduler... quitting"
|
return debug "sch ($wid): no need for scheduler... quitting"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $coder = JSON::PP->new->utf8(0)->allow_nonref(1)->allow_unknown(1);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
# sleep until some point in the next minute
|
# sleep until some point in the next minute
|
||||||
my $naptime = 60 - (time % 60) + int(rand(45));
|
my $naptime = 60 - (time % 60) + int(rand(45));
|
||||||
@@ -68,21 +78,31 @@ sub worker_body {
|
|||||||
$win_start, $win_end, $sched->{when}->next_time($win_start);
|
$win_start, $win_end, $sched->{when}->next_time($win_start);
|
||||||
next unless $sched->{when}->next_time($win_start) <= $win_end;
|
next unless $sched->{when}->next_time($win_start) <= $win_end;
|
||||||
|
|
||||||
my $net = NetAddr::IP->new($sched->{device});
|
|
||||||
next if ($sched->{device}
|
|
||||||
and (!$net or $net->num == 0 or $net->addr eq '0.0.0.0'));
|
|
||||||
|
|
||||||
my @hostlist = map { (ref $_) ? $_->addr : undef }
|
|
||||||
(defined $sched->{device} ? ($net->hostenum) : (undef));
|
|
||||||
my @job_specs = ();
|
my @job_specs = ();
|
||||||
|
|
||||||
foreach my $host (@hostlist) {
|
if ($sched->{only} or $sched->{no}) {
|
||||||
push @job_specs, {
|
$sched->{label} = $action;
|
||||||
action => $real_action,
|
push @job_specs, {
|
||||||
device => $host,
|
action => 'scheduler',
|
||||||
port => $sched->{port},
|
subaction => $coder->encode($sched),
|
||||||
subaction => $sched->{extra},
|
};
|
||||||
};
|
}
|
||||||
|
else {
|
||||||
|
my $net = NetAddr::IP->new($sched->{device});
|
||||||
|
next if ($sched->{device}
|
||||||
|
and (!$net or $net->num == 0 or $net->addr eq '0.0.0.0'));
|
||||||
|
|
||||||
|
my @hostlist = map { (ref $_) ? $_->addr : undef }
|
||||||
|
(defined $sched->{device} ? ($net->hostenum) : (undef));
|
||||||
|
|
||||||
|
foreach my $host (@hostlist) {
|
||||||
|
push @job_specs, {
|
||||||
|
action => $real_action,
|
||||||
|
device => $host,
|
||||||
|
port => $sched->{port},
|
||||||
|
subaction => $sched->{extra},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info sprintf 'sched (%s): queueing %s %s jobs',
|
info sprintf 'sched (%s): queueing %s %s jobs',
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ use Dancer qw/:syntax :script/;
|
|||||||
use Dancer::Plugin::DBIC 'schema';
|
use Dancer::Plugin::DBIC 'schema';
|
||||||
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
||||||
|
|
||||||
|
use List::MoreUtils ();
|
||||||
use File::Spec::Functions qw(catdir catfile);
|
use File::Spec::Functions qw(catdir catfile);
|
||||||
use File::Path 'make_path';
|
use File::Path 'make_path';
|
||||||
|
use NetAddr::IP;
|
||||||
|
|
||||||
use base 'Exporter';
|
use base 'Exporter';
|
||||||
our @EXPORT = ();
|
our @EXPORT = ();
|
||||||
@@ -362,7 +364,34 @@ sub get_denied_actions {
|
|||||||
push @badactions, 'arpnip'
|
push @badactions, 'arpnip'
|
||||||
if not is_arpnipable($device);
|
if not is_arpnipable($device);
|
||||||
|
|
||||||
return @badactions;
|
# add pseudo-actions for schedule entries with ACLs
|
||||||
|
my $schedule = setting('schedule') || {};
|
||||||
|
foreach my $label (keys %$schedule) {
|
||||||
|
my $sched = $schedule->{$label} || next;
|
||||||
|
next unless $sched->{only} or $sched->{no};
|
||||||
|
|
||||||
|
my $action = $sched->{action} || $label;
|
||||||
|
my $pseudo_action = "scheduled-$label";
|
||||||
|
|
||||||
|
# if this action is denied in global config then schedule should not run
|
||||||
|
if (scalar grep {$_ eq $action} @badactions) {
|
||||||
|
push @badactions, $pseudo_action;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $net = NetAddr::IP->new($sched->{device});
|
||||||
|
next if ($sched->{device}
|
||||||
|
and (!$net or $net->num == 0 or $net->addr eq '0.0.0.0'));
|
||||||
|
|
||||||
|
push @badactions, $pseudo_action
|
||||||
|
if $sched->{device} and not acl_matches_only($device, $net->cidr);
|
||||||
|
push @badactions, $pseudo_action
|
||||||
|
if $sched->{no} and acl_matches($device, $sched->{no});
|
||||||
|
push @badactions, $pseudo_action
|
||||||
|
if $sched->{only} and not acl_matches_only($device, $sched->{only});
|
||||||
|
}
|
||||||
|
|
||||||
|
return List::MoreUtils::uniq @badactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
59
lib/App/Netdisco/Worker/Plugin/Scheduler.pm
Normal file
59
lib/App/Netdisco/Worker/Plugin/Scheduler.pm
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package App::Netdisco::Worker::Plugin::Scheduler;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use App::Netdisco::Worker::Plugin;
|
||||||
|
use aliased 'App::Netdisco::Worker::Status';
|
||||||
|
|
||||||
|
use App::Netdisco::JobQueue 'jq_insert';
|
||||||
|
use Dancer::Plugin::DBIC 'schema';
|
||||||
|
|
||||||
|
use JSON::PP ();
|
||||||
|
|
||||||
|
register_worker({ phase => 'check' }, sub {
|
||||||
|
my ($job, $workerconf) = @_;
|
||||||
|
|
||||||
|
return Status->error("Missing data of Scheduler entry")
|
||||||
|
unless $job->extra;
|
||||||
|
|
||||||
|
return Status->defer("scheduler skipped: have not yet primed skiplist")
|
||||||
|
unless schema(vars->{'tenant'})->resultset('DeviceSkip')
|
||||||
|
->search({
|
||||||
|
backend => setting('workers')->{'BACKEND'},
|
||||||
|
device => '255.255.255.255',
|
||||||
|
})->count();
|
||||||
|
|
||||||
|
return Status->done('Scheduler is able to run');
|
||||||
|
});
|
||||||
|
|
||||||
|
register_worker({ phase => 'main' }, sub {
|
||||||
|
my ($job, $workerconf) = @_;
|
||||||
|
|
||||||
|
my $coder = JSON::PP->new->utf8(0)->allow_nonref(1)->allow_unknown(1);
|
||||||
|
my $sched = $coder->decode( $job->extra || {} );
|
||||||
|
my $action = $sched->{action} || $sched->{label};
|
||||||
|
|
||||||
|
return Status->error("Missing label of Scheduler entry")
|
||||||
|
unless $action;
|
||||||
|
|
||||||
|
my @walk = schema(vars->{'tenant'})->resultset('Virtual::WalkJobs')
|
||||||
|
->search(undef,{ bind => [
|
||||||
|
$action, ('scheduled-'. $sched->{label}),
|
||||||
|
setting('workers')->{'max_deferrals'},
|
||||||
|
setting('workers')->{'retry_after'},
|
||||||
|
]})->get_column('ip')->all;
|
||||||
|
|
||||||
|
jq_insert([
|
||||||
|
map {{
|
||||||
|
device => $_,
|
||||||
|
action => $action,
|
||||||
|
port => $sched->{port},
|
||||||
|
subaction => $sched->{subaction},
|
||||||
|
username => $job->username,
|
||||||
|
userip => $job->userip,
|
||||||
|
}} (@walk)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Status->done(sprintf 'Queued %s job for all devices', $action);
|
||||||
|
});
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -509,6 +509,7 @@ job_prio:
|
|||||||
- 'macwalk'
|
- 'macwalk'
|
||||||
- 'nbtstat'
|
- 'nbtstat'
|
||||||
- 'nbtwalk'
|
- 'nbtwalk'
|
||||||
|
- 'scheduler'
|
||||||
- 'stats'
|
- 'stats'
|
||||||
|
|
||||||
worker_plugins:
|
worker_plugins:
|
||||||
@@ -567,6 +568,7 @@ worker_plugins:
|
|||||||
- 'PrimeSkiplist'
|
- 'PrimeSkiplist'
|
||||||
- 'Psql'
|
- 'Psql'
|
||||||
- 'Renumber'
|
- 'Renumber'
|
||||||
|
- 'Scheduler'
|
||||||
- 'Show'
|
- 'Show'
|
||||||
- 'Snapshot'
|
- 'Snapshot'
|
||||||
- 'Stats'
|
- 'Stats'
|
||||||
@@ -584,11 +586,13 @@ driver_priority:
|
|||||||
snmp: 100
|
snmp: 100
|
||||||
|
|
||||||
deferrable_actions:
|
deferrable_actions:
|
||||||
- 'snapshot'
|
|
||||||
- 'nbtwalk'
|
|
||||||
- 'macwalk'
|
|
||||||
- 'arpwalk'
|
- 'arpwalk'
|
||||||
- 'discoverall'
|
- 'discoverall'
|
||||||
|
- 'macwalk'
|
||||||
|
- 'nbtwalk'
|
||||||
|
- 'primeskiplist'
|
||||||
|
- 'scheduler'
|
||||||
|
- 'snapshot'
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# GraphViz Export
|
# GraphViz Export
|
||||||
|
|||||||
Reference in New Issue
Block a user