diff --git a/lib/App/Netdisco/Core/Plugin.pm b/lib/App/Netdisco/Core/Plugin.pm index bfebb788..9042f19c 100644 --- a/lib/App/Netdisco/Core/Plugin.pm +++ b/lib/App/Netdisco/Core/Plugin.pm @@ -8,38 +8,38 @@ use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/; use Scope::Guard; use Try::Tiny; -Dancer::Factory::Hook->instance->install_hooks( - map {("before_$_", $_, "after_$_")} - @{ setting('core_phases') } -); - -register 'register_core_driver' => sub { - my ($self, $driverconf, $code) = @_; - return error "bad param to register_core_driver" - unless ((ref sub {} eq ref $code) and (ref {} eq ref $driverconf) - and exists $driverconf->{phase} and exists $driverconf->{driver} - and Dancer::Factory::Hook->instance->hook_is_registered($driverconf->{phase})); +register 'register_core_worker' => sub { + my ($self, $workerconf, $code) = @_; + return error "bad param to register_core_worker" + unless ((ref sub {} eq ref $code) and (ref {} eq ref $workerconf) + and exists $workerconf->{driver}); # needs to be here for caller() context - $driverconf->{plugin} = (caller)[0]; + my ($package, $phase) = ((caller)[0], undef); + if ($package =~ m/::(Discover|Arpnip|Macsuck|Expire|Nbtstat)::(\w+)/) { + $phase = lc( $1 .'_'. $2 ); + } + else { return error "worker Package does not match standard naming" } - my $hook = sub { + $workerconf->{hook} ||= 'after'; + return error "bad hook param to register_core_worker" + unless $workerconf->{hook} =~ m/^(?:before|on|after)$/; + + my $worker = sub { my $device = shift or return false; - my $no = (exists $driverconf->{no} ? $driverconf->{no} : undef); - my $only = (exists $driverconf->{only} ? $driverconf->{only} : undef); + my $no = (exists $workerconf->{no} ? $workerconf->{no} : undef); + my $only = (exists $workerconf->{only} ? $workerconf->{only} : undef); my @newuserconf = (); my @userconf = @{ setting('device_auth') || [] }; - # reduce device_auth by driver, plugin, driver's only/no + # reduce device_auth by driver, plugin, worker'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}); + and (($stanza->{driver} || '') ne $workerconf->{driver}); push @newuserconf, $stanza; } @@ -48,17 +48,21 @@ register 'register_core_driver' => sub { my $guard = guard { set(device_auth => \@userconf) }; set(device_auth => \@newuserconf); - # run driver + # run worker my $happy = false; try { - $code->($device, $driverconf); + $code->($device, $workerconf); $happy = true; } catch { debug $_ }; return $happy; }; - Dancer::Factory::Hook->instance->register_hook($driverconf->{phase}, $hook); + my $hook = $workerconf->{hook} .'_'. $phase; + Dancer::Factory::Hook->instance->install_hooks($hook) + unless Dancer::Factory::Hook->instance->hook_is_registered($hook); + + Dancer::Factory::Hook->instance->register_hook($hook, $worker); }; register_plugin; @@ -66,43 +70,38 @@ true; =head1 NAME -App::Netdisco::Core::Plugin - Netdisco Backend Drivers +App::Netdisco::Core::Plugin - Netdisco Core Workers =head1 Introduction -L's plugin system allows users to create backend I -which use different I to gather information from network devices -and store in the database. +L's plugin system allows users to write I to gather +information from network devices using different I and store +results in the database. -For example, transports might be SNMP, SSH, or HTTPS. Drivers might be +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. +scraping. The combination of transport and protocol is known as a I. -Drivers can be restricted to certain vendor platforms using familiar ACL +Workers can be restricted to certain vendor platforms using familiar ACL syntax. They are also attached to specific phases in Netdisco's backend -operation. +operation (discover, macsuck, etc). =head1 Application Configuration -The C and C settings list in YAML -format the set of Perl module names which are the plugins to be loaded. +The C and C settings list in YAML format the +set of Perl module names which are the plugins to be loaded. Any change should go into your local C configuration file. If you want to view the default settings, see the C file in the C distribution. -Driver phases are in the C setting and for a given backend -action, the registered drivers at one or more phases will be executed if they -apply to the target device. Each phase ("X") also gets a C and -C phase added for preparatory or optional work, respectively. - =head1 How to Configure -The C setting is empty, and used only if you want to -add new plugins but not change the set enabled by default. If you do want to -add to or remove from the default set, then create a version of -C instead. +The C setting is empty, and used only if you want to add +new plugins but not change the set enabled by default. If you do want to add +to or remove from the default set, then create a version of C +instead. Netdisco prepends "C" to any entry in the list. For example, "C" will load the @@ -121,9 +120,9 @@ plugins. As an example, if your plugin is called ~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). +The order of the entries is significant, workers 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 deleted file mode 100644 index bb5fb5fd..00000000 --- a/lib/App/Netdisco/Manual/WritingBackendDrivers.pod +++ /dev/null @@ -1,161 +0,0 @@ -=head1 NAME - -App::Netdisco::Manual::WritingCorePlugins - Documentation on Backend Driver -Plugins for Developers - -=head1 Introduction - -L's plugin system allows users to create backend I -which use different I to gather information from network devices -and store in the database. - -For example, transports might be SNMP, SSH, or HTTPS. Drivers might be -combining those transports with application protocols such as SNMP, NETCONF -(OpenConfig with XML), RESTCONF (OpenConfig with JSON), eAPI, or even CLI -scraping. - -Drivers can be restricted to certain vendor platforms using familiar ACL -syntax. They are also attached to specific phases in Netdisco's backend -operation. - -See L for more information about core plugins. - -=head1 Developing Plugins - -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 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: - - package App::Netdisco::Core::Plugin::Discover::Wireless::UniFi; - - use Dancer ':syntax'; - use Dancer::Plugin::DBIC; - use App::Netdisco::Core::Plugin; - - # driver registration code goes here, ** see below ** - - true; - -=head1 Registering a Driver - -Use the C helper from L to -register a driver: - - register_core_driver( \%driverconf, $coderef ); - -For example: - - register_core_driver({ - driver => 'unifiapi', - phase => 'discover_wireless', - }, sub { "driver code here" }); - -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); - -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). - -=head2 Required Parameters - -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 -include C, C, C, C, and C. The convention -is for driver names to be lowercase. Users use the driver name to associate -authentication configuration settings with the correct drivers. - -The C corresponds to the action run by Netdisco's backend, with the -addition of wrapping phases for additional windows of execution. The list of -so-called I
phases is below (and you can see the actions these map to): - - core_phases: - - discover_properties - - discover_interfaces - - discover_vlans - - discover_wireless - - discover_entities - - macsuck_nodes - - arpnip_nodes - - arpnip_subnets - - netbios_stat - -Each main phase also has C and C phases. For example the -C phase will have C and -C phases available when you register a driver. Note the -significance of the Return Code, and execution order, of drivers in these -phases, explained below. - -=head2 Optional Parameters - -Drivers may have C and C parameters configured which use the -standard ACL syntax described in -L. The C -directive is especially useful as it can restrict a driver to a given device -platform or operating system (for example Cisco IOS XR, for RESTCONF). - -=head2 Driver Execution and Return Code - -Drivers are configured as an ordered list (in C or -C). For the C and C phases of any -action, drivers are run in the order loaded. For the main phase of any action -they are run in REVERSE order. This has the effect that driver plugins loaded -through C will be run I core drivers. - -The return code of the driver is ignored for C and C phases, -but is significant for the main phase of the action. During this phase if any -driver returns a true value then the main phase is deemed to have been -satisfied and Netdsico will move on to any C driver plugins. - -Remember that a driver is only run if it matches the hardware platform of the -target device and the user's configuration, and is not also excluded by the -user's configuration. - -=head2 Accessing Transports - -From your driver you will want to connect to a device to gather data. This is -done using a transport protocol session (SNMP, SSH, etc). Transports are -singleton objects instantiated on demand, so they can be shared among a series -of action phases that are accessing the same device. - -See the documentation for each transport to find out how to access it: - -=over 4 - -=item * - -L - -=item * - -L - -=item * - -L - -=back - -=head2 Database Connections - -The Netdisco database is available via the C schema key, as below. -You can also use the C configuration item to set up -connections to other databases. - - # plugin code - use Dancer::Plugin::DBIC; - schema('netdisco')->resultset('Devices')->search({vendor => 'cisco'}); - -=cut - diff --git a/lib/App/Netdisco/Manual/WritingCoreWorkers.pod b/lib/App/Netdisco/Manual/WritingCoreWorkers.pod new file mode 100644 index 00000000..370deffc --- /dev/null +++ b/lib/App/Netdisco/Manual/WritingCoreWorkers.pod @@ -0,0 +1,181 @@ +=head1 NAME + +App::Netdisco::Manual::WritingCoreWorkers - Developer Documentation on Core Plugins + +=head1 Introduction + +L's plugin system allows users to write I to gather +information from network devices using different I 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. + +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 for more information about core plugins. + +=head1 Developing Workers + +A worker is Perl code which is run. Therefore it can do anything you like, but +typically it will make a connection to a device, gather some data, and store +it in Netdisco's database. + +App::Netdisco plugins must load the L module. +This exports a helper subroutine to register the worker. Here's the +boilerplate code for our example plugin module: + + package App::Netdisco::Core::Plugin::Discover::Wireless::UniFi; + + use Dancer ':syntax'; + use App::Netdisco::Core::Plugin; + + # worker registration code goes here, ** see below ** + + true; + +=head1 Registering a Worker + +Use the C helper from L to +register a worker: + + register_core_worker( \%workerconf, $coderef ); + +For example: + + register_core_worker({ + driver => 'unifiapi', + }, sub { "worker code here" }); + +An explanation of the C<%workerconf> options is below. The C<$coderef> is the +main body of your worker. Your worker is run in a L statement to +catch errors, and passed the following arguments: + + $coderef->($device, $workerconf); + +The C<$device> is an instance of L (that +is, an object representation of a row in the database). Note that for early +discover phases this row may not yet exist in the database. The C<$workerconf> +hashref is the set of configuration parameters you used to declare the worker +(documented below). + +=head2 Package Naming Convention + +The package name used where the worker is declared is significant. Let's look +at the boilerplate example again: + + package App::Netdisco::Core::Plugin::Discover::Wireless::UniFi; + +Workers registered in this package will be run during the I backend +action (that is, during a C job). You can replace C with +other actions such as C, C, C, and C. + +The component after the action is known as the I (C in this +example), and is the way to override a Netdisco built-in worker, by using the +same name (plus an entry in C<$workerconf>, see below). Otherwise you can use +any valid Perl bareword for the phase. + +=head2 Required Parameters + +You must register workers with a C parameter. + +The C is a label associated with a group of workers and typically +refers to the combination of transport and application protocol. Examples +include C, C, C, C, and C. The convention +is for driver names to be lowercase. Use the driver name to associate +authentication configuration settings with the correct workers. + +=head2 Optional Parameters + +Workers may have C and C parameters configured which use the +standard ACL syntax described in L. The C directive is +especially useful as it can restrict a worker to a given device platform or +operating system (for example Cisco IOS XR for the C driver). + +The C parameter tells Netdisco the role that your worker plays in the +backend action. It can have one of three values: + +=over 4 + +=item before + +A worker that is essential to the action and run before any other workers +within the same action. For example at the start of C we need to +gather basic parameters and create a C row in the database. The first +C worker which succeeds will short-circuit any others (see Return +Code, below). + +=item on + +This worker is run alongside others with the same phase, and also +short-curcuits any other workers in the same phase (see Return Code, below). + +=item after + +This worker is run alongside others with the same phase. All workers are run, +regardless of their return code. + +=back + +=head2 Worker Execution and Return Code + +Workers are configured as an ordered list. They are grouped by C and +C (as in Package Naming Convention, above). + +Workers defined in C are run before those in +C so you have an opportunity to override core workers by +adding them to C and setting C to C in the +worker configuration. + +The return code of the worker is ignored for those configured with hook +C, but is significant for those configured with C or C. +If any worker returns a true value then the group at that phase is +deemed to have been satisfied and Netdsico will move on to other worker +plugins. + +Remember that a worker is only run if it matches the hardware platform of the +target device and the user's configuration, and is not also excluded by the +user's configuration. + +=head2 Accessing Transports + +From your worker you will want to connect to a device to gather data. This is +done using a transport protocol session (SNMP, SSH, etc). Transports are +singleton objects instantiated on demand, so they can be shared among a set of +workers that are accessing the same device. + +See the documentation for each transport to find out how to access it: + +=over 4 + +=item * + +L + +=item * + +L + +=item * + +L + +=back + +=head2 Database Connections + +The Netdisco database is available via the C schema key, as below. +You can also use the C configuration item to set up +connections to other databases. + + # plugin package + use Dancer::Plugin::DBIC; + schema('netdisco')->resultset('Devices')->search({vendor => 'cisco'}); + +=cut + diff --git a/share/config.yml b/share/config.yml index 4b695c80..4fe6fb86 100644 --- a/share/config.yml +++ b/share/config.yml @@ -253,15 +253,15 @@ job_prio: - nbtstat - expire -extra_collector_plugins: - - Discover::ConfigBackup::CLI - # standards go into ::RFC # such as: SNMP, NETCONF, RESTCONF # others go into new ::DRIVERNAME # such as: ::CLI ::eAPI -collector_plugins: +extra_core_plugins: + - Discover::ConfigBackup::CLI + +core_plugins: - Discover::Properties::RFC - Discover::Interfaces::RFC - Discover::PortPower::RFC @@ -272,24 +272,13 @@ collector_plugins: - Discover::VLANs::RFC - Discover::Wireless::RFC - Discover::Entities::RFC - - Macsuck::Nodes::RFC # {platform: any} + - Macsuck::Nodes::RFC - Macsuck::WirelessNodes::RFC - - Arpnip::Nodes::CLI # {platform: 'os:iosxr', no: any} + - Arpnip::Nodes::CLI - Arpnip::Nodes::RFC - Arpnip::Subnets::RFC - NetBIOS::Nbtstat::RFC -core_phases: - - discover_properties - - discover_interfaces - - discover_vlans - - discover_wireless - - discover_entities - - macsuck_nodes - - arpnip_nodes - - arpnip_subnets - - netbios_stat - # --------------- # GraphViz Export # ---------------