140 lines
3.2 KiB
Perl
Executable File
140 lines
3.2 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/../lib";
|
|
use App::Netdisco;
|
|
|
|
use Dancer qw/:moose :script/;
|
|
use Dancer::Plugin::DBIC 'schema';
|
|
|
|
use Daemon::Generic::While1;
|
|
use Parallel::Prefork;
|
|
use Net::Domain 'hostfqdn';
|
|
use Role::Tiny;
|
|
use Try::Tiny;
|
|
|
|
# Parallel::Prefork instance
|
|
my $pp = undef;
|
|
|
|
# track worker pids and their roles
|
|
my %workers = ();
|
|
my $next_role = undef;
|
|
|
|
# must come after globals initialization
|
|
newdaemon(
|
|
progname => 'netdisco-daemon',
|
|
($> != 0 ? (pidbase => './') : ()),
|
|
logpriority => 'daemon.info',
|
|
);
|
|
|
|
sub gd_preconfig {
|
|
my $gd = shift;
|
|
|
|
# used for locking jobs in central Pg queue
|
|
$gd->{nd_host} = hostfqdn;
|
|
|
|
set(daemon_pollers => 2)
|
|
if !defined setting('daemon_pollers');
|
|
set(daemon_interactives => 2)
|
|
if !defined setting('daemon_interactives');
|
|
|
|
# need to do this after setting defaults
|
|
$pp = Parallel::Prefork->new(
|
|
max_workers => (1 + setting('daemon_pollers')
|
|
+ setting('daemon_interactives')),
|
|
spawn_interval => 2,
|
|
before_fork => \&set_next_worker_role,
|
|
after_fork => \®ister_worker,
|
|
on_child_reap => \&unregister_worker,
|
|
trap_signals => {
|
|
TERM => 'TERM',
|
|
INT => 'TERM',
|
|
},
|
|
);
|
|
|
|
# do not remove this line - required by Daemon::Generic
|
|
return ();
|
|
}
|
|
|
|
# main loop
|
|
sub gd_run_body {
|
|
my $gd = shift;
|
|
|
|
$gd->handle_term
|
|
if $pp->signal_received =~ m/^(?:TERM|INT)$/;
|
|
|
|
$pp->start(sub {
|
|
print STDERR ">>> new $next_role worker starting...\n";
|
|
with "App::Netdisco::Daemon::Worker::$next_role";
|
|
$gd->worker_begin if $gd->can('worker_begin');
|
|
$gd->worker_body;
|
|
$gd->worker_end if $gd->can('worker_end');
|
|
});
|
|
|
|
# I don't think Parallel::Prefork ever returns from start()
|
|
# until a child exits. Not sure this is ever reached.
|
|
$gd->gd_sleep( setting('daemon_sleep_time') || 5 )
|
|
if not $pp->signal_received;
|
|
}
|
|
|
|
sub register_worker {
|
|
my (undef, $pid) = @_;
|
|
$workers{$pid} = $next_role;
|
|
}
|
|
|
|
sub unregister_worker {
|
|
my (undef, $pid, $status) = @_;
|
|
delete $workers{$pid};
|
|
# also check for bad exit status?
|
|
|
|
# revert any running jobs (will be such if child died)
|
|
try {
|
|
schema('daemon')->resultset('Admin')
|
|
->search({status => "running-$pid"})
|
|
->update({status => 'queued', started => undef});
|
|
}
|
|
catch { warn "error reverting jobs for pid $pid: $_\n" };
|
|
}
|
|
|
|
sub set_next_worker_role {
|
|
my $pp = shift;
|
|
$next_role = _find_next_worker_role();
|
|
}
|
|
|
|
sub _find_next_worker_role {
|
|
my @cur = values %workers;
|
|
my $manager = scalar grep {$_ eq 'Manager'} @cur;
|
|
my $poller = scalar grep {$_ eq 'Poller'} @cur;
|
|
my $inter = scalar grep {$_ eq 'Interactive'} @cur;
|
|
|
|
return 'Manager' if $manager < 1;
|
|
|
|
my $need_poller = $poller < setting('daemon_pollers');
|
|
my $need_inter = $inter < setting('daemon_interactives');
|
|
|
|
if ($need_poller and $need_inter) {
|
|
return (int(rand(2)) ? 'Interactive' : 'Poller');
|
|
}
|
|
|
|
return 'Interactive' if $need_inter;
|
|
return 'Poller' if $need_poller;
|
|
}
|
|
|
|
sub handle_term {
|
|
my $gd = shift;
|
|
$pp->wait_all_children;
|
|
$gd->gd_quit_event
|
|
}
|
|
|
|
# in case we screw up and die ourselves
|
|
END {
|
|
if (defined $pp) {
|
|
$pp->signal_all_children('TERM');
|
|
$pp->wait_all_children;
|
|
}
|
|
}
|
|
|
|
# nullify this to permit Parallel::Prefork to register handlers instead
|
|
sub gd_setup_signals {}
|
|
|