diff --git a/.github/issue_template.md b/.github/issue_template.md index 869ecf2d..91bedb26 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,12 +1,33 @@ - + - +READ THIS FIRST! +---------------- - - +STOP! If your ticket is about a device not being detected correctly, +see SNMP::Info: https://github.com/netdisco/snmp-info/issues/new + +STOP! If you have new MIBs to submit, +see netdisco-mibs: https://github.com/netdisco/netdisco-mibs/issues/new + +STOP! If you are running a netdisco docker setup, +see netdisco-docker: https://github.com/netdisco/netdisco-docker/issues/new + + + +everything else about Netdisco's behaviour is good, here :-D + +the more info you can provide, the easier it is for us the help you, so please +fill out as many of the items below as possible. + +Provide a general summary of the issue in the Title above + +when including netdisco config snippets, whitespace matters since it's a yaml file +for github issues it really helps if you include the relevant config parts in a codeblock (code fencing) +see the "code" subject on https://guides.github.com/features/mastering-markdown/ for that) +this should preserve spaces in the issue tracker and make troubleshooting quicker + +--> - ## Expected Behavior @@ -37,6 +58,10 @@ * Netdisco version used: * SNMP::Info version used: +## Config info (deployment.yml) + + + ## Device information diff --git a/.gitignore b/.gitignore index ecc05a24..bb308a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ _build blib !.docker/hooks/* .idea +node_modules/* +yarn.lock +package.json +.stylelintrc diff --git a/.travis.yml b/.travis.yml index 854ccb40..dda9e6ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,14 @@ language: perl perl: - "5.30" - "5.10" +os: linux +dist: trusty addons: apt: packages: - libsnmp-dev - phantomjs - - graphviz + - graphviz hosts: - localhost branches: @@ -15,7 +17,7 @@ branches: - /^2\.\d{6}$/ - 'master' install: - - cpanm --quiet --notest PkgConfig Test::CChecker Alien::zlib::Static Alien::OpenSSL::Static Alien::SNMP + - cpanm --quiet --notest https://github.com/egiles/test-compile/archive/v2.1.2.tar.gz PkgConfig Test::CChecker Alien::zlib::Static Alien::OpenSSL::Static Alien::SNMP script: | perl Build.PL && \ ./Build && \ diff --git a/Build.PL b/Build.PL index 3fb6f8c4..5097b335 100644 --- a/Build.PL +++ b/Build.PL @@ -26,6 +26,7 @@ Module::Build->new( 'App::cpanminus' => '1.6108', 'App::local::lib::helper' => '0.07', 'Archive::Extract' => '0', + 'Authen::Radius' => '0', 'CGI::Expand' => '2.05', 'Data::Printer' => '0', 'DBD::Pg' => '0', diff --git a/LICENCE b/LICENCE deleted file mode 100644 index acff9c0b..00000000 --- a/LICENCE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2012, The Netdisco Developer Team -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Netdisco Project nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE NETDISCO DEVELOPER TEAM BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..5c750980 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, The Netdisco Developer Team. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST b/MANIFEST index 566cb575..704eb7a5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -62,6 +62,7 @@ lib/App/Netdisco/DB/Result/Virtual/DevicePoeStatus.pm lib/App/Netdisco/DB/Result/Virtual/DevicePortSpeed.pm lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm lib/App/Netdisco/DB/Result/Virtual/GenericReport.pm +lib/App/Netdisco/DB/Result/Virtual/LastNode.pm lib/App/Netdisco/DB/Result/Virtual/NodeIp4.pm lib/App/Netdisco/DB/Result/Virtual/NodeIp6.pm lib/App/Netdisco/DB/Result/Virtual/NodeMonitor.pm @@ -237,7 +238,7 @@ lib/App/Netdisco/Worker/Plugin/Vlan/Core.pm lib/App/Netdisco/Worker/Runner.pm lib/App/Netdisco/Worker/Status.pm lib/Dancer/Template/NetdiscoTemplateToolkit.pm -LICENCE +LICENSE.md MANIFEST This list of files META.json META.yml @@ -318,6 +319,7 @@ share/public/javascripts/netdisco_portcontrol.js share/public/javascripts/portsort.js share/public/javascripts/toastr.js share/public/javascripts/underscore.min.js +share/radius_dictionaries/TODO share/schema_versions/App-Netdisco-DB-1-2-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-10-11-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-11-12-PostgreSQL.sql @@ -371,6 +373,7 @@ share/schema_versions/App-Netdisco-DB-54-55-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-55-56-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-56-57-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-57-58-PostgreSQL.sql +share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-6-7-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-7-8-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-8-9-PostgreSQL.sql @@ -505,6 +508,7 @@ share/views/sidebar/search/device.tt share/views/sidebar/search/node.tt share/views/sidebar/search/port.tt xt/00-compile.t +xt/01-local-pod.t xt/10-sort_port.t xt/11-portsort.t xt/20-checkacl.t diff --git a/META.json b/META.json index 1687f6cd..e4467c6f 100644 --- a/META.json +++ b/META.json @@ -4,7 +4,7 @@ "Oliver Gorwits " ], "dynamic_config" : 1, - "generated_by" : "Module::Build version 0.4224", + "generated_by" : "Module::Build version 0.4229", "license" : [ "bsd" ], @@ -39,6 +39,7 @@ "App::cpanminus" : "1.6108", "App::local::lib::helper" : "0.07", "Archive::Extract" : "0", + "Authen::Radius" : "0", "CGI::Expand" : "2.05", "DBD::Pg" : "0", "DBIx::Class" : "0.082841", @@ -143,7 +144,7 @@ }, "App::Netdisco::DB" : { "file" : "lib/App/Netdisco/DB.pm", - "version" : "58" + "version" : "59" }, "App::Netdisco::DB::ExplicitLocking" : { "file" : "lib/App/Netdisco/DB/ExplicitLocking.pm" @@ -268,6 +269,9 @@ "App::Netdisco::DB::Result::Virtual::GenericReport" : { "file" : "lib/App/Netdisco/DB/Result/Virtual/GenericReport.pm" }, + "App::Netdisco::DB::Result::Virtual::LastNode" : { + "file" : "lib/App/Netdisco/DB/Result/Virtual/LastNode.pm" + }, "App::Netdisco::DB::Result::Virtual::NodeIp4" : { "file" : "lib/App/Netdisco/DB/Result/Virtual/NodeIp4.pm" }, @@ -804,7 +808,7 @@ }, "homepage" : "http://netdisco.org/", "license" : [ - "http://opensource.org/licenses/BSD-3-Clause" + "http://opensource.org/licenses/bsd-license.php" ], "repository" : { "url" : "https://github.com/netdisco/netdisco" @@ -813,5 +817,5 @@ "x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users" }, "version" : "2.042010", - "x_serialization_backend" : "JSON::PP version 2.97001" + "x_serialization_backend" : "JSON::PP version 4.02" } diff --git a/META.yml b/META.yml index c8b99498..7b79c6da 100644 --- a/META.yml +++ b/META.yml @@ -14,7 +14,7 @@ configure_requires: DBIx::Class: '0.082810' Module::Build: '0.42' dynamic_config: 1 -generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010' +generated_by: 'Module::Build version 0.4229, CPAN::Meta::Converter version 2.150010' license: bsd meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -38,7 +38,7 @@ provides: file: lib/App/Netdisco/Configuration.pm App::Netdisco::DB: file: lib/App/Netdisco/DB.pm - version: '58' + version: '59' App::Netdisco::DB::ExplicitLocking: file: lib/App/Netdisco/DB/ExplicitLocking.pm App::Netdisco::DB::Result::Admin: @@ -121,6 +121,8 @@ provides: file: lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm App::Netdisco::DB::Result::Virtual::GenericReport: file: lib/App/Netdisco/DB/Result/Virtual/GenericReport.pm + App::Netdisco::DB::Result::Virtual::LastNode: + file: lib/App/Netdisco/DB/Result/Virtual/LastNode.pm App::Netdisco::DB::Result::Virtual::NodeIp4: file: lib/App/Netdisco/DB/Result/Virtual/NodeIp4.pm App::Netdisco::DB::Result::Virtual::NodeIp6: @@ -483,6 +485,7 @@ requires: App::cpanminus: '1.6108' App::local::lib::helper: '0.07' Archive::Extract: '0' + Authen::Radius: '0' CGI::Expand: '2.05' DBD::Pg: '0' DBIx::Class: '0.082841' @@ -556,7 +559,7 @@ resources: MailingList: https://lists.sourceforge.net/lists/listinfo/netdisco-users bugtracker: https://github.com/netdisco/netdisco/issues homepage: http://netdisco.org/ - license: http://opensource.org/licenses/BSD-3-Clause + license: http://opensource.org/licenses/bsd-license.php repository: https://github.com/netdisco/netdisco version: '2.042010' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff --git a/bin/netdisco-deploy b/bin/netdisco-deploy index 32cd212e..bd9ff262 100755 --- a/bin/netdisco-deploy +++ b/bin/netdisco-deploy @@ -71,7 +71,7 @@ these is an optional service which the user is asked to confirm. Pre-existing requirements are that there be a database table created and a user with rights to create tables in that database. Both the table and user name must match those configured in your environment YAML file (default -C<~/environments/deployment.yml>). +F<~/environments/deployment.yml>). This script will download the latest MAC address vendor prefix data from the Internet, and update the OUI table in the database. Hence Internet access is @@ -80,10 +80,17 @@ required to run the script. Similarly the latest Netdisco MIB bundle is also downloaded and placed into the user's home directory (or C<$ENV{NETDISCO_HOME}>). +If you upgrade Netdisco make sure you run this script again to make sure +your config remains compatible. + +Before each upgrade also review the +L since +additional steps might be required! + =cut print color 'bold cyan'; -say 'This is the Netdisco II deployment script.'; +say 'This is the Netdisco 2 deployment script.'; say ''; say 'Before we continue, the following prerequisites must be in place:'; say ' * Database added to PostgreSQL for Netdisco'; @@ -92,6 +99,11 @@ say ' * "~/environments/deployment.yml" file configured with Database dsn/user/p say ' * A full backup of any existing Netdisco database data'; say ' * Internet access (for OUIs and MIBs)'; say ''; +say 'If you are upgrading Netdisco 2 read the release notes:'; +say 'https://github.com/netdisco/netdisco/wiki/Release-Notes'; +say 'There you will find required and incompatible changes'; +say 'which are not covered by this script.'; +say ''; say 'You will be asked to confirm all changes to your system.'; say ''; print color 'reset'; @@ -194,13 +206,13 @@ sub deploy_db { } sub get_userpass { - my $term = shift; - my $name = $term->get_reply(prompt => 'Username: '); - my $pass = $term->get_reply(prompt => 'Password: '); + my $upterm = shift; + my $name = $upterm->get_reply(prompt => 'Username: '); + my $pass = $upterm->get_reply(prompt => 'Password: '); unless ($name and $pass) { say 'username and password cannot be empty, please try again.'; - ($name, $pass) = get_userpass($term); + ($name, $pass) = get_userpass($upterm); } return ($name, $pass); @@ -257,7 +269,7 @@ sub deploy_oui { print color 'reset'; } -# This subroutine is from Wireshark's make-manuf +# This subroutine is based on Wireshark's make-manuf # http://anonsvn.wireshark.org/wireshark/trunk/tools/make-manuf sub shorten { my $manuf = shift; @@ -271,22 +283,27 @@ sub shorten { # & isn't needed when Standalone $manuf =~ s/ \& / /g; + # remove junk whitespace + $manuf =~ s/\s+/ /g; + # Remove any "the", "inc", "plc" ... $manuf - =~ s/\s(the|inc|incorporated|plc|systems|corp|corporation|s\/a|a\/s|ab|ag|kg|gmbh|co|company|limited|ltd|holding|spa)(?= )//gi; + =~ s/\s(?:the|inc|incorporated|plc|systems|corp|corporation|s\/a|a\/s|ab|ag|kg|gmbh|co|company|limited|ltd|holding|spa)(?= )//gi; # Convert to consistent case $manuf =~ s/(\w+)/\u\L$1/g; - # Remove all spaces - $manuf =~ s/\s+//g; - # Deviating from make-manuf for HP $manuf =~ s/Hewlett[-]?Packard/Hp/; - # Truncate all names to a reasonable length, say, 8 characters. - # If the string contains UTF-8, this may be substantially more than 8 bytes. - $manuf = substr( $manuf, 0, 8 ); + # Truncate all names to first two words max 20 chars + if (length($manuf) > 21) { + my @twowords = grep {defined} (split ' ', $manuf)[0 .. 1]; + $manuf = join ' ', @twowords; + } + + # Remove all spaces + $manuf =~ s/\s+//g; return encode( "utf8", $manuf ); } diff --git a/lib/App/Netdisco.pm b/lib/App/Netdisco.pm index 088b6ab0..3b139a82 100644 --- a/lib/App/Netdisco.pm +++ b/lib/App/Netdisco.pm @@ -71,7 +71,8 @@ L. Netdisco has several Perl library dependencies which will be automatically installed. However it's required that you first install the following -operating system packages: +operating system packages, if not the installation will most likely fail +further down the road. On Ubuntu/Debian: @@ -100,7 +101,7 @@ will take about 250MB including MIB files. root:~# useradd -m -p x -s /bin/bash netdisco Netdisco uses the PostgreSQL database server. Install PostgreSQL (at least -version 8.4) and then change to the PostgreSQL superuser (usually +version 9.4) and then change to the PostgreSQL superuser (usually C). Create a new database and PostgreSQL user for the Netdisco application: @@ -115,14 +116,25 @@ application: You may wish to L so that local connections are working. The default PostgreSQL configuration -also needs tuning for modern server hardware. We recommend that you use the -C Python program to auto-tune your C file: +can also use tuning for modern server hardware. We recommend that you use one of the following +tools to tune your C file: =over 4 -=item * +=item L -L +Script that will check your operating system resources and settings as well as your +running PostgreSQL database and will make recommendations based on actual load. Works +on new netdisco installs but will make the best suggestions once the database contains +a bigger dataset. + +=item L + +A web based application which will recommend which parameters to change. + +=item L + +Program to auto-tune your C, regretfully not updated in a while. =back @@ -229,7 +241,7 @@ If you're running a version of Netdisco prior to 2.x then you should follow the full installation instructions, above. This process is for upgrading version 2.x only. -Before upgrading please review the latest L. +Before upgrading always review the latest L. Then the process below should be run for each installation: # upgrade Netdisco @@ -251,7 +263,7 @@ Then the process below should be run for each installation: The main black navigation bar has a search box which is smart enough to work out what you're looking for in most cases. For example device names, node IP -or MAC addreses, VLAN numbers, and so on. +or MAC addresses, VLAN numbers, and so on. =head2 Command-Line Device and Port Actions @@ -332,7 +344,7 @@ built upon. =head1 COPYRIGHT AND LICENSE -This software is copyright (c) 2011-2018 by The Netdisco Developer Team. +This software is copyright (c) 2011-2019 by The Netdisco Developer Team. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/lib/App/Netdisco/Backend/Job.pm b/lib/App/Netdisco/Backend/Job.pm index bab5d036..34e97f1b 100644 --- a/lib/App/Netdisco/Backend/Job.pm +++ b/lib/App/Netdisco/Backend/Job.pm @@ -70,7 +70,7 @@ sub display_name { =head2 cancel -Log a status and prevent other stages from runnning. +Log a status and prevent other stages from running. =cut diff --git a/lib/App/Netdisco/DB.pm b/lib/App/Netdisco/DB.pm index baa9bd21..75040b9c 100644 --- a/lib/App/Netdisco/DB.pm +++ b/lib/App/Netdisco/DB.pm @@ -11,7 +11,7 @@ __PACKAGE__->load_namespaces( ); our # try to hide from kwalitee - $VERSION = 58; # schema version used for upgrades, keep as integer + $VERSION = 59; # schema version used for upgrades, keep as integer use Path::Class; use File::ShareDir 'dist_dir'; diff --git a/lib/App/Netdisco/DB/Result/DevicePort.pm b/lib/App/Netdisco/DB/Result/DevicePort.pm index 0b1266e0..aef84ed7 100644 --- a/lib/App/Netdisco/DB/Result/DevicePort.pm +++ b/lib/App/Netdisco/DB/Result/DevicePort.pm @@ -242,6 +242,23 @@ __PACKAGE__->belongs_to( neighbor_alias => 'App::Netdisco::DB::Result::DeviceIp' { join_type => 'LEFT' }, ); +=head2 last_node + +This relationship will return the last node that was seen on the port. + +The JOIN is of type "LEFT" in case there isn't any such node. + +=cut + +__PACKAGE__->belongs_to( + last_node => 'App::Netdisco::DB::Result::Virtual::LastNode', { + 'foreign.switch' => 'self.ip', + 'foreign.port' => 'self.port', + }, { + join_type => 'LEFT', + } +); + =head2 vlans As compared to C, this relationship returns a set of Device VLAN diff --git a/lib/App/Netdisco/DB/Result/User.pm b/lib/App/Netdisco/DB/Result/User.pm index 612019db..6e525547 100644 --- a/lib/App/Netdisco/DB/Result/User.pm +++ b/lib/App/Netdisco/DB/Result/User.pm @@ -29,6 +29,8 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 1 }, "ldap", { data_type => "boolean", default_value => \"false", is_nullable => 1 }, + "radius", + { data_type => "boolean", default_value => \"false", is_nullable => 1 }, "admin", { data_type => "boolean", default_value => \"false", is_nullable => 1 }, "fullname", diff --git a/lib/App/Netdisco/DB/Result/Virtual/LastNode.pm b/lib/App/Netdisco/DB/Result/Virtual/LastNode.pm new file mode 100644 index 00000000..8385a8ab --- /dev/null +++ b/lib/App/Netdisco/DB/Result/Virtual/LastNode.pm @@ -0,0 +1,54 @@ +package App::Netdisco::DB::Result::Virtual::LastNode; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +__PACKAGE__->table_class('DBIx::Class::ResultSource::View'); + +__PACKAGE__->table('last_node'); +__PACKAGE__->result_source_instance->is_virtual(1); +__PACKAGE__->result_source_instance->view_definition(<add_columns( + "mac", + { data_type => "macaddr", is_nullable => 0 }, + "switch", + { data_type => "inet", is_nullable => 0 }, + "port", + { data_type => "text", is_nullable => 0 }, + "active", + { data_type => "boolean", is_nullable => 1 }, + "oui", + { data_type => "varchar", is_nullable => 1, size => 8 }, + "time_first", + { + data_type => "timestamp", + default_value => \"current_timestamp", + is_nullable => 1, + original => { default_value => \"now()" }, + }, + "time_recent", + { + data_type => "timestamp", + default_value => \"current_timestamp", + is_nullable => 1, + original => { default_value => \"now()" }, + }, + "time_last", + { + data_type => "timestamp", + default_value => \"current_timestamp", + is_nullable => 1, + original => { default_value => \"now()" }, + }, + "vlan", + { data_type => "text", is_nullable => 0, default_value => '0' }, +); + +1; diff --git a/lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm b/lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm index f08bac65..a51e6ccc 100644 --- a/lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm +++ b/lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm @@ -7,6 +7,7 @@ use base 'DBIx::Class::Core'; __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); +# NOTE this query is in `git grep 'THREE PLACES'` __PACKAGE__->table('port_utilization'); __PACKAGE__->result_source_instance->is_virtual(1); __PACKAGE__->result_source_instance->view_definition(<result_source_instance->view_definition(< ?::interval )) THEN 1 - ELSE 0 END) as ports_free - FROM device d LEFT JOIN device_port dp + sum(CASE + WHEN ( dp.type != 'propVirtual' AND dp.up_admin = 'up' AND dp.up != 'up' + AND (age(now(), to_timestamp(extract(epoch from d.last_discover) - (d.uptime/100))) < ?::interval) + AND (last_node.time_last IS NULL OR (age(now(), last_node.time_last)) > ?::interval) ) + THEN 1 + WHEN ( dp.type != 'propVirtual' AND dp.up_admin = 'up' AND dp.up != 'up' + AND (age(now(), to_timestamp(extract(epoch from d.last_discover) - (d.uptime - dp.lastchange)/100)) > ?::interval) ) + THEN 1 + ELSE 0 + END) as ports_free + FROM device d + LEFT JOIN device_port dp ON d.ip = dp.ip + LEFT JOIN + ( SELECT DISTINCT ON (switch, port) * FROM node + ORDER BY switch, port, time_last desc ) AS last_node + ON dp.port = last_node.port AND dp.ip = last_node.switch GROUP BY d.dns, d.ip ORDER BY d.dns, d.ip ENDSQL diff --git a/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm b/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm index af960260..7e9f2910 100644 --- a/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm +++ b/lib/App/Netdisco/DB/Result/Virtual/UserRole.pm @@ -20,6 +20,9 @@ __PACKAGE__->result_source_instance->view_definition(<load_components(qw/ =head2 skipped( $backend?, $max_deferrals?, $retry_after? ) -Retuns a correlated subquery for the set of C entries that apply +Returns a correlated subquery for the set of C entries that apply to some jobs. They match the device IP, current backend, and job action. Pass the C FQDN (or the current host will be used as a default), the diff --git a/lib/App/Netdisco/DB/ResultSet/Device.pm b/lib/App/Netdisco/DB/ResultSet/Device.pm index c53043b8..fa5ce923 100644 --- a/lib/App/Netdisco/DB/ResultSet/Device.pm +++ b/lib/App/Netdisco/DB/ResultSet/Device.pm @@ -187,11 +187,11 @@ Will match exactly the C field. =item os -Will match exactly the C field, which is the operating sytem. +Will match exactly the C field, which is the operating system. =item os_ver -Will match exactly the C field, which is the operating sytem software version. +Will match exactly the C field, which is the operating system software version. =item vendor @@ -226,21 +226,20 @@ sub search_by_field { } # For Search on Layers - my @layer_search = ( '_', '_', '_', '_', '_', '_', '_' ); - # @layer_search is computer indexed, left->right my $layers = $p->{layers}; + my @layer_select = (); if ( defined $layers && ref $layers ) { foreach my $layer (@$layers) { next unless defined $layer and length($layer); next if ( $layer < 1 || $layer > 7 ); - $layer_search[ $layer - 1 ] = 1; + push @layer_select, + \[ 'substring(me.layers,9-?, 1)::int = 1', $layer ]; } } elsif ( defined $layers ) { - $layer_search[ $layers - 1 ] = 1; + push @layer_select, + \[ 'substring(me.layers,9-?, 1)::int = 1', $layers ]; } - # the database field is in order 87654321 - my $layer_string = join( '', reverse @layer_search ); return $rs ->search_rs({}, $attrs) @@ -252,8 +251,6 @@ sub search_by_field { { '-ilike' => "\%$p->{location}\%" }) : ()), ($p->{description} ? ('me.description' => { '-ilike' => "\%$p->{description}\%" }) : ()), - ($p->{layers} ? ('me.layers' => - { '-ilike' => "\%$layer_string" }) : ()), ($p->{model} ? ('me.model' => { '-in' => $p->{model} }) : ()), @@ -264,6 +261,8 @@ sub search_by_field { ($p->{vendor} ? ('me.vendor' => { '-in' => $p->{vendor} }) : ()), + ($p->{layers} ? (-or => \@layer_select) : ()), + ($p->{dns} ? ( -or => [ 'me.dns' => { '-ilike' => "\%$p->{dns}\%" }, diff --git a/lib/App/Netdisco/DB/ResultSet/DevicePort.pm b/lib/App/Netdisco/DB/ResultSet/DevicePort.pm index 3ae4f80f..30afb730 100644 --- a/lib/App/Netdisco/DB/ResultSet/DevicePort.pm +++ b/lib/App/Netdisco/DB/ResultSet/DevicePort.pm @@ -40,7 +40,7 @@ sub with_times { }); } -=head2 with_free_ports +=head2 with_is_free This is a modifier for any C (including the helpers below) which will add the following additional synthesized columns to the result set: @@ -67,12 +67,13 @@ sub with_is_free { ->search({}, { '+columns' => { is_free => - \["me.up != 'up' and " - ."age(now(), to_timestamp(extract(epoch from device.last_discover) " - ."- (device.uptime - me.lastchange)/100)) " - ."> ?::interval", - [{} => $interval]] }, - join => 'device', + # NOTE this query is in `git grep 'THREE PLACES'` + \["me.up_admin = 'up' AND me.up != 'up' AND me.type != 'propVirtual' AND " + ."((age(now(), to_timestamp(extract(epoch from device.last_discover) - (device.uptime/100))) < ?::interval " + ."AND (last_node.time_last IS NULL OR age(now(), last_node.time_last) > ?::interval)) " + ."OR age(now(), to_timestamp(extract(epoch from device.last_discover) - (device.uptime - me.lastchange)/100)) > ?::interval)", + [{} => $interval],[ {} => $interval],[ {} => $interval]] }, + join => [qw/device last_node/], }); } @@ -96,14 +97,23 @@ sub only_free_ports { ->search_rs($cond, $attrs) ->search( { - 'me.up' => { '!=' => 'up' }, - },{ - where => - \["age(now(), to_timestamp(extract(epoch from device.last_discover) " - ."- (device.uptime - me.lastchange)/100)) " - ."> ?::interval", + # NOTE this query is in `git grep 'THREE PLACES'` + 'me.up_admin' => 'up', + 'me.up' => { '!=' => 'up' }, + 'me.type' => { '!=' => 'propVirtual' }, + -or => [ + -and => [ + \["age(now(), to_timestamp(extract(epoch from device.last_discover) - (device.uptime/100))) < ?::interval", + [{} => $interval]], + -or => [ + 'last_node.time_last' => undef, + \["age(now(), last_node.time_last) > ?::interval", [{} => $interval]], + ] + ], + \["age(now(), to_timestamp(extract(epoch from device.last_discover) - (device.uptime - me.lastchange)/100)) > ?::interval", [{} => $interval]], - join => 'device' }, + ], + },{ join => [qw/device last_node/] }, ); } diff --git a/lib/App/Netdisco/JobQueue/PostgreSQL.pm b/lib/App/Netdisco/JobQueue/PostgreSQL.pm index e332131a..c8faf5b8 100644 --- a/lib/App/Netdisco/JobQueue/PostgreSQL.pm +++ b/lib/App/Netdisco/JobQueue/PostgreSQL.pm @@ -79,6 +79,19 @@ sub jq_warm_thrusters { actionset => $actionset{$_}, }, { key => 'primary' }) for keys %actionset; }); + + # fix up the pseudo devices which need layer 3 + # TODO remove this after next release + schema('netdisco')->txn_do(sub { + my @hosts = grep { defined } + map { schema('netdisco')->resultset('Device')->search_for_device($_->{only}) } + grep { exists $_->{only} and ref '' eq ref $_->{only} } + grep { exists $_->{driver} and $_->{driver} eq 'cli' } + @{ setting('device_auth') }; + + $_->update({ layers => \[q{overlay(layers placing '1' from 6 for 1)}] }) + for @hosts; + }); } sub jq_getsome { diff --git a/lib/App/Netdisco/SSHCollector/Platform/ACE.pm b/lib/App/Netdisco/SSHCollector/Platform/ACE.pm index e905e479..39ea3b85 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/ACE.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/ACE.pm @@ -1,7 +1,5 @@ package App::Netdisco::SSHCollector::Platform::ACE; -# vim: set expandtab tabstop=8 softtabstop=4 shiftwidth=4: - =head1 NAME App::Netdisco::SSHCollector::Platform::ACE @@ -13,7 +11,7 @@ virtual contexts with individual ARP tables. Contexts are enumerated with C, afterwards the commands C and C must be executed for every context. -The IOS shell does not permit to combine mulitple commands in a single +The IOS shell does not permit to combine multiple commands in a single line, and Net::OpenSSH uses individual connections for individual commands, so we need to use Expect to execute the changeto and show commands in the same context. @@ -36,7 +34,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/ASA.pm b/lib/App/Netdisco/SSHCollector/Platform/ASA.pm index 91bc3348..70952708 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/ASA.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/ASA.pm @@ -16,14 +16,14 @@ C status after login: To use an C password separate from the login password, add an C under C tag in your configuration file: -device_auth: - - tag: sshasa - driver: cli - platform: ASA - only: '192.0.2.1' - username: oliver - password: letmein - enable_password: myenablepass + device_auth: + - tag: sshasa + driver: cli + platform: ASA + only: '192.0.2.1' + username: oliver + password: letmein + enable_password: myenablepass =cut @@ -43,7 +43,7 @@ use Moo; Retrieve ARP and neighbor entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/BigIP.pm b/lib/App/Netdisco/SSHCollector/Platform/BigIP.pm index df10ff6c..17a115d1 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/BigIP.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/BigIP.pm @@ -34,7 +34,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm b/lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm index 74292a95..2307cdb7 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/CPVSX.pm @@ -12,14 +12,14 @@ This collector uses "C" as the command for the arp utility on your system. Clish "C" does not work correctly in versions prior to R77.30. Config example: -device_auth: - - tag: sshcpvsx - driver: cli - platform: CPVSX - only: '192.0.2.1' - username: oliver - password: letmein - expert_password: letmein2 + device_auth: + - tag: sshcpvsx + driver: cli + platform: CPVSX + only: '192.0.2.1' + username: oliver + password: letmein + expert_password: letmein2 =cut diff --git a/lib/App/Netdisco/SSHCollector/Platform/FreeBSD.pm b/lib/App/Netdisco/SSHCollector/Platform/FreeBSD.pm index 061c9225..1e80ed8f 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/FreeBSD.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/FreeBSD.pm @@ -12,14 +12,14 @@ This collector uses "C" as the command for the arp utility on your system. If you wish to specify an absolute path, then add an C item to your configuration: -device_auth: - - tag: sshfreebsd - driver: cli - platform: FreeBSD - only: '192.0.2.1' - username: oliver - password: letmein - arp_command: '/usr/sbin/arp' + device_auth: + - tag: sshfreebsd + driver: cli + platform: FreeBSD + only: '192.0.2.1' + username: oliver + password: letmein + arp_command: '/usr/sbin/arp' =cut @@ -39,7 +39,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/GAIAEmbedded.pm b/lib/App/Netdisco/SSHCollector/Platform/GAIAEmbedded.pm index c4e3d775..d20df0ad 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/GAIAEmbedded.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/GAIAEmbedded.pm @@ -7,21 +7,21 @@ App::Netdisco::SSHCollector::Platform::GAIAEmbedded =head1 DESCRIPTION Collect ARP entries from Checkpoint GAIA embedded Systems -To get this Plugin to work you have to add an User like 'netdisco' with -'Network admin' right in the GAIA embedded OS +To get this Plugin to work you have to add a user like 'netdisco' with +'Network admin' rights in the GAIA embedded OS. This collector uses "C" as the command for the arp utility on your -system. If you wish to specify an absolute path, then add an C +system. If you wish to specify an absolute path, then add an C item to your configuration: -device_auth: - - tag: sshgaia - driver: cli - platform: GAIAEmbedded - only: '192.0.2.1' - username: oliver - password: letmein - arp_command: 'arp' + device_auth: + - tag: sshgaia + driver: cli + platform: GAIAEmbedded + only: '192.0.2.1' + username: oliver + password: letmein + arp_command: 'arp' =cut @@ -41,7 +41,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/IOS.pm b/lib/App/Netdisco/SSHCollector/Platform/IOS.pm index 64c3be76..d68d912a 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/IOS.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/IOS.pm @@ -27,7 +27,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm b/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm index a3b1210c..3bf91470 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/IOSXR.pm @@ -27,7 +27,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/SSHCollector/Platform/Linux.pm b/lib/App/Netdisco/SSHCollector/Platform/Linux.pm index b0449267..46d22e12 100644 --- a/lib/App/Netdisco/SSHCollector/Platform/Linux.pm +++ b/lib/App/Netdisco/SSHCollector/Platform/Linux.pm @@ -12,14 +12,14 @@ This collector uses "C" as the command for the arp utility on your system. If you wish to specify an absolute path, then add an C item to your configuration: -device_auth: - - tag: sshlinux - driver: cli - platform: Linux - only: '192.0.2.1' - username: oliver - password: letmein - arp_command: '/usr/sbin/arp' + device_auth: + - tag: sshlinux + driver: cli + platform: Linux + only: '192.0.2.1' + username: oliver + password: letmein + arp_command: '/usr/sbin/arp' =cut @@ -39,7 +39,7 @@ use Moo; Retrieve ARP entries from device. C<$host> is the hostname or IP address of the device. C<$ssh> is a Net::OpenSSH connection to the device. -Returns a list of hashrefs in the format C<{ mac => MACADDR, ip => IPADDR }>. +Returns a list of hashrefs in the format C<{ mac =E MACADDR, ip =E IPADDR }>. =back diff --git a/lib/App/Netdisco/Transport/SNMP.pm b/lib/App/Netdisco/Transport/SNMP.pm index f342155b..5fbf5fed 100644 --- a/lib/App/Netdisco/Transport/SNMP.pm +++ b/lib/App/Netdisco/Transport/SNMP.pm @@ -58,8 +58,11 @@ Returns C if the connection fails. sub reader_for { my ($class, $ip, $useclass) = @_; my $device = get_device($ip) or return undef; + return undef if $device->in_storage and $device->is_pseudo; + my $readers = $class->instance->readers or return undef; return $readers->{$device->ip} if exists $readers->{$device->ip}; + debug sprintf 'snmp reader cache warm: [%s]', $device->ip; return ($readers->{$device->ip} = _snmp_connect_generic('read', $device, $useclass)); @@ -104,8 +107,11 @@ Returns C if the connection fails. sub writer_for { my ($class, $ip, $useclass) = @_; my $device = get_device($ip) or return undef; + return undef if $device->in_storage and $device->is_pseudo; + my $writers = $class->instance->writers or return undef; return $writers->{$device->ip} if exists $writers->{$device->ip}; + debug sprintf 'snmp writer cache warm: [%s]', $device->ip; return ($writers->{$device->ip} = _snmp_connect_generic('write', $device, $useclass)); diff --git a/lib/App/Netdisco/Util/Device.pm b/lib/App/Netdisco/Util/Device.pm index e0fbd211..6c31583a 100644 --- a/lib/App/Netdisco/Util/Device.pm +++ b/lib/App/Netdisco/Util/Device.pm @@ -158,6 +158,8 @@ If C<$device_type> is also given, then C will be checked. Also respects C and C if either are set to false. +Also checks if the device is a pseudo device (vendor is C). + Returns false if the host is not permitted to discover the target device. =cut @@ -168,6 +170,9 @@ sub is_discoverable { $remote_type ||= ''; $remote_cap ||= []; + return _bail_msg("is_discoverable: $device is pseudo-device") + if $device->is_pseudo; + return _bail_msg("is_discoverable: $device matches wap_platforms but discover_waps is not enabled") if ((not setting('discover_waps')) and (match_to_setting($remote_type, 'wap_platforms') or @@ -192,9 +197,8 @@ sub is_discoverable { =head2 is_discoverable_now( $ip, $device_type? ) -Same as C, but also checks the last_discover field if the -device is in storage, and returns false if that host has been too recently -discovered. +Same as C, but also compares the C field +of the C to the C configuration. Returns false if the host is not permitted to discover the target device. @@ -222,6 +226,8 @@ the local configuration to arpnip the device. The configuration items C and C are checked against the given IP. +Also checks if the device reports layer 3 capability. + Returns false if the host is not permitted to arpnip the target device. =cut @@ -230,6 +236,9 @@ sub is_arpnipable { my $ip = shift; my $device = get_device($ip) or return 0; + return _bail_msg("is_arpnipable: $device has no layer 3 capability") + unless $device->has_layer(3); + return _bail_msg("is_arpnipable: $device matched arpnip_no") if check_acl_no($device, 'arpnip_no'); @@ -241,9 +250,8 @@ sub is_arpnipable { =head2 is_arpnipable_now( $ip ) -Same as C, but also checks the last_arpnip field if the -device is in storage, and returns false if that host has been too recently -arpnipped. +Same as C, but also compares the C field +of the C to the C configuration. Returns false if the host is not permitted to arpnip the target device. @@ -271,6 +279,8 @@ the local configuration to macsuck the device. The configuration items C and C are checked against the given IP. +Also checks if the device reports layer 2 capability. + Returns false if the host is not permitted to macsuck the target device. =cut @@ -279,6 +289,9 @@ sub is_macsuckable { my $ip = shift; my $device = get_device($ip) or return 0; + return _bail_msg("is_macsuckable: $device has no layer 2 capability") + unless $device->has_layer(2); + return _bail_msg("is_macsuckable: $device matched macsuck_no") if check_acl_no($device, 'macsuck_no'); @@ -290,9 +303,8 @@ sub is_macsuckable { =head2 is_macsuckable_now( $ip ) -Same as C, but also checks the last_macsuck field if the -device is in storage, and returns false if that host has been too recently -macsucked. +Same as C, but also compares the C field +of the C to the C configuration. Returns false if the host is not permitted to macsuck the target device. diff --git a/lib/App/Netdisco/Web/AdminTask.pm b/lib/App/Netdisco/Web/AdminTask.pm index 7826a6f4..f5b73bf7 100644 --- a/lib/App/Netdisco/Web/AdminTask.pm +++ b/lib/App/Netdisco/Web/AdminTask.pm @@ -59,13 +59,20 @@ ajax qr{/ajax/control/admin/(?:\w+/)?delete} => require_role setting('defanged_a get '/admin/*' => require_role admin => sub { my ($tag) = splat; - # trick the ajax into working as if this were a tabbed page - params->{tab} = $tag; + if (exists setting('_admin_tasks')->{ $tag }) { + # trick the ajax into working as if this were a tabbed page + params->{tab} = $tag; - var(nav => 'admin'); - template 'admintask', { - task => setting('_admin_tasks')->{ $tag }, - }; + var(nav => 'admin'); + template 'admintask', { + task => setting('_admin_tasks')->{ $tag }, + }; + } + else { + var('notfound' => true); + status 'not_found'; + template 'index'; + } }; true; diff --git a/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm b/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm index a35f8d19..bb6780be 100644 --- a/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm +++ b/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm @@ -13,6 +13,7 @@ use Dancer::Plugin::DBIC; use Dancer::Plugin::Passphrase; use Digest::MD5; use Net::LDAP; +use Authen::Radius; use Try::Tiny; sub authenticate_user { @@ -103,9 +104,20 @@ sub match_password { my $settings = $self->realm_settings; my $username_column = $settings->{users_username_column} || 'username'; - return $user->ldap - ? $self->match_with_ldap($password, $user->$username_column) - : $self->match_with_local_pass($password, $user); + my $pwmatch_result = 0; + my $username = $user->$username_column; + + if ($user->ldap) { + $pwmatch_result = $self->match_with_ldap($password, $username); + } + elsif ($user->radius) { + $pwmatch_result = $self->match_with_radius($password, $username); + } + else { + $pwmatch_result = $self->match_with_local_pass($password, $user); + } + + return $pwmatch_result; } sub match_with_local_pass { @@ -215,4 +227,28 @@ sub _ldap_search { return undef; } +sub match_with_radius { + my($self, $pass, $user) = @_; + return unless setting('radius') and ref {} eq ref setting('radius'); + + my $conf = setting('radius'); + my $radius = Authen::Radius->new(Host => $conf->{server}, Secret => $conf->{secret}); + # my $dict_dir = Path::Class::Dir->new( dist_dir('App-Netdisco') ) + #  ->subdir('radius_dictionaries')->stringify; + Authen::Radius->load_dictionary(); # put $dict_dir in here once it's useful + + $radius->add_attributes( + { Name => 'User-Name', Value => $user }, + { Name => 'User-Password', Value => $pass }, + { Name => 'h323-return-code', Value => '0' }, # Cisco AV pair + { Name => 'Digest-Attributes', Value => { Method => 'REGISTER' } } + ); + $radius->send_packet(ACCESS_REQUEST); + + my $type = $radius->recv_packet(); + my $radius_return = ($type eq ACCESS_ACCEPT) ? 1 : 0; + + return $radius_return; +} + 1; diff --git a/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm b/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm index 15d3f15d..dac18e58 100644 --- a/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm +++ b/lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm @@ -36,7 +36,7 @@ ajax '/ajax/control/admin/pseudodevice/add' => require_role admin => sub { ip => param('ip'), dns => param('dns'), vendor => 'netdisco', - layers => '00000100', + layers => param('layers'), last_discover => \'now()', }); return unless $device; @@ -87,6 +87,9 @@ ajax '/ajax/control/admin/pseudodevice/update' => require_role admin => sub { })->delete; } } + + # also set layers + $device->update({layers => param('layers')}); }); }; diff --git a/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm b/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm index 94893702..712e26ca 100644 --- a/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm +++ b/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm @@ -41,6 +41,7 @@ ajax '/ajax/control/admin/users/add' => require_role setting('defanged_admin') = password => _make_password(param('password')), fullname => param('fullname'), ldap => (param('ldap') ? \'true' : \'false'), + radius => (param('radius') ? \'true' : \'false'), port_control => (param('port_control') ? \'true' : \'false'), admin => (param('admin') ? \'true' : \'false'), note => param('note'), @@ -71,6 +72,7 @@ ajax '/ajax/control/admin/users/update' => require_role setting('defanged_admin' : ()), fullname => param('fullname'), ldap => (param('ldap') ? \'true' : \'false'), + radius => (param('radius') ? \'true' : \'false'), port_control => (param('port_control') ? \'true' : \'false'), admin => (param('admin') ? \'true' : \'false'), note => param('note'), diff --git a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm index be5f98ea..d4850b5b 100644 --- a/lib/App/Netdisco/Web/Plugin/Device/Ports.pm +++ b/lib/App/Netdisco/Web/Plugin/Device/Ports.pm @@ -31,7 +31,7 @@ get '/ajax/content/device/ports' => require_login sub { # change wildcard chars to SQL $f =~ s/\*/%/g; $f =~ s/\?/_/g; - # set wilcards at param boundaries + # set wildcards at param boundaries if ($f !~ m/[%_]/) { $f =~ s/^\%*/%/; $f =~ s/\%*$/%/; @@ -82,6 +82,8 @@ get '/ajax/content/device/ports' => require_login sub { }); } delete $port_state{free}; + # showing free ports requires showing down ports + ++$port_state{down}; } if (scalar keys %port_state < 3) { diff --git a/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm b/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm index b8f61f53..6be6aa35 100644 --- a/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm +++ b/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm @@ -47,7 +47,7 @@ get '/ajax/content/report/nodevendor/data' => require_login sub { my $match = $vendor eq 'blank' ? undef : $vendor; $rs = $rs->search( { 'oui.abbrev' => $match }, - { '+columns' => [qw/ device.dns device.name oui.abbrev /], + { '+columns' => [qw/ device.dns device.name oui.abbrev oui.company /], join => [qw/ oui device /], collapse => 1, }); @@ -86,7 +86,7 @@ get '/ajax/content/report/nodevendor' => require_login sub { my $match = $vendor eq 'blank' ? undef : $vendor; $rs = $rs->search( { 'oui.abbrev' => $match }, - { '+columns' => [qw/ device.dns device.name oui.abbrev /], + { '+columns' => [qw/ device.dns device.name oui.abbrev oui.company /], join => [qw/ oui device /], collapse => 1, }); @@ -102,9 +102,9 @@ get '/ajax/content/report/nodevendor' => require_login sub { $rs = $rs->search( { }, { join => 'oui', - select => [ 'oui.abbrev', { count => {distinct => 'me.mac'}} ], - as => [qw/ vendor count /], - group_by => [qw/ oui.abbrev /] + select => [ 'oui.abbrev', 'oui.company', { count => {distinct => 'me.mac'}} ], + as => [qw/ abbrev vendor count /], + group_by => [qw/ oui.abbrev oui.company /] } )->order_by( { -desc => 'count' } ); diff --git a/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm b/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm index 399ee51e..ad06f3e7 100644 --- a/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm +++ b/lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm @@ -20,7 +20,7 @@ get '/ajax/content/report/portutilization' => require_login sub { my $age_num = param('age_num') || 3; my $age_unit = param('age_unit') || 'months'; my @results = schema('netdisco')->resultset('Virtual::PortUtilization') - ->search(undef, { bind => [ "$age_num $age_unit" ] })->hri->all; + ->search(undef, { bind => [ "$age_num $age_unit", "$age_num $age_unit", "$age_num $age_unit" ] })->hri->all; if (request->is_ajax) { my $json = to_json (\@results); diff --git a/lib/App/Netdisco/Web/Plugin/Search/Node.pm b/lib/App/Netdisco/Web/Plugin/Search/Node.pm index ed95588e..0df12490 100644 --- a/lib/App/Netdisco/Web/Plugin/Search/Node.pm +++ b/lib/App/Netdisco/Web/Plugin/Search/Node.pm @@ -6,6 +6,7 @@ use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; use NetAddr::IP::Lite ':lower'; +use Regexp::Common 'net'; use NetAddr::MAC (); use App::Netdisco::Web::Plugin; @@ -23,10 +24,14 @@ ajax '/ajax/content/search/node' => require_login sub { my ( $start, $end ) = param('daterange') =~ m/(\d+-\d+-\d+)/gmx; my $mac = NetAddr::MAC->new(mac => $node); - undef $mac if ($mac and $mac->as_ieee and ($mac->as_ieee eq '00:00:00:00')); - my @active = (param('archived') ? () : (-bool => 'active')); + undef $mac if + ($mac and $mac->as_ieee + and (($mac->as_ieee eq '00:00:00:00:00:00') + or ($mac->as_ieee !~ m/$RE{net}{MAC}/))); + my @active = (param('archived') ? () : (-bool => 'active')); my (@times, @wifitimes, @porttimes); + if ( $start and $end ) { $start = $start . ' 00:00:00'; $end = $end . ' 23:59:59'; diff --git a/lib/App/Netdisco/Web/Search.pm b/lib/App/Netdisco/Web/Search.pm index 2d2cb01d..b447b031 100644 --- a/lib/App/Netdisco/Web/Search.pm +++ b/lib/App/Netdisco/Web/Search.pm @@ -6,6 +6,7 @@ use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Util::Web 'sql_match'; +use Regexp::Common 'net'; use NetAddr::MAC (); hook 'before_template' => sub { @@ -39,7 +40,11 @@ get '/search' => require_login sub { my $nd = $s->resultset('Device')->search_fuzzy($q); my ($likeval, $likeclause) = sql_match($q); my $mac = NetAddr::MAC->new($q); - undef $mac if ($mac and $mac->as_ieee and ($mac->as_ieee eq '00:00:00:00')); + + undef $mac if + ($mac and $mac->as_ieee + and (($mac->as_ieee eq '00:00:00:00:00:00') + or ($mac->as_ieee !~ m/$RE{net}{MAC}/))); if ($nd and $nd->count) { if ($nd->count == 1) { diff --git a/lib/App/Netdisco/Worker/Plugin/Arpnip.pm b/lib/App/Netdisco/Worker/Plugin/Arpnip.pm index 12e8f1e3..1637b5df 100644 --- a/lib/App/Netdisco/Worker/Plugin/Arpnip.pm +++ b/lib/App/Netdisco/Worker/Plugin/Arpnip.pm @@ -16,9 +16,6 @@ register_worker({ phase => 'check' }, sub { return Status->error("arpnip skipped: $device not yet discovered") unless $device->in_storage; - return Status->info("arpnip skipped: $device has no layer 3 capability") - unless $device->has_layer(3); - return Status->info("arpnip skipped: $device is not arpnipable") unless is_arpnipable_now($device); diff --git a/lib/App/Netdisco/Worker/Plugin/Arpnip/Nodes.pm b/lib/App/Netdisco/Worker/Plugin/Arpnip/Nodes.pm index b6122f35..91ba0303 100644 --- a/lib/App/Netdisco/Worker/Plugin/Arpnip/Nodes.pm +++ b/lib/App/Netdisco/Worker/Plugin/Arpnip/Nodes.pm @@ -55,6 +55,7 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub { push @{ vars->{'v6arps'} }, @{get_arps_snmp($device, $snmp->ipv6_n2p_mac, $snmp->ipv6_n2p_addr) }; + $device->update({layers => \[q{overlay(layers placing '1' from 6 for 1)}]}); return Status->done("Gathered arp caches from $device"); }); @@ -82,24 +83,25 @@ sub get_arps_snmp { } register_worker({ phase => 'main', driver => 'cli' }, sub { - my ($job, $workerconf) = @_; + my ($job, $workerconf) = @_; - my $device = $job->device; - my $cli = App::Netdisco::Transport::SSH->session_for($device) - or return Status->defer("arpnip failed: could not SSH connect to $device"); + my $device = $job->device; + my $cli = App::Netdisco::Transport::SSH->session_for($device) + or return Status->defer("arpnip failed: could not SSH connect to $device"); - # should be both v4 and v6 - my @arps = @{ get_arps_cli($device, [$cli->arpnip]) }; + # should be both v4 and v6 + my @arps = @{ get_arps_cli($device, [$cli->arpnip]) }; - # cache v4 arp table - push @{ vars->{'v4arps'} }, - grep { NetAddr::IP::Lite->new($_->{ip})->bits == 32 } @arps; + # cache v4 arp table + push @{ vars->{'v4arps'} }, + grep { NetAddr::IP::Lite->new($_->{ip})->bits == 32 } @arps; - # cache v6 neighbor cache - push @{ vars->{'v6arps'} }, - grep { NetAddr::IP::Lite->new($_->{ip})->bits == 128 } @arps; + # cache v6 neighbor cache + push @{ vars->{'v6arps'} }, + grep { NetAddr::IP::Lite->new($_->{ip})->bits == 128 } @arps; - return Status->done("Gathered arp caches from $device"); + $device->update({layers => \[q{overlay(layers placing '1' from 6 for 1)}]}); + return Status->done("Gathered arp caches from $device"); }); sub get_arps_cli { diff --git a/lib/App/Netdisco/Worker/Plugin/Discover.pm b/lib/App/Netdisco/Worker/Plugin/Discover.pm index d65d274e..5934b8b3 100644 --- a/lib/App/Netdisco/Worker/Plugin/Discover.pm +++ b/lib/App/Netdisco/Worker/Plugin/Discover.pm @@ -16,9 +16,6 @@ register_worker({ phase => 'check' }, sub { return Status->error("discover failed: no device param (need -d ?)") if $device->ip eq '0.0.0.0'; - return Status->info("discover skipped: $device is pseudo-device") - if $device->is_pseudo; - # runner has already called get_device to promote $job->device return $job->cancel("fresh discover cancelled: $device already known") if $device->in_storage diff --git a/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm b/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm index b8f0dee7..34d75c9d 100644 --- a/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm +++ b/lib/App/Netdisco/Worker/Plugin/Discover/Neighbors.pm @@ -95,7 +95,7 @@ port relationships. The Device database object can be a fresh L object which is not yet stored to the database. -A list of discovererd neighbors will be returned as [C<$ip>, C<$type>] tuples. +A list of discovered neighbors will be returned as [C<$ip>, C<$type>] tuples. =cut diff --git a/lib/App/Netdisco/Worker/Plugin/Macsuck.pm b/lib/App/Netdisco/Worker/Plugin/Macsuck.pm index 8f90fdf0..13b61065 100644 --- a/lib/App/Netdisco/Worker/Plugin/Macsuck.pm +++ b/lib/App/Netdisco/Worker/Plugin/Macsuck.pm @@ -16,12 +16,6 @@ register_worker({ phase => 'check' }, sub { return Status->error("macsuck skipped: $device not yet discovered") unless $device->in_storage; - return Status->info("macsuck skipped: $device is pseudo-device") - if $device->is_pseudo; - - return Status->info("macsuck skipped: $device has no layer 2 capability") - unless $device->has_layer(2); - return Status->info("macsuck skipped: $device is not macsuckable") unless is_macsuckable_now($device); diff --git a/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes.pm b/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes.pm index fc96e8f1..029adb2a 100644 --- a/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes.pm +++ b/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes.pm @@ -78,7 +78,7 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub { debug sprintf ' [%s] macsuck - %s updated forwarding table entries', $device->ip, $total_nodes; - # a use for $now ... need to archive dissapeared nodes + # a use for $now ... need to archive disappeared nodes my $archived = 0; if (setting('node_freshness')) { @@ -105,7 +105,7 @@ All four fields in the tuple are required. If you don't know the VLAN ID, Netdisco supports using ID "0". Optionally, a fifth argument can be the literal string passed to the time_last -field of the database record. If not provided, it defauls to C. +field of the database record. If not provided, it defaults to C. =cut diff --git a/lib/App/Netdisco/Worker/Plugin/MakeRancidConf.pm b/lib/App/Netdisco/Worker/Plugin/MakeRancidConf.pm index 5ab13c60..a43ac553 100644 --- a/lib/App/Netdisco/Worker/Plugin/MakeRancidConf.pm +++ b/lib/App/Netdisco/Worker/Plugin/MakeRancidConf.pm @@ -189,7 +189,7 @@ email config and creating the repository with C. =head2 C The location where the rancid configuration (F and -F) is installed. It will be used to check the existance +F) is installed. It will be used to check the existence of device types before exporting the devices to the rancid configuration. If no match is found the device will not be added to rancid. diff --git a/share/environments/deployment.yml b/share/environments/deployment.yml index 7a97d5e2..fcde8244 100644 --- a/share/environments/deployment.yml +++ b/share/environments/deployment.yml @@ -20,8 +20,9 @@ database: # RECOMMENDED SETTINGS # -------------------- -# SNMP community string(s) -# ```````````````````````` +# Device authentication settings +# define snmp communities and ssh credentials here +# ```````````````````````````````````````````````` device_auth: - tag: 'default_v2_readonly' community: 'public' @@ -50,7 +51,7 @@ device_auth: # SNMP, which just clogs up the job queue. # ``````````````````````````````````````````````````````````````` #discover_waps: true -#disover_phones: false +#discover_phones: false # this is the schedule for automatically keeping netdisco up-to-date; # these are good defaults, so only uncomment if needing to change. diff --git a/share/public/css/netdisco.css b/share/public/css/netdisco.css index ebc913b4..5e2fedaf 100644 --- a/share/public/css/netdisco.css +++ b/share/public/css/netdisco.css @@ -245,6 +245,13 @@ td > form.nd_inline-form { text-decoration: none; } +/* badge for pseudo devices layer three toggle */ +.nd_layer-three-link { + text-decoration: none !important; + display: inline-block; + margin-left: -4px; +} + /* for the job control admin page play/pause links */ #nd_countdown-refresh:hover, #nd_countdown-control:hover { text-decoration: none; @@ -359,7 +366,7 @@ td > form.nd_inline-form { .container-fluid > .nd_sidebar { position: absolute; right: 20px; - width: 200px; + width: 205px; left: auto; } diff --git a/share/radius_dictionaries/TODO b/share/radius_dictionaries/TODO new file mode 100644 index 00000000..e69de29b diff --git a/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql b/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql new file mode 100644 index 00000000..53983c95 --- /dev/null +++ b/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql @@ -0,0 +1,11 @@ +BEGIN; + +ALTER TABLE users ADD COLUMN "radius" boolean DEFAULT false; + +UPDATE device SET layers = NULL WHERE vendor = 'netdisco'; + +UPDATE device SET layers = '00000000' WHERE layers IS NULL; + +ALTER TABLE device ALTER layers SET DEFAULT '00000000'; + +COMMIT; diff --git a/share/views/ajax/admintask/pseudodevice.tt b/share/views/ajax/admintask/pseudodevice.tt index d08fce1b..7416eace 100644 --- a/share/views/ajax/admintask/pseudodevice.tt +++ b/share/views/ajax/admintask/pseudodevice.tt @@ -4,6 +4,7 @@ Device Name Device IP Number of Ports + Services Action @@ -11,7 +12,12 @@ - + + +    + 3     + + @@ -26,6 +32,11 @@ + +    + 3     + + @@ -68,6 +79,17 @@ $(document).ready(function() { } ], [% INCLUDE 'ajax/datatabledefaults.tt' -%] } ); + $('.nd_layer-three-link').click(function() { + var badge = $(this).children('span').first(); + var layers = $(this).parent().children('input').first(); + $(badge).toggleClass('badge-success'); + if ($(badge).hasClass('badge-success')) { + $(layers).attr('value', '00000100'); + } + else { + $(layers).attr('value', '00000000'); + } + }); } ); diff --git a/share/views/ajax/admintask/users.tt b/share/views/ajax/admintask/users.tt index ab766b2e..f5fbfa05 100644 --- a/share/views/ajax/admintask/users.tt +++ b/share/views/ajax/admintask/users.tt @@ -5,6 +5,7 @@ Username Password LDAP Auth + RADIUS Auth Port Control Administrator Created @@ -16,15 +17,16 @@ - - + + + - - + + @@ -37,14 +39,17 @@ - + - + + + + @@ -54,10 +59,10 @@ [% row.created | html_entity %] [% row.last_seen | html_entity %] - + - +