From 10f78d5dbe4702c13fc200cb0146b7d75b4c5c60 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sat, 4 Nov 2017 23:06:20 +0000 Subject: [PATCH] add priority and namespace to support fancy worker overrides --- lib/App/Netdisco/Worker.pm | 31 +++++++++++++++++-- lib/App/Netdisco/Worker/Plugin.pm | 51 ++++++++++++++++++++++++------- lib/App/Netdisco/Worker/Runner.pm | 6 ++++ share/config.yml | 7 +++++ 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/lib/App/Netdisco/Worker.pm b/lib/App/Netdisco/Worker.pm index 5f52b645..949402d7 100644 --- a/lib/App/Netdisco/Worker.pm +++ b/lib/App/Netdisco/Worker.pm @@ -5,9 +5,9 @@ use warnings; use Module::Load (); use Module::Find qw/findsubmod findallmod/; -use Dancer ':syntax'; -# load worker plugins for our action +use Dancer ':syntax'; +use Dancer::Factory::Hook; sub import { my ($class, $action) = @_; @@ -17,6 +17,7 @@ sub import { my @check_plugins = findsubmod 'App::Netdisco::Worker::Plugin'; my @phase_plugins = map { findallmod $_ } @check_plugins; + # load worker plugins for our action foreach my $plugin (@user_plugins, @check_plugins, @phase_plugins) { $plugin =~ s/^X::/App::NetdiscoX::Worker::Plugin::/; next unless $plugin =~ m/::Plugin::${action}(?:::|$)/i; @@ -24,6 +25,32 @@ sub import { debug "loading worker plugin $plugin"; Module::Load::load $plugin; } + + # now vars->{workers} is populated, we set the dispatch order + my $store = Dancer::Factory::Hook->instance(); + # use DDP; p vars->{'workers'}; + + foreach my $phase (qw/check early main user/) { + $store->install_hooks("nd2_core_${phase}"); + + foreach my $namespace (sort keys %{ vars->{'workers'}->{$phase} }) { + hook "nd2_core_${phase}" => sub { + vars->{'last_worker_ok'} = false; + vars->{'last_worker_priority'} = 0; + }; + + foreach my $priority (sort {$b <=> $a} keys %{ vars->{'workers'}->{$phase}->{$namespace} }) { + + # D::Factory::Hook::register_hook() does not work?! + hook "nd2_core_${phase}" => $_ + for @{ vars->{'workers'}->{$phase}->{$namespace}->{$priority} }; + + hook "nd2_core_${phase}" => sub { + vars->{'last_worker_priority'} = $priority; + }; + } + } + } } true; diff --git a/lib/App/Netdisco/Worker/Plugin.pm b/lib/App/Netdisco/Worker/Plugin.pm index 6424f2ed..1cbbba68 100644 --- a/lib/App/Netdisco/Worker/Plugin.pm +++ b/lib/App/Netdisco/Worker/Plugin.pm @@ -2,17 +2,11 @@ package App::Netdisco::Worker::Plugin; use Dancer ':syntax'; use Dancer::Plugin; -use Dancer::Factory::Hook; use Scope::Guard 'guard'; use aliased 'App::Netdisco::Worker::Status'; use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/; -my $store = Dancer::Factory::Hook->instance(); -foreach my $phase (qw/check early main user/) { - $store->install_hooks("nd2_core_${phase}"); -} - register 'register_worker' => sub { my ($self, $first, $second) = plugin_args(@_); @@ -21,10 +15,28 @@ register 'register_worker' => sub { return error "bad param to register_worker" unless ((ref sub {} eq ref $code) and (ref {} eq ref $workerconf)); - $workerconf->{phase} ||= 'user'; + my $package = (caller)[0]; + if ($package =~ m/Plugin::(\w+)(?:::(\w+))?/) { + $workerconf->{action} = lc($1); + $workerconf->{namespace} = lc($2) if $2; + } + return error "failed to parse action in '$package'" + unless $workerconf->{action}; + + $workerconf->{phase} ||= 'user'; + $workerconf->{namespace} ||= '_base_'; + $workerconf->{priority} ||= (exists $workerconf->{driver} + ? setting('driver_priority')->{$workerconf->{driver}} : 0); my $worker = sub { my $job = shift or return Status->error('missing job param'); + # use DDP; p $workerconf; + + # once workers at a given priority level in a namespace are successful, + # we can skip workers at lower priorities (that is, other drivers) + return Status->noop('skipped worker after previous namespace success') + if vars->{'last_worker_ok'} + and $workerconf->{priority} < vars->{'last_worker_priority'}; # worker might be vendor/platform specific if (ref $job->device) { @@ -39,11 +51,14 @@ register 'register_worker' => sub { my @newuserconf = (); my @userconf = @{ setting('device_auth') || [] }; - # reduce device_auth by driver + # reduce device_auth by driver and action filters foreach my $stanza (@userconf) { next if exists $stanza->{driver} and exists $workerconf->{driver} and (($stanza->{driver} || '') ne ($workerconf->{driver} || '')); + next if exists $stanza->{action} + and not _find_matchaction($workerconf, lc($stanza->{action})); + push @newuserconf, $stanza; } @@ -59,11 +74,25 @@ register 'register_worker' => sub { return $code->($job, $workerconf); }; - # D::Factory::Hook::register_hook() does not work?! - my $hook = 'nd2_core_'. $workerconf->{phase}; - hook $hook => $worker; + # store the built worker as Worker.pm will build the dispatch order later on + push @{ vars->{'workers'} + ->{$workerconf->{phase}} + ->{$workerconf->{namespace}} + ->{$workerconf->{priority}} }, $worker; }; +sub _find_matchaction { + my ($conf, $action) = @_; + return true if !defined $action; + $action = [$action] if ref [] ne ref $action; + + foreach my $f (@$action) { + return true if + $f eq $conf->{action} or $f eq "$conf->{action}::$conf->{namespace}"; + } + return false; +} + register_plugin; true; diff --git a/lib/App/Netdisco/Worker/Runner.pm b/lib/App/Netdisco/Worker/Runner.pm index 6ba1bae2..6461a6d3 100644 --- a/lib/App/Netdisco/Worker/Runner.pm +++ b/lib/App/Netdisco/Worker/Runner.pm @@ -100,6 +100,12 @@ sub run_workers { catch { debug "=> $_" if $_; $self->jobstat->error($_) if $phase eq 'check'; + } + # allow workers to know whether previous worker of a different driver + # but the same namespace was successful + finally { + vars->{'last_worker_ok'} = $self->jobstat->is_ok + if not vars->{'last_worker_ok'}; }; } } diff --git a/share/config.yml b/share/config.yml index 89b7a64a..90ddaaab 100644 --- a/share/config.yml +++ b/share/config.yml @@ -256,6 +256,13 @@ job_prio: extra_worker_plugins: [] # - Discover::ConfigBackup::CLI +driver_priority: + restconf: 500 + netconf: 400 + eapi: 300 + cli: 200 + snmp: 100 + # --------------- # GraphViz Export # ---------------