diff --git a/Build.PL b/Build.PL index 690bc4bd..bc418d15 100644 --- a/Build.PL +++ b/Build.PL @@ -55,6 +55,7 @@ Module::Build->new( 'Plack::Middleware::ReverseProxy' => '0.15', 'Pod::Usage' => 0, 'Role::Tiny' => '1.002005', + 'Scope::Guard' => 0, 'Sereal' => '0', 'Socket6' => '0.23', 'Starman' => '0.4008', diff --git a/lib/App/Netdisco/Core/Plugin.pm b/lib/App/Netdisco/Core/Plugin.pm index 98fae721..bfebb788 100644 --- a/lib/App/Netdisco/Core/Plugin.pm +++ b/lib/App/Netdisco/Core/Plugin.pm @@ -5,6 +5,7 @@ use Dancer::Plugin; use Dancer::Factory::Hook; use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/; +use Scope::Guard; use Try::Tiny; Dancer::Factory::Hook->instance->install_hooks( @@ -19,32 +20,38 @@ register 'register_core_driver' => sub { and exists $driverconf->{phase} and exists $driverconf->{driver} and Dancer::Factory::Hook->instance->hook_is_registered($driverconf->{phase})); - my $no = (exists $driverconf->{no} ? $driverconf->{no} : undef); - my $only = (exists $driverconf->{only} ? $driverconf->{only} : undef); + # needs to be here for caller() context $driverconf->{plugin} = (caller)[0]; my $hook = sub { - my ($device, $userconf) = @_; - return false unless (ref $device and (ref {} eq ref $userconf)); + my $device = shift or return false; - # first check internal (driverconf) exclusion/inclusion criteria - return false if ($only and not check_acl_only($device, $only)); - return false if ($no and (not exists $userconf->{driver}) - and check_acl_no($device, $only)); + my $no = (exists $driverconf->{no} ? $driverconf->{no} : undef); + my $only = (exists $driverconf->{only} ? $driverconf->{only} : undef); - # then check external (userconf) exclusion/inclusion criteria - return false if exists $userconf->{phase} - and (($userconf->{phase} || '') ne $driverconf->{phase}); + my @newuserconf = (); + my @userconf = @{ setting('device_auth') || [] }; - return false if exists $userconf->{driver} - and (($userconf->{driver} || '') ne $driverconf->{driver}); + # reduce device_auth by driver, plugin, driver's only/no + foreach my $stanza (@userconf) { + next if $no and check_acl_no($device, $no); + next if $only and not check_acl_only($device, $only); + next if exists $stanza->{driver} + and (($stanza->{driver} || '') ne $driverconf->{driver}); + next if exists $stanza->{plugin} + and (($stanza->{plugin} || '') ne $driverconf->{plugin}); + push @newuserconf, $stanza; + } - return false if exists $userconf->{plugin} - and (($userconf->{plugin} || '') ne $driverconf->{plugin}); + # back up and restore device_auth + return false unless scalar @newuserconf; + my $guard = guard { set(device_auth => \@userconf) }; + set(device_auth => \@newuserconf); + # run driver my $happy = false; try { - $code->($device, $driverconf, $userconf); + $code->($device, $driverconf); $happy = true; } catch { debug $_ }; @@ -109,11 +116,14 @@ Plugin modules can either ship with the App::Netdisco distribution itself, or be installed separately. Perl uses the standard C<@INC> path searching mechanism to load the plugin modules. See the C and C settings in order to modify C<@INC> for loading local -plugins. +plugins. As an example, if your plugin is called +"App::NetdiscoX::Core::Plugin::MyPluginName" then it could live at: -The order of the entries is significant. Drivers are executed in REVERSE -order that they appear in the C and -C settings. + ~netdisco/nd-site-local/lib/App/NetdiscoX/Core/Plugin/MyPluginName.pm + +The order of the entries is significant, drivers being executed in the order +which they appear in C and C +(although see L for caveats). Finally, you can also prepend module names with "C", to support the "Netdisco extension" namespace. For example, diff --git a/lib/App/Netdisco/Manual/WritingBackendDrivers.pod b/lib/App/Netdisco/Manual/WritingBackendDrivers.pod index c613866b..bb5fb5fd 100644 --- a/lib/App/Netdisco/Manual/WritingBackendDrivers.pod +++ b/lib/App/Netdisco/Manual/WritingBackendDrivers.pod @@ -22,12 +22,11 @@ See L for more information about core plugins. =head1 Developing Plugins -A plugin is simply a Perl module which is loaded. Therefore it can do anything -you like, but most usefully for the App::Netdisco application the module -will make a connection to a device, gather some data, and store it in -Netdisco's database. +A plugin is a Perl module which is loaded. Therefore it can do anything you +like, but the module will make a connection to a device, gather some data, and +store it in Netdisco's database. -App::Netdisco plugins should load the L module. +App::Netdisco plugins must load the L module. This exports a set of helper subroutines to register the driver. Here's the boilerplate code for our example plugin module: @@ -55,24 +54,21 @@ For example: phase => 'discover_wireless', }, sub { "driver code here" }); -An explanation of the C<$driverconf> options is below. The C<$coderef> is the +An explanation of the C<%driverconf> options is below. The C<$coderef> is the main body of your driver. Your driver is run in a L statement to catch errors, and passed the following arguments: - $coderef->($device, $driverconf, $userconf); + $coderef->($device, $driverconf); The C<$device> is an instance of L; that is, a representation of a row in the database. Note that for early discover -phases this row may not yet exist in the database. - -The C<$driverconf> hashref is the set of configuration parameters you used to -declare the driver (documented below). The C<$userconf> hashref is the -settings from C that the end-user configured for authentication; -these are typically specific to the driver and transport in use. +phases this row may not yet exist in the database. The C<$driverconf> hashref +is the set of configuration parameters you used to declare the driver +(documented below). =head2 Required Parameters -You must register drivers with a C and C parameter. +You must register drivers with a C and a C parameter. The C is a label associated with a group of drivers and typically refers to the combination of transport and application protocol. Examples @@ -161,36 +157,5 @@ connections to other databases. use Dancer::Plugin::DBIC; schema('netdisco')->resultset('Devices')->search({vendor => 'cisco'}); -=head1 Naming and File Location - -There are several options for how you name, distribute and install your -App::Netdisco plugin. - -=head2 Namespaces - -As mentioned in L, official Netdisco plugins live -in the C namespace. You can use this namespace -and submit the product to the Netdisco developer team for consideration for -inclusion in the official distribution. - -Alternatively you can release the plugin to CPAN under your own account. In -that case we request that you instead use the -C namespace (note the "X"). Users can load -such modules by using the abbreviated form "X::MyPluginName" which is then -expanded to the full package. - -=head2 File Location - -If writing your own plugins that are not for redistribution or packaging on -CPAN, Netdisco can enable a local include path (C<@INC>). Configuring the -C setting to be "true" enables: - - $ENV{NETDISCO_HOME}/nd-site-local/lib - -As an example, if your plugin is called -"App::NetdiscoX::Core::Plugin::MyPluginName" then it could live at: - - ~netdisco/nd-site-local/lib/App/NetdiscoX/Core/Plugin/MyPluginName.pm - =cut