diff --git a/Netdisco/Changes b/Netdisco/Changes index f4c86751..f993df0f 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,13 +1,35 @@ -2.017001 - +2.018001 - + + [BUG FIXES] + + * Update Print media CSS to handle new UI components + +2.018000 - 2013-10-08 + + [NEW FEATURES] + + * Add VLAN Inventory Report + * Add Wireless SSID Inventory Report + * Add Device Inventory by Location Report + * Node DNS names resolved in their own job - see nodenames_no and nodenames_only [ENHANCEMENTS] * Respect ignore_interfaces and i_ignore when detecting wrapped device uptime * Try NodeIp OUI company name search if no node results found + * Format About page numbers [BUG FIXES] * Update NodeWireless entries which match both MAC and SSID found, only + * Fix SSL-proxy behaviour by using only path+query in links (W. Gould) + * Avoid macsuck generated SQL bug when cleaning NULL VLAN (W. Gould) + * During macsuck get VLAN from Q-BRIDGE if available (jeneric) + * OK to include device ports when doing arpnip (jeneric) + * Correct bulkwalk_off logic + * Silence warnings when ports don't support i_lastchange + * Correct *_only and *_no setting logic + * Correct the instructions for runing dev instance of web and daemon 2.017000 - 2013-09-23 diff --git a/Netdisco/MANIFEST b/Netdisco/MANIFEST index 89ab3308..0a407855 100644 --- a/Netdisco/MANIFEST +++ b/Netdisco/MANIFEST @@ -146,9 +146,12 @@ lib/App/Netdisco/Web/Plugin/Device/Ports.pm lib/App/Netdisco/Web/Plugin/Inventory.pm lib/App/Netdisco/Web/Plugin/Report/ApChannelDist.pm lib/App/Netdisco/Web/Plugin/Report/ApRadioChannelPower.pm +lib/App/Netdisco/Web/Plugin/Report/DeviceByLocation.pm lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm lib/App/Netdisco/Web/Plugin/Report/HalfDuplex.pm lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm +lib/App/Netdisco/Web/Plugin/Report/SsidInventory.pm +lib/App/Netdisco/Web/Plugin/Report/VlanInventory.pm lib/App/Netdisco/Web/Plugin/Search/Device.pm lib/App/Netdisco/Web/Plugin/Search/Node.pm lib/App/Netdisco/Web/Plugin/Search/Port.pm @@ -235,12 +238,18 @@ share/views/ajax/report/apchanneldist.tt share/views/ajax/report/apchanneldist_csv.tt share/views/ajax/report/apradiochannelpower.tt share/views/ajax/report/apradiochannelpower_csv.tt +share/views/ajax/report/devicebylocation.tt +share/views/ajax/report/devicebylocation_csv.tt share/views/ajax/report/duplexmismatch.tt share/views/ajax/report/duplexmismatch_csv.tt share/views/ajax/report/halfduplex.tt share/views/ajax/report/halfduplex_csv.tt share/views/ajax/report/portutilization.tt share/views/ajax/report/portutilization_csv.tt +share/views/ajax/report/ssidinventory.tt +share/views/ajax/report/ssidinventory_csv.tt +share/views/ajax/report/vlaninventory.tt +share/views/ajax/report/vlaninventory_csv.tt share/views/ajax/search/device.tt share/views/ajax/search/device_csv.tt share/views/ajax/search/node_by_ip.tt diff --git a/Netdisco/META.yml b/Netdisco/META.yml index 60d8aaa8..c3017ff8 100644 --- a/Netdisco/META.yml +++ b/Netdisco/META.yml @@ -25,11 +25,11 @@ requires: DBD::Pg: 0 DBD::SQLite: 1.37 DBIx::Class: 0.0821 - DBIx::Class::Helpers: 2.016006 + DBIx::Class::Helpers: 2.018004 Daemon::Control: 0.001 Dancer: 1.3112 Dancer::Plugin::Auth::Extensible: 0.2 - Dancer::Plugin::DBIC: 0.1802 + Dancer::Plugin::DBIC: 0.1803 File::ShareDir: 1.03 HTML::Parser: 3.7 HTTP::Tiny: 0.029 @@ -52,6 +52,7 @@ requires: Starman: 0.4008 Template: 2.24 Template::Plugin::CSV: 0.04 + Template::Plugin::Number::Format: 1.02 URL::Encode: 0.01 YAML: 0.84 YAML::XS: 0.41 @@ -64,4 +65,4 @@ resources: homepage: http://netdisco.org/ license: http://opensource.org/licenses/bsd-license.php repository: git://git.code.sf.net/p/netdisco/netdisco-ng -version: 2.017000 +version: 2.018000 diff --git a/Netdisco/Makefile.PL b/Netdisco/Makefile.PL index e9c5e82f..be3ec6a0 100644 --- a/Netdisco/Makefile.PL +++ b/Netdisco/Makefile.PL @@ -10,10 +10,10 @@ requires 'App::local::lib::helper' => 0.07; requires 'DBD::Pg' => 0; requires 'DBD::SQLite' => 1.37; requires 'DBIx::Class' => 0.08210; -requires 'DBIx::Class::Helpers' => 2.016006; +requires 'DBIx::Class::Helpers' => 2.018004; requires 'Daemon::Control' => 0.001000; requires 'Dancer' => 1.3112; -requires 'Dancer::Plugin::DBIC' => 0.1802; +requires 'Dancer::Plugin::DBIC' => 0.1803; requires 'Dancer::Plugin::Auth::Extensible' => 0.20; requires 'File::ShareDir' => 1.03; requires 'HTML::Parser' => 3.70; diff --git a/Netdisco/lib/App/Netdisco.pm b/Netdisco/lib/App/Netdisco.pm index 0671940f..23a7b9fb 100644 --- a/Netdisco/lib/App/Netdisco.pm +++ b/Netdisco/lib/App/Netdisco.pm @@ -7,7 +7,7 @@ use 5.010_000; use File::ShareDir 'dist_dir'; use Path::Class; -our $VERSION = '2.017000'; +our $VERSION = '2.018000'; BEGIN { if (not ($ENV{DANCER_APPDIR} || '') diff --git a/Netdisco/lib/App/Netdisco/Core/Arpnip.pm b/Netdisco/lib/App/Netdisco/Core/Arpnip.pm index f441f05e..549169bc 100644 --- a/Netdisco/lib/App/Netdisco/Core/Arpnip.pm +++ b/Netdisco/lib/App/Netdisco/Core/Arpnip.pm @@ -3,7 +3,6 @@ package App::Netdisco::Core::Arpnip; use Dancer qw/:syntax :script/; use Dancer::Plugin::DBIC 'schema'; -use App::Netdisco::Util::PortMAC 'get_port_macs'; use App::Netdisco::Util::SanityCheck 'check_mac'; use App::Netdisco::Util::DNS ':all'; use NetAddr::IP::Lite ':lower'; @@ -11,7 +10,7 @@ use Time::HiRes 'gettimeofday'; use base 'Exporter'; our @EXPORT = (); -our @EXPORT_OK = qw/ do_arpnip store_arp /; +our @EXPORT_OK = qw/ do_arpnip store_arp resolve_node_names /; our %EXPORT_TAGS = (all => \@EXPORT_OK); =head1 NAME @@ -44,12 +43,10 @@ sub do_arpnip { return; } - my $port_macs = get_port_macs($device); - # get v4 arp table - my @v4 = _get_arps($device, $port_macs, $snmp->at_paddr, $snmp->at_netaddr); + my @v4 = _get_arps($device, $snmp->at_paddr, $snmp->at_netaddr); # get v6 neighbor cache - my @v6 = _get_arps($device, $port_macs, $snmp->ipv6_n2p_mac, $snmp->ipv6_n2p_addr); + my @v6 = _get_arps($device, $snmp->ipv6_n2p_mac, $snmp->ipv6_n2p_addr); # get directly connected networks my @subnets = _gather_subnets($device, $snmp); @@ -78,23 +75,23 @@ sub do_arpnip { # get an arp table (v4 or v6) sub _get_arps { - my ($device, $port_macs, $paddr, $netaddr) = @_; + my ($device, $paddr, $netaddr) = @_; my @arps = (); while (my ($arp, $node) = each %$paddr) { my $ip = $netaddr->{$arp}; next unless defined $ip; - next unless check_mac($device, $node, $port_macs); - push @arps, [$node, $ip, hostname_from_ip($ip)]; + next unless check_mac($device, $node); + push @arps, [$node, $ip]; } return @arps; } -=head2 store_arp( $mac, $ip, $name, $now? ) +=head2 store_arp( $mac, $ip, $now? ) -Stores a new entry to the C table with the given MAC, IP (v4 or v6) -and DNS host name. +Stores a new entry to the C table with the given MAC, and IP (v4 or +v6). Will mark old entries for this IP as no longer C. @@ -104,7 +101,7 @@ C timestamp, otherwise the current timestamp (C) is used. =cut sub store_arp { - my ($mac, $ip, $name, $now) = @_; + my ($mac, $ip, $now) = @_; $now ||= 'now()'; schema('netdisco')->txn_do(sub { @@ -122,7 +119,6 @@ sub store_arp { ->search({'me.mac' => $mac, 'me.ip' => $ip}) ->update_or_create( { - dns => $name, active => \'true', time_last => \$now, }, @@ -175,4 +171,39 @@ sub _store_subnet { }); } +=head2 resolve_node_names( $device ) + +Given a Device database object, resolve Node IP (ARP) entries belonging to +this device into DNS names, and store them in the C database table. + +This action is usually queued following C so that it may run +asynchronously, and/or on another daemon worker node. + +=cut + +sub resolve_node_names { + my ($device) = @_; + + schema('netdisco')->txn_do(sub { + my $nodeips = schema('netdisco') + ->resultset('NodeIp')->search( + { + -and => [ + -bool => 'me.active', + -bool => 'nodes.active', + ], + 'nodes.switch' => $device->ip, + }, + { + join => 'nodes', + for => 'update', + } + ); + + while (my $nodeip = $nodeips->next) { + $nodeip->update({dns => hostname_from_ip($nodeip->ip)}); + } + }); +} + 1; diff --git a/Netdisco/lib/App/Netdisco/Core/Macsuck.pm b/Netdisco/lib/App/Netdisco/Core/Macsuck.pm index a8126804..39dda17a 100644 --- a/Netdisco/lib/App/Netdisco/Core/Macsuck.pm +++ b/Netdisco/lib/App/Netdisco/Core/Macsuck.pm @@ -94,6 +94,10 @@ sub do_macsuck { $device->ip, $port, $vlan, scalar keys %{ $fwtable->{$vlan}->{$port} }; foreach my $mac (keys %{ $fwtable->{$vlan}->{$port} }) { + # get VLAN from Q-BRIDGE if available + $vlan = $fwtable->{$vlan}->{$port}->{$mac} + if $vlan == 0; + # remove vlan 0 entry for this MAC addr delete $fwtable->{0}->{$_}->{$mac} for keys %{ $fwtable->{0} }; @@ -130,53 +134,48 @@ sub store_node { my $nodes = schema('netdisco')->resultset('Node'); # TODO: probably needs changing if we're to support VTP domains - my $old = $nodes->search( - { - mac => $mac, - vlan => $vlan, - -bool => 'active', - -not => { - switch => $ip, - port => $port, - }, - }); + my $old = $nodes->search({ + mac => $mac, + vlan => $vlan, + -bool => 'active', + -not => { + switch => $ip, + port => $port, + }, + }); # lock rows, # and get the count so we know whether to set time_recent my $old_count = scalar $old->search(undef, { columns => [qw/switch vlan port mac/], - order_by => [qw/switch vlan port mac/], for => 'update', })->all; $old->update({ active => \'false' }); - my $new = $nodes->search( - { - 'me.switch' => $ip, - 'me.port' => $port, - 'me.mac' => $mac, - }, - { - order_by => [qw/switch vlan port mac/], - for => 'update', - }); - - # lock rows - $new->search({vlan => [$vlan, 0, undef]})->first; - - # upgrade old schema - $new->search({vlan => [0, undef]})->delete(); + my $new = $nodes->search({ + 'me.switch' => $ip, + 'me.port' => $port, + 'me.mac' => $mac, + }); # new data - $new->update_or_create({ - vlan => $vlan, - active => \'true', - oui => substr($mac,0,8), - time_last => \$now, - ($old_count ? (time_recent => \$now) : ()), - }); + $new->update_or_create( + { + vlan => $vlan, + active => \'true', + oui => substr($mac,0,8), + time_last => \$now, + ($old_count ? (time_recent => \$now) : ()), + }, + { for => 'update' } + ); + + # upgrade old schema + # an entry for this MAC existed in old schema with vlan => null + # which now has either vlan number or 0 + $new->search({vlan => undef})->delete({only_nodes => 1}); }); } @@ -387,7 +386,7 @@ sub _walk_fwtable { next unless setting('macsuck_bleed'); } - ++$cache->{$port}->{$mac}; + $cache->{$port}->{$mac} = ($fw_vlan->{$idx} || '0'); } return $cache; diff --git a/Netdisco/lib/App/Netdisco/DB/ResultSet/Node.pm b/Netdisco/lib/App/Netdisco/DB/ResultSet/Node.pm index 1c77a8fc..acdf8761 100644 --- a/Netdisco/lib/App/Netdisco/DB/ResultSet/Node.pm +++ b/Netdisco/lib/App/Netdisco/DB/ResultSet/Node.pm @@ -100,6 +100,10 @@ sub delete { # avoid letting DBIC delete nodes return 0E0; } + elsif (exists $opts->{only_nodes} and $opts->{only_nodes}) { + # now let DBIC do its thing + return $self->next::method(); + } elsif (exists $opts->{keep_nodes} and $opts->{keep_nodes}) { # avoid letting DBIC delete nodes return 0E0; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Queue.pm b/Netdisco/lib/App/Netdisco/Daemon/Queue.pm index f89e14fb..336f65a4 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Queue.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Queue.pm @@ -22,7 +22,7 @@ sub capacity_for { debug "checking local capacity for action $action"; my $action_map = { - Poller => [qw/discoverall discover arpwalk arpnip macwalk macsuck/], + Poller => [qw/discoverall discover arpwalk arpnip nodenames macwalk macsuck/], Interactive => [qw/location contact portcontrol portname vlan power/], }; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Manager.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Manager.pm index 047196f2..0087d149 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Manager.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Manager.pm @@ -13,7 +13,7 @@ my $fqdn = hostfqdn || 'localhost'; my $role_map = { (map {$_ => 'Poller'} - qw/discoverall discover arpwalk arpnip macwalk macsuck/), + qw/discoverall discover arpwalk arpnip nodenames macwalk macsuck/), (map {$_ => 'Interactive'} qw/location contact portcontrol portname vlan power/) }; diff --git a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Arpnip.pm b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Arpnip.pm index d511c1b4..26e47be2 100644 --- a/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Arpnip.pm +++ b/Netdisco/lib/App/Netdisco/Daemon/Worker/Poller/Arpnip.pm @@ -1,9 +1,17 @@ package App::Netdisco::Daemon::Worker::Poller::Arpnip; +use Dancer::Plugin::DBIC 'schema'; + use App::Netdisco::Core::Arpnip 'do_arpnip'; -use App::Netdisco::Util::Device 'is_arpnipable'; +use App::Netdisco::Util::Device qw/get_device is_arpnipable can_nodenames/; + +use App::Netdisco::Core::Arpnip 'resolve_node_names'; +use App::Netdisco::Daemon::Util ':all'; + +use NetAddr::IP::Lite ':lower'; use Role::Tiny; +use Class::Method::Modifiers; use namespace::clean; with 'App::Netdisco::Daemon::Worker::Poller::Common'; @@ -15,4 +23,43 @@ sub arpnip_layer { 3 } sub arpwalk { (shift)->_walk_body('arpnip', @_) } sub arpnip { (shift)->_single_body('arpnip', @_) } +after 'arpnip' => sub { + my ($self, $job) = @_; + + my $host = NetAddr::IP::Lite->new($job->device); + my $device = get_device($host->addr); + my $jobqueue = schema('netdisco')->resultset('Admin'); + + schema('netdisco')->txn_do(sub { + $jobqueue->create({ + device => $device->ip, + action => 'nodenames', + status => 'queued', + username => $job->username, + userip => $job->userip, + }); + }); +}; + +# run a nodenames job for one device +sub nodenames { + my ($self, $job) = @_; + + my $host = NetAddr::IP::Lite->new($job->device); + my $device = get_device($host->addr); + my $jobqueue = schema('netdisco')->resultset('Admin'); + + if ($device->ip eq '0.0.0.0') { + return job_error("nodenames failed: no device param (need -d ?)"); + } + + unless (can_nodenames($device->ip)) { + return job_defer("nodenames deferred: cannot run for $host"); + } + + resolve_node_names($device); + + return job_done("Ended nodenames for ". $host->addr); +} + 1; diff --git a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod index 591e19bb..906f0aa2 100644 --- a/Netdisco/lib/App/Netdisco/Manual/Configuration.pod +++ b/Netdisco/lib/App/Netdisco/Manual/Configuration.pod @@ -444,6 +444,22 @@ Value: Number. Default: 0. Sets the minimum amount of time in seconds which must elapse between any two arpnip jobs for a device. +=head3 C + +Value: List of Network Identifiers or Device Properties. Default: Empty List. + +Devices in the list will not have their Node IPs resolved to names using the +DNS. You can include hostnames, IP addresses and subnets (IPv4 or IPv6) in the +list. + +=head3 C + +Value: List of Network Identifiers or Device Properties. Default: Empty List. + +Only thos devices in the list will have their Node IPs resolved to names using +the DNS. You can include hostnames, IP addresses and subnets (IPv4 or IPv6) in +the list. + =head3 C Value: Boolean. Default: C. diff --git a/Netdisco/lib/App/Netdisco/Manual/Developing.pod b/Netdisco/lib/App/Netdisco/Manual/Developing.pod index fbe82cff..4e4cefe0 100644 --- a/Netdisco/lib/App/Netdisco/Manual/Developing.pod +++ b/Netdisco/lib/App/Netdisco/Manual/Developing.pod @@ -18,7 +18,7 @@ the L. Then: git clone git://git.code.sf.net/p/netdisco/netdisco-ng netdisco-ng cd netdisco-ng/Netdisco - ~/bin/localenv DBIC_TRACE_PROFILE=console DBIC_TRACE=1 plackup -R share,lib -p 5001 bin/netdisco-web-fg + DBIC_TRACE_PROFILE=console DBIC_TRACE=1 ~/bin/localenv plackup -R share,lib -p 5001 bin/netdisco-web-fg The above creates you a git clone (change the URL if you're a Netdisco Developer) and runs the web server: @@ -47,8 +47,17 @@ Restarts the web server when you save a file in the C or C directory =back -You should be able to work out something similar for -C, too. Happy hacking! +You might also want to set C to C in your config to quieten +some of the web client callbacks. + +For the daemon, it's very similar: + + DBIC_TRACE_PROFILE=console DBIC_TRACE=1 ~/bin/localenv bin/netdisco-daemon-fg + +Don't be alarmed by the high rate of database queries in the daemon - most of +them are communicating only with a local in-memory SQLite database. + +Happy hacking! =head1 Introduction diff --git a/Netdisco/lib/App/Netdisco/Manual/ReleaseNotes.pod b/Netdisco/lib/App/Netdisco/Manual/ReleaseNotes.pod index d9a08d0d..ad0fba31 100644 --- a/Netdisco/lib/App/Netdisco/Manual/ReleaseNotes.pod +++ b/Netdisco/lib/App/Netdisco/Manual/ReleaseNotes.pod @@ -35,6 +35,21 @@ but they are backwards compatible. =back +=head1 2.018000 + +=head2 General Notices + +The previous mentioned bug in Macsuck is now fixed. + +During Arpnip, Node IPs are no longer resolved to DNS hostnames in real-time. +Another job is queued to perform this action for the device. You can therefore +control using the new C and C config parameters +which daemons run this job. + +The idea here is to support sites where the SNMP polling node has no useful +DNS, but another system can update the DNS entries for nodes (yet do no +polling). + =head1 2.017000 =head2 General Notices diff --git a/Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pod b/Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pod index 2d37fad7..2befd384 100644 --- a/Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pod +++ b/Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pod @@ -307,18 +307,18 @@ App::Netdisco: =item C -A base url which links to the Node tab of the Search page, together with the -correct default search options set. +A path and query string which links to the Node tab of the Search page, +together with the correct default search options set. =item C -A base url which links to the Device tab of the Search page, together with the -correct default search options set. +A path and query string which links to the Device tab of the Search page, +together with the correct default search options set. =item C -A base url which links to the Ports tab of the Device page, together with -the correct default column view options set. +A path and query sting which links to the Ports tab of the Device page, +together with the correct default column view options set. =item C diff --git a/Netdisco/lib/App/Netdisco/Util/Device.pm b/Netdisco/lib/App/Netdisco/Util/Device.pm index 720aa783..11863c71 100644 --- a/Netdisco/lib/App/Netdisco/Util/Device.pm +++ b/Netdisco/lib/App/Netdisco/Util/Device.pm @@ -10,8 +10,10 @@ our @EXPORT = (); our @EXPORT_OK = qw/ get_device check_no + check_only is_discoverable is_arpnipable + can_nodenames is_macsuckable /; our %EXPORT_TAGS = (all => \@EXPORT_OK); @@ -57,42 +59,11 @@ sub get_device { ->find_or_new({ip => $ip}); } -=head2 check_no( $ip, $setting_name ) - -Given the IP address of a device, returns true if the configuration setting -C<$setting_name> matches that device, else returns false. - -There are several options for what C<$setting_name> can contain: - -=over 4 - -=item * - -Hostname, IP address, IP prefix - -=item * - -C<"model:regex"> - matched against the device model - -=item * - -C<"vendor:regex"> - matched against the device vendor - -=back - -To simply match all devices, use IP Prefix "C<0.0.0.0/0>". All regular -expressions are anchored (that is, they must match the whole string). - -=cut - -sub check_no { - my ($ip, $setting_name) = @_; +sub _check_acl { + my ($ip, $config) = @_; my $device = get_device($ip) or return 0; my $addr = NetAddr::IP::Lite->new($device->ip); - my $config = setting($setting_name) || []; - return 0 unless scalar @$config; - foreach my $item (@$config) { if ($item =~ m/^(.*)\s*:\s*(.*)$/) { my $prop = $1; @@ -116,6 +87,86 @@ sub check_no { return 0; } +=head2 check_no( $ip, $setting_name ) + +Given the IP address of a device, returns true if the configuration setting +C<$setting_name> matches that device, else returns false. + + print "rejected!" if check_no($ip, 'discover_no'); + +There are several options for what C<$setting_name> can contain: + +=over 4 + +=item * + +Hostname, IP address, IP prefix + +=item * + +C<"model:regex"> - matched against the device model + +=item * + +C<"vendor:regex"> - matched against the device vendor + +=back + +To simply match all devices, use "C" or IP Prefix "C<0.0.0.0/0>". All +regular expressions are anchored (that is, they must match the whole string). +To match no devices we recommend an entry of "C" in the setting. + +=cut + +sub check_no { + my ($ip, $setting_name) = @_; + + my $config = setting($setting_name) || []; + return 0 unless scalar @$config; + + return _check_acl($ip, $config); +} + +=head2 check_only( $ip, $setting_name ) + +Given the IP address of a device, returns false if the configuration setting +C<$setting_name> matches that device, else returns true. + + print "rejected!" unless check_only($ip, 'discover_only'); + +There are several options for what C<$setting_name> can contain: + +=over 4 + +=item * + +Hostname, IP address, IP prefix + +=item * + +C<"model:regex"> - matched against the device model + +=item * + +C<"vendor:regex"> - matched against the device vendor + +=back + +To simply match all devices, use "C" or IP Prefix "C<0.0.0.0/0>". All +regular expressions are anchored (that is, they must match the whole string). +To match no devices we recommend an entry of "C" in the setting. + +=cut + +sub check_only { + my ($ip, $setting_name) = @_; + + my $config = setting($setting_name) || []; + return 1 unless scalar @$config; + + return _check_acl($ip, $config); +} + =head2 is_discoverable( $ip, $device_type? ) Given an IP address, returns C if Netdisco on this host is permitted by @@ -147,7 +198,7 @@ sub is_discoverable { if check_no($device, 'discover_no'); return _bail_msg("is_discoverable: device failed to match discover_only") - if check_no($device, 'discover_only'); + unless check_only($device, 'discover_only'); # cannot check last_discover for as yet undiscovered devices :-) return 1 if not $device->in_storage; @@ -181,7 +232,7 @@ sub is_arpnipable { if check_no($device, 'arpnip_no'); return _bail_msg("is_arpnipable: device failed to match arpnip_only") - if check_no($device, 'arpnip_only'); + unless check_only($device, 'arpnip_only'); return _bail_msg("is_arpnipable: cannot arpnip an undiscovered device") if not $device->in_storage; @@ -195,6 +246,32 @@ sub is_arpnipable { return 1; } +=head2 can_nodenames( $ip ) + +Given an IP address, returns C if Netdisco on this host is permitted by +the local configuration to resolve Node IPs to DNS names for the device. + +The configuration items C and C are checked +against the given IP. + +Returns false if the host is not permitted to do this job for the target +device. + +=cut + +sub can_nodenames { + my $ip = shift; + my $device = get_device($ip) or return 0; + + return _bail_msg("can_nodenames device matched nodenames_no") + if check_no($device, 'nodenames_no'); + + return _bail_msg("can_nodenames: device failed to match nodenames_only") + unless check_only($device, 'nodenames_only'); + + return 1; +} + =head2 is_macsuckable( $ip ) Given an IP address, returns C if Netdisco on this host is permitted by @@ -215,7 +292,7 @@ sub is_macsuckable { if check_no($device, 'macsuck_no'); return _bail_msg("is_macsuckable: device failed to match macsuck_only") - if check_no($device, 'macsuck_only'); + unless check_only($device, 'macsuck_only'); return _bail_msg("is_macsuckable: cannot macsuck an undiscovered device") if not $device->in_storage; diff --git a/Netdisco/lib/App/Netdisco/Util/SanityCheck.pm b/Netdisco/lib/App/Netdisco/Util/SanityCheck.pm index 43a3fe5a..5319d840 100644 --- a/Netdisco/lib/App/Netdisco/Util/SanityCheck.pm +++ b/Netdisco/lib/App/Netdisco/Util/SanityCheck.pm @@ -44,22 +44,25 @@ MAC address is well-formed (according to common formats) MAC address is not all-zero, broadcast, CLIP, VRRP or HSRP +=back + +Optionally pass a cached set of Device port MAC addresses as the third +argument, in which case an additional check is added: + +=over 4 + =item * MAC address does not belong to an interface on any known Device =back -Optionally pass a cached set of Device port MAC addresses as the third -argument, or else C will retrieve this for itself from the -database. - =cut sub check_mac { my ($device, $node, $port_macs) = @_; - $port_macs ||= get_port_macs($device); my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); + $port_macs ||= {}; # incomplete MAC addresses (BayRS frame relay DLCI, etc) if ($mac->get_error) { diff --git a/Netdisco/lib/App/Netdisco/Web.pm b/Netdisco/lib/App/Netdisco/Web.pm index fdf3c4f4..22430c0e 100644 --- a/Netdisco/lib/App/Netdisco/Web.pm +++ b/Netdisco/lib/App/Netdisco/Web.pm @@ -48,6 +48,7 @@ if (setting('extra_web_plugins') and ref [] eq ref setting('extra_web_plugins')) # workaround for https://github.com/PerlDancer/Dancer/issues/935 hook after_error_render => sub { setting('layout' => 'main') }; +# this hook should be loaded _after_ all plugins hook 'before_template' => sub { my $tokens = shift; @@ -61,6 +62,10 @@ hook 'before_template' => sub { # access to logged in user's roles $tokens->{user_has_role} = sub { user_has_role(@_) }; + # fix Plugin Template Variables to be only path+query + $tokens->{$_} = $tokens->{$_}->path_query + for qw/search_node search_device device_ports/; + # allow very long lists of ports $Template::Directive::WHILE_MAX = 10_000; diff --git a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm index c9b7278b..3a600d2a 100644 --- a/Netdisco/lib/App/Netdisco/Web/AdminTask.pm +++ b/Netdisco/lib/App/Netdisco/Web/AdminTask.pm @@ -55,7 +55,7 @@ foreach my $jobtype (keys %jobs_all, keys %jobs) { if exists $jobs{$jobtype} and not param('device'); add_job($jobtype, param('device'), param('extra')); - redirect uri_for('/admin/jobqueue')->as_string; + redirect uri_for('/admin/jobqueue')->path; }; } diff --git a/Netdisco/lib/App/Netdisco/Web/Device.pm b/Netdisco/lib/App/Netdisco/Web/Device.pm index 4e66ac27..f5853f92 100644 --- a/Netdisco/lib/App/Netdisco/Web/Device.pm +++ b/Netdisco/lib/App/Netdisco/Web/Device.pm @@ -159,7 +159,7 @@ get '/device' => require_login sub { }); if (!defined $dev) { - return redirect uri_for('/', {nosuchdevice => 1})->as_string(); + return redirect uri_for('/', {nosuchdevice => 1})->path_query; } params->{'tab'} ||= 'details'; diff --git a/Netdisco/lib/App/Netdisco/Web/Search.pm b/Netdisco/lib/App/Netdisco/Web/Search.pm index 376a5a9c..92f48675 100644 --- a/Netdisco/lib/App/Netdisco/Web/Search.pm +++ b/Netdisco/lib/App/Netdisco/Web/Search.pm @@ -66,7 +66,7 @@ get '/search' => require_login sub { if (not param('tab')) { if (not $q) { - return redirect uri_for('/')->as_string; + return redirect uri_for('/')->path; } # pick most likely tab for initial results @@ -83,7 +83,7 @@ get '/search' => require_login sub { tab => 'details', q => ($nd->first->dns || $nd->first->ip), f => '', - })->as_string; + })->path_query; } # multiple devices diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 3b753507..f8a366e8 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -86,6 +86,8 @@ macsuck_min_age: 0 arpnip_no: [] arpnip_only: [] arpnip_min_age: 0 +nodenames_no: [] +nodenames_only: [] store_wireless_clients: true store_modules: true ignore_interfaces: diff --git a/Netdisco/share/public/css/nd_print.css b/Netdisco/share/public/css/nd_print.css index 0944b8e5..bb1b653b 100644 --- a/Netdisco/share/public/css/nd_print.css +++ b/Netdisco/share/public/css/nd_print.css @@ -14,3 +14,12 @@ body { margin-left: 0px !important; margin-right: 0px !important; } + +.floatThead-table { + position: inherit; + top: 45px; +} + +a:after { + content: "" !important; +} diff --git a/TODO b/TODO index cea74e79..f207fb1a 100644 --- a/TODO +++ b/TODO @@ -1,31 +1 @@ -FRONTEND -======== - -* moar reports - -DAEMON -====== - -BACKEND -======= - -* PING from workers? - -CORE -==== - -* VRF support (multi-tenancy?) -* import legacy config file? - -DOCS -==== - -* Scheduler -* Discover/Refresh jobs -* new X:: plugin namespace -* observiumsparklines plugin -* port column plugins -* add css and js for plugin -* site_plugins -* missing user management ("User Rights") -* Auth::Extensible +See: https://sourceforge.net/p/netdisco/netdisco2/