Squashed commit of the following:
commit 4081e22202693bd7c4ea00e95daad8e628c6fd5a
Author: Oliver Gorwits <oliver@cpan.org>
Date: Mon May 29 21:02:07 2023 +0100
large rename of check_acl* to acl_matches*
commit 3cfa284ddd24d68765c255578cc5c184afbdcd83
Author: Oliver Gorwits <oliver@cpan.org>
Date: Fri May 19 20:39:03 2023 +0100
update permission doc
commit 8c7bb93cc5e9fafb770f98f446e45cbd94b14894
Author: Oliver Gorwits <oliver@cpan.org>
Date: Wed May 17 21:50:07 2023 +0100
migrate most check_acl_only to acl_matches_only
commit c47f699f2a22f08f2f3e093ed0f24c891e6f9a82
Author: Oliver Gorwits <oliver@cpan.org>
Date: Wed May 17 21:39:19 2023 +0100
rename check_acl* to be acl_matches*
commit a884a22c3ab1f3262118c3a47ed8e25b0b0a7336
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 14 16:50:42 2023 +0100
update macsuck_no_deviceports to use acl_matches
commit 8c256af728721329b64d071fa529dfc844073ac6
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 7 22:54:33 2023 +0100
update hide_deviceports to use acl_matches multi @things
commit cd5d9978aba1da459be4fed4500f395df13f7784
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sun May 7 22:53:38 2023 +0100
check_acl fix to allow all @things to offer a property before fallback to missing as empty string
commit 1a3ab9a7646e9f994f03126d45fc36e9e5a13ed5
Author: Oliver Gorwits <oliver@cpan.org>
Date: Tue May 2 15:31:17 2023 +0100
add ignore_deviceports to portproperties discover; improve comments
commit 51385ce89458dc939587dae902fda431719c22c9
Merge: b97c07d2 3f8ffe78
Author: Oliver Gorwits <oliver@cpan.org>
Date: Tue May 2 15:21:48 2023 +0100
Merge branch 'master' into og-acl_multidict
commit b97c07d237d750c1d9eb3095d8ff3908512eac2a
Author: Oliver Gorwits <oliver@cpan.org>
Date: Sat Mar 25 14:37:53 2023 +0000
add support for arrayref of items, and unblessed hash, to check_acl
152 lines
5.3 KiB
Perl
152 lines
5.3 KiB
Perl
package App::Netdisco::Worker::Plugin;
|
|
|
|
use Dancer ':syntax';
|
|
use Dancer::Plugin;
|
|
|
|
use App::Netdisco::Util::Permission qw/acl_matches acl_matches_only/;
|
|
use aliased 'App::Netdisco::Worker::Status';
|
|
use Scope::Guard 'guard';
|
|
use Storable 'dclone';
|
|
|
|
register 'register_worker' => sub {
|
|
my ($self, $first, $second) = plugin_args(@_);
|
|
|
|
my $workerconf = (ref $first eq 'HASH' ? $first : {});
|
|
my $code = (ref $first eq 'CODE' ? $first : $second);
|
|
return error "bad param to register_worker"
|
|
unless ((ref sub {} eq ref $code) and (ref {} eq ref $workerconf));
|
|
|
|
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->{title} ||= lc($package) ) =~ s/.+plugin:://;
|
|
$workerconf->{phase} ||= 'user';
|
|
$workerconf->{namespace} ||= '_base_';
|
|
$workerconf->{priority} ||= (exists $workerconf->{driver}
|
|
? (setting('driver_priority')->{$workerconf->{driver}} || 0) : 0);
|
|
|
|
my $worker = sub {
|
|
my $job = shift or die 'missing job param';
|
|
# use DDP; p $workerconf;
|
|
|
|
debug sprintf '-> run worker %s/%s "%s"',
|
|
@$workerconf{qw/phase priority title/};
|
|
|
|
if ($job->is_cancelled) {
|
|
return $job->add_status( Status->info('skip: job is cancelled') );
|
|
}
|
|
|
|
if ($job->is_offline
|
|
and $workerconf->{phase} eq 'main'
|
|
and $workerconf->{priority} > 0
|
|
and $workerconf->{priority} < setting('driver_priority')->{'direct'}) {
|
|
|
|
return $job->add_status( Status->info('skip: networked worker but job is running offline') );
|
|
}
|
|
|
|
# check to see if this namespace has already passed at higher priority
|
|
# and also update job's record of namespace and priority
|
|
return $job->add_status( Status->info('skip: namespace passed at higher priority') )
|
|
if $job->namespace_passed($workerconf);
|
|
|
|
# support part-actions via action::namespace
|
|
if ($job->only_namespace and $workerconf->{phase} ne 'check') {
|
|
# skip namespaces not the requested ::namespace
|
|
return unless $workerconf->{namespace} eq lc( $job->only_namespace )
|
|
# apart from discover::properties which needs to run, so that's early
|
|
# phase for unknown devices, but not ::hooks/early (if implemented)
|
|
or (($job->only_namespace ne 'hooks') and ($workerconf->{phase} eq 'early')
|
|
and ($job->device and not $job->device->in_storage));
|
|
}
|
|
|
|
my @newuserconf = ();
|
|
my @userconf = @{ dclone (setting('device_auth') || []) };
|
|
|
|
# worker might be vendor/platform specific
|
|
if (ref $job->device) {
|
|
my $no = (exists $workerconf->{no} ? $workerconf->{no} : undef);
|
|
my $only = (exists $workerconf->{only} ? $workerconf->{only} : undef);
|
|
|
|
return $job->add_status( Status->info('skip: acls restricted') )
|
|
if ($no and acl_matches($job->device, $no))
|
|
or ($only and not acl_matches_only($job->device, $only));
|
|
|
|
# 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} || ''));
|
|
|
|
# filter here rather than in Runner as runner does not know namespace
|
|
next if exists $stanza->{action}
|
|
and not _find_matchaction($workerconf, lc($stanza->{action}));
|
|
|
|
push @newuserconf, dclone $stanza;
|
|
}
|
|
|
|
# per-device action but no device creds available
|
|
return $job->add_status( Status->info('skip: driver or action not applicable') )
|
|
if 0 == scalar @newuserconf
|
|
and $workerconf->{priority} > 0
|
|
and $workerconf->{priority} < setting('driver_priority')->{'direct'};
|
|
}
|
|
|
|
# back up and restore device_auth
|
|
my $guard = guard { set(device_auth => \@userconf) };
|
|
set(device_auth => \@newuserconf);
|
|
# use DDP; p @newuserconf;
|
|
|
|
# run worker
|
|
$code->($job, $workerconf);
|
|
};
|
|
|
|
# store the built worker as Worker.pm will build the dispatch order later on
|
|
push @{ vars->{'workers'}->{$workerconf->{action}}
|
|
->{$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;
|
|
|
|
=head1 NAME
|
|
|
|
App::Netdisco::Worker::Plugin - Netdisco Workers
|
|
|
|
=head1 Introduction
|
|
|
|
L<App::Netdisco>'s plugin system allows users to write I<workers> to gather
|
|
information from network devices using different I<transports> and store
|
|
results in the database.
|
|
|
|
For example, transports might be SNMP, SSH, or HTTPS. Workers might be
|
|
combining those transports with application protocols such as SNMP, NETCONF
|
|
(OpenConfig with XML), RESTCONF (OpenConfig with JSON), eAPI, or even CLI
|
|
scraping. The combination of transport and protocol is known as a I<driver>.
|
|
|
|
Workers can be restricted to certain vendor platforms using familiar ACL
|
|
syntax. They are also attached to specific actions in Netdisco's backend
|
|
operation (discover, macsuck, etc).
|
|
|
|
See L<https://github.com/netdisco/netdisco/wiki/Backend-Plugins> for details.
|
|
|
|
=cut
|
|
|