This commit is contained in:
Christian Ramseyer
2019-03-05 10:08:29 +01:00
24 changed files with 228 additions and 142 deletions

View File

@@ -46,7 +46,7 @@ Module::Build->new(
'JSON' => '2.90', 'JSON' => '2.90',
'JSON::XS' => '3.01', 'JSON::XS' => '3.01',
'List::Util' => '1.49', 'List::Util' => '1.49',
'List::MoreUtils' => '0.33', 'List::MoreUtils' => '0.428',
'MIME::Base64' => '3.13', 'MIME::Base64' => '3.13',
'Module::Load' => '0.32', 'Module::Load' => '0.32',
'Moo' => '1.001000', 'Moo' => '1.001000',
@@ -72,7 +72,7 @@ Module::Build->new(
'Starman' => '0.4008', 'Starman' => '0.4008',
'Storable' => '0', 'Storable' => '0',
'Sys::SigAction' => '0', 'Sys::SigAction' => '0',
'SNMP::Info' => '3.64', 'SNMP::Info' => '3.65',
'SQL::Abstract' => '1.85', 'SQL::Abstract' => '1.85',
'SQL::Translator' => '0.11024', 'SQL::Translator' => '0.11024',
'Template' => '2.24', 'Template' => '2.24',

40
Changes
View File

@@ -1,3 +1,43 @@
2.040006 - 2019-03-04
[BUG FIXES]
* #527 update List::MoreUtils version requirement
2.040005 - 2019-03-04
[BUG FIXES]
* #526 fix discover syntax bug
2.040004 - 2019-03-03
[NEW FEATURES]
* #510 store ifindex in Device Port Properties table (rc9000)
* new discover_waps and discover_phones boolean settings (ollyg)
[ENHANCEMENTS]
* #428 Port-Channels now showing in netmap (ollyg)
* #490 use new LLDP capability checks for ports having phones (ollyg)
* #494 update Cisco ASA ssh collector (stromsoe)
[BUG FIXES]
* #492 Port Control incorrectly uses VLAN config check (inphobia)
* #493 HTML tag fix (inphobia)
* #498 Map with VLAN filter omits unconnected devices (ollyg)
* #499 netdisco-do renumber reports wrong ip (inphobia)
* #500 no more duplicate entries in vlan filter (ollyg)
* #505 renumbering device missed a few tables (ollyg)
* #512 fix regression in phone/wap discovery exclusion (ollyg)
* #514 ipinventory report returns consistent data (inphobia)
* #520 make sure aggports have a master<->slave (ollyg)
* #521 Search Node Date Range not working (ollyg)
* #522 TypeAhead.pm can reference empty data (inphobia)
* fix bug showing no nodes when only one matches in netmap (ollyg)
2.040003 - 2019-01-18 2.040003 - 2019-01-18
[NEW FEATURES] [NEW FEATURES]

View File

@@ -364,6 +364,7 @@ share/schema_versions/App-Netdisco-DB-5-6-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-50-51-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-50-51-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-51-52-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-51-52-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-52-53-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-52-53-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-53-54-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-6-7-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-7-8-PostgreSQL.sql
share/schema_versions/App-Netdisco-DB-8-9-PostgreSQL.sql share/schema_versions/App-Netdisco-DB-8-9-PostgreSQL.sql

View File

@@ -60,7 +60,7 @@
"IO::Socket::SSL" : "2.048", "IO::Socket::SSL" : "2.048",
"JSON" : "2.90", "JSON" : "2.90",
"JSON::XS" : "3.01", "JSON::XS" : "3.01",
"List::MoreUtils" : "0.33", "List::MoreUtils" : "0.428",
"List::Util" : "1.49", "List::Util" : "1.49",
"MCE" : "1.703", "MCE" : "1.703",
"MIME::Base64" : "3.13", "MIME::Base64" : "3.13",
@@ -81,7 +81,7 @@
"Plack::Middleware::ReverseProxy" : "0.15", "Plack::Middleware::ReverseProxy" : "0.15",
"Pod::Usage" : "0", "Pod::Usage" : "0",
"Role::Tiny" : "1.002005", "Role::Tiny" : "1.002005",
"SNMP::Info" : "3.64", "SNMP::Info" : "3.65",
"SQL::Abstract" : "1.85", "SQL::Abstract" : "1.85",
"SQL::Translator" : "0.11024", "SQL::Translator" : "0.11024",
"Scope::Guard" : "0", "Scope::Guard" : "0",
@@ -118,7 +118,7 @@
"provides" : { "provides" : {
"App::Netdisco" : { "App::Netdisco" : {
"file" : "lib/App/Netdisco.pm", "file" : "lib/App/Netdisco.pm",
"version" : "2.040003" "version" : "2.040006"
}, },
"App::Netdisco::AnyEvent::Nbtstat" : { "App::Netdisco::AnyEvent::Nbtstat" : {
"file" : "lib/App/Netdisco/AnyEvent/Nbtstat.pm" "file" : "lib/App/Netdisco/AnyEvent/Nbtstat.pm"
@@ -140,7 +140,7 @@
}, },
"App::Netdisco::DB" : { "App::Netdisco::DB" : {
"file" : "lib/App/Netdisco/DB.pm", "file" : "lib/App/Netdisco/DB.pm",
"version" : "53" "version" : "54"
}, },
"App::Netdisco::DB::ExplicitLocking" : { "App::Netdisco::DB::ExplicitLocking" : {
"file" : "lib/App/Netdisco/DB/ExplicitLocking.pm" "file" : "lib/App/Netdisco/DB/ExplicitLocking.pm"
@@ -800,6 +800,6 @@
"x_IRC" : "irc://irc.freenode.org/#netdisco", "x_IRC" : "irc://irc.freenode.org/#netdisco",
"x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users" "x_MailingList" : "https://lists.sourceforge.net/lists/listinfo/netdisco-users"
}, },
"version" : "2.040003", "version" : "2.040006",
"x_serialization_backend" : "JSON::PP version 2.97001" "x_serialization_backend" : "JSON::PP version 2.97001"
} }

View File

@@ -22,7 +22,7 @@ name: App-Netdisco
provides: provides:
App::Netdisco: App::Netdisco:
file: lib/App/Netdisco.pm file: lib/App/Netdisco.pm
version: '2.040003' version: '2.040006'
App::Netdisco::AnyEvent::Nbtstat: App::Netdisco::AnyEvent::Nbtstat:
file: lib/App/Netdisco/AnyEvent/Nbtstat.pm file: lib/App/Netdisco/AnyEvent/Nbtstat.pm
App::Netdisco::Backend::Job: App::Netdisco::Backend::Job:
@@ -37,7 +37,7 @@ provides:
file: lib/App/Netdisco/Configuration.pm file: lib/App/Netdisco/Configuration.pm
App::Netdisco::DB: App::Netdisco::DB:
file: lib/App/Netdisco/DB.pm file: lib/App/Netdisco/DB.pm
version: '53' version: '54'
App::Netdisco::DB::ExplicitLocking: App::Netdisco::DB::ExplicitLocking:
file: lib/App/Netdisco/DB/ExplicitLocking.pm file: lib/App/Netdisco/DB/ExplicitLocking.pm
App::Netdisco::DB::Result::Admin: App::Netdisco::DB::Result::Admin:
@@ -497,7 +497,7 @@ requires:
IO::Socket::SSL: '2.048' IO::Socket::SSL: '2.048'
JSON: '2.90' JSON: '2.90'
JSON::XS: '3.01' JSON::XS: '3.01'
List::MoreUtils: '0.33' List::MoreUtils: '0.428'
List::Util: '1.49' List::Util: '1.49'
MCE: '1.703' MCE: '1.703'
MIME::Base64: '3.13' MIME::Base64: '3.13'
@@ -518,7 +518,7 @@ requires:
Plack::Middleware::ReverseProxy: '0.15' Plack::Middleware::ReverseProxy: '0.15'
Pod::Usage: '0' Pod::Usage: '0'
Role::Tiny: '1.002005' Role::Tiny: '1.002005'
SNMP::Info: '3.64' SNMP::Info: '3.65'
SQL::Abstract: '1.85' SQL::Abstract: '1.85'
SQL::Translator: '0.11024' SQL::Translator: '0.11024'
Scope::Guard: '0' Scope::Guard: '0'
@@ -549,5 +549,5 @@ resources:
homepage: http://netdisco.org/ homepage: http://netdisco.org/
license: http://opensource.org/licenses/bsd-license.php license: http://opensource.org/licenses/bsd-license.php
repository: https://github.com/netdisco/netdisco repository: https://github.com/netdisco/netdisco
version: '2.040003' version: '2.040006'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018' x_serialization_backend: 'CPAN::Meta::YAML version 0.018'

View File

@@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use 5.010_000; use 5.010_000;
our $VERSION = '2.040003'; our $VERSION = '2.040006';
use App::Netdisco::Configuration; use App::Netdisco::Configuration;
=head1 NAME =head1 NAME

View File

@@ -262,20 +262,27 @@ sub renumber {
foreach my $set (qw/ foreach my $set (qw/
DeviceIp DeviceIp
DeviceModule DeviceModule
DevicePower
DeviceVlan
DevicePort DevicePort
DevicePortLog DevicePortLog
DevicePortPower DevicePortPower
DevicePortProperties
DevicePortSsid DevicePortSsid
DevicePortVlan DevicePortVlan
DevicePortWireless DevicePortWireless
DevicePower
DeviceVlan
/) { /) {
$schema->resultset($set) $schema->resultset($set)
->search({ip => $old_ip}) ->search({ip => $old_ip})
->update({ip => $new_ip}); ->update({ip => $new_ip});
} }
$schema->resultset('DeviceSkip')
->search({device => $new_ip})->delete;
$schema->resultset('DeviceSkip')
->search({device => $old_ip})
->update({device => $new_ip});
$schema->resultset('DevicePort') $schema->resultset('DevicePort')
->search({remote_ip => $old_ip}) ->search({remote_ip => $old_ip})
->update({remote_ip => $new_ip}); ->update({remote_ip => $new_ip});

View File

@@ -22,8 +22,8 @@ __PACKAGE__->result_source_instance->view_definition(<<ENDSQL
array_agg(dp.port) AS left_port, array_agg(dp.port) AS left_port,
array_agg(dp.name) AS left_descr, array_agg(dp.name) AS left_descr,
count(dpp.*) AS aggports,
sum(COALESCE(dpp.raw_speed, 0)) AS aggspeed, sum(COALESCE(dpp.raw_speed, 0)) AS aggspeed,
count(*) AS aggports,
di.ip AS right_ip, di.ip AS right_ip,
rd.dns AS right_dns, rd.dns AS right_dns,
@@ -32,8 +32,14 @@ __PACKAGE__->result_source_instance->view_definition(<<ENDSQL
array_agg(dp2.name) AS right_descr array_agg(dp2.name) AS right_descr
FROM device_port dp FROM device_port dp
LEFT OUTER JOIN device_port_properties dpp USING (ip,
port) LEFT OUTER JOIN device_port_properties dpp ON (
(dp.ip = dpp.ip) AND (dp.port = dpp.port)
AND (dp.type IS NULL
OR dp.type !~* '^(53|ieee8023adLag|propVirtual|l2vlan|l3ipvlan|135|136|137)\$')
AND (dp.is_master = 'false'
OR dp.slave_of IS NOT NULL) )
INNER JOIN device ld ON dp.ip = ld.ip INNER JOIN device ld ON dp.ip = ld.ip
INNER JOIN device_ip di ON dp.remote_ip = di.alias INNER JOIN device_ip di ON dp.remote_ip = di.alias
INNER JOIN device rd ON di.ip = rd.ip INNER JOIN device rd ON di.ip = rd.ip
@@ -45,12 +51,7 @@ __PACKAGE__->result_source_instance->view_definition(<<ENDSQL
WHERE dp.remote_port IS NOT NULL WHERE dp.remote_port IS NOT NULL
AND dp.port !~* 'vlan' AND dp.port !~* 'vlan'
AND (dp.descr IS NULL AND (dp.descr IS NULL OR dp.descr !~* 'vlan')
OR dp.descr !~* 'vlan')
AND (dp.type IS NULL
OR dp.type !~* '^(53|ieee8023adLag|propVirtual|l2vlan|l3ipvlan|135|136|137)\$')
AND (dp.is_master = 'false'
OR dp.slave_of IS NOT NULL)
GROUP BY left_ip, GROUP BY left_ip,
left_dns, left_dns,
@@ -58,6 +59,7 @@ __PACKAGE__->result_source_instance->view_definition(<<ENDSQL
right_ip, right_ip,
right_dns, right_dns,
right_name ) right_name )
SELECT * SELECT *
FROM BothWays b FROM BothWays b
WHERE NOT EXISTS WHERE NOT EXISTS

View File

@@ -15,6 +15,7 @@ __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL');
d.ip, d.name, d.dns, d.ip, d.name, d.dns,
p.port, p.name AS port_description, p.port, p.name AS port_description,
p.remote_ip, p.remote_id, p.remote_type, p.remote_port, p.remote_ip, p.remote_id, p.remote_type, p.remote_port,
dpp.remote_is_wap, dpp.remote_is_phone,
l.log AS comment, l.log AS comment,
a.log, a.finished a.log, a.finished
@@ -23,6 +24,7 @@ __PACKAGE__->result_source_instance->view_definition(<<'ENDSQL');
INNER JOIN device d USING (ip) INNER JOIN device d USING (ip)
LEFT OUTER JOIN device_skip ds LEFT OUTER JOIN device_skip ds
ON ('discover' = ANY(ds.actionset) AND p.remote_ip = ds.device) ON ('discover' = ANY(ds.actionset) AND p.remote_ip = ds.device)
LEFT OUTER JOIN device_port_properties dpp USING (ip, port)
LEFT OUTER JOIN device_port_log l USING (ip, port) LEFT OUTER JOIN device_port_log l USING (ip, port)
LEFT OUTER JOIN admin a LEFT OUTER JOIN admin a
ON (p.remote_ip = a.device AND a.action = 'discover') ON (p.remote_ip = a.device AND a.action = 'discover')
@@ -58,6 +60,10 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_nullable => 1 }, { data_type => "text", is_nullable => 1 },
"remote_id", "remote_id",
{ data_type => "text", is_nullable => 1 }, { data_type => "text", is_nullable => 1 },
"remote_is_wap",
{ data_type => "boolean", is_nullable => 1 },
"remote_is_phone",
{ data_type => "boolean", is_nullable => 1 },
"comment", "comment",
{ data_type => "text", is_nullable => 1 }, { data_type => "text", is_nullable => 1 },
"log", "log",

View File

@@ -47,16 +47,16 @@ sub with_times {
->search({}, ->search({},
{ {
'+columns' => { '+columns' => {
uptime_age => \("replace(age(timestamp 'epoch' + uptime / 100 * interval '1 second', " uptime_age => \("replace(age(timestamp 'epoch' + me.uptime / 100 * interval '1 second', "
."timestamp '1970-01-01 00:00:00-00')::text, 'mon', 'month')"), ."timestamp '1970-01-01 00:00:00-00')::text, 'mon', 'month')"),
first_seen_stamp => \"to_char(me.creation, 'YYYY-MM-DD HH24:MI')", first_seen_stamp => \"to_char(me.creation, 'YYYY-MM-DD HH24:MI')",
last_discover_stamp => \"to_char(last_discover, 'YYYY-MM-DD HH24:MI')", last_discover_stamp => \"to_char(me.last_discover, 'YYYY-MM-DD HH24:MI')",
last_macsuck_stamp => \"to_char(last_macsuck, 'YYYY-MM-DD HH24:MI')", last_macsuck_stamp => \"to_char(me.last_macsuck, 'YYYY-MM-DD HH24:MI')",
last_arpnip_stamp => \"to_char(last_arpnip, 'YYYY-MM-DD HH24:MI')", last_arpnip_stamp => \"to_char(me.last_arpnip, 'YYYY-MM-DD HH24:MI')",
since_first_seen => \"extract(epoch from (age(now(), me.creation)))", since_first_seen => \"extract(epoch from (age(now(), me.creation)))",
since_last_discover => \"extract(epoch from (age(now(), last_discover)))", since_last_discover => \"extract(epoch from (age(now(), me.last_discover)))",
since_last_macsuck => \"extract(epoch from (age(now(), last_macsuck)))", since_last_macsuck => \"extract(epoch from (age(now(), me.last_macsuck)))",
since_last_arpnip => \"extract(epoch from (age(now(), last_arpnip)))", since_last_arpnip => \"extract(epoch from (age(now(), me.last_arpnip)))",
}, },
}); });
} }

View File

@@ -10,7 +10,7 @@ our @EXPORT_OK = qw/
get_device get_device
delete_device delete_device
renumber_device renumber_device
match_devicetype match_to_setting
is_discoverable is_discoverable_now is_discoverable is_discoverable_now
is_arpnipable is_arpnipable_now is_arpnipable is_arpnipable_now
is_macsuckable is_macsuckable_now is_macsuckable is_macsuckable_now
@@ -120,7 +120,7 @@ sub renumber_device {
schema('netdisco')->resultset('UserLog')->create({ schema('netdisco')->resultset('UserLog')->create({
username => session('logged_in_user'), username => session('logged_in_user'),
userip => scalar eval {request->remote_address}, userip => scalar eval {request->remote_address},
event => (sprintf "Renumber device %s to %s", $device->ip, $new_ip), event => (sprintf "Renumber device %s to %s", $ip, $new_ip),
}); });
$happy = 1; $happy = 1;
@@ -129,7 +129,7 @@ sub renumber_device {
return $happy; return $happy;
} }
=head2 match_devicetype( $type, $setting_name ) =head2 match_to_setting( $type, $setting_name )
Given a C<$type> (which may be any text value), returns true if any of the Given a C<$type> (which may be any text value), returns true if any of the
list of regular expressions in C<$setting_name> is matched, otherwise returns list of regular expressions in C<$setting_name> is matched, otherwise returns
@@ -137,7 +137,7 @@ false.
=cut =cut
sub match_devicetype { sub match_to_setting {
my ($type, $setting_name) = @_; my ($type, $setting_name) = @_;
return 0 unless $type and $setting_name; return 0 unless $type and $setting_name;
return (scalar grep {$type =~ m/$_/} return (scalar grep {$type =~ m/$_/}
@@ -146,7 +146,7 @@ sub match_devicetype {
sub _bail_msg { debug $_[0]; return 0; } sub _bail_msg { debug $_[0]; return 0; }
=head2 is_discoverable( $ip, $device_type? ) =head2 is_discoverable( $ip, [$device_type, \@device_capabilities]? )
Given an IP address, returns C<true> if Netdisco on this host is permitted by Given an IP address, returns C<true> if Netdisco on this host is permitted by
the local configuration to discover the device. the local configuration to discover the device.
@@ -154,20 +154,32 @@ the local configuration to discover the device.
The configuration items C<discover_no> and C<discover_only> are checked The configuration items C<discover_no> and C<discover_only> are checked
against the given IP. against the given IP.
If C<$device_type> is also given, then C<discover_no_type> will also be If C<$device_type> is also given, then C<discover_no_type> will be checked.
checked. Also respects C<discover_phones> and C<discover_waps> if either are set to
false.
Returns false if the host is not permitted to discover the target device. Returns false if the host is not permitted to discover the target device.
=cut =cut
sub is_discoverable { sub is_discoverable {
my ($ip, $remote_type) = @_; my ($ip, $remote_type, $remote_cap) = @_;
my $device = get_device($ip) or return 0; my $device = get_device($ip) or return 0;
$remote_type ||= '';
$remote_cap ||= [];
if (match_devicetype($remote_type, 'discover_no_type')) { return _bail_msg("is_discoverable: $device matches wap_platforms but discover_waps is not enabled")
return _bail_msg("is_discoverable: $device matched discover_no_type"); if ((not setting('discover_waps')) and
} (match_to_setting($remote_type, 'wap_platforms') or
scalar grep {match_to_setting($_, 'wap_capabilities')} @$remote_cap));
return _bail_msg("is_discoverable: $device matches phone_platforms but discover_phones is not enabled")
if ((not setting('discover_phones')) and
(match_to_setting($remote_type, 'phone_platforms') or
scalar grep {match_to_setting($_, 'phone_capabilities')} @$remote_cap));
return _bail_msg("is_discoverable: $device matched discover_no_type")
if (match_to_setting($remote_type, 'discover_no_type'));
return _bail_msg("is_discoverable: $device matched discover_no") return _bail_msg("is_discoverable: $device matched discover_no")
if check_acl_no($device, 'discover_no'); if check_acl_no($device, 'discover_no');

View File

@@ -7,7 +7,7 @@ use Dancer::Plugin::Auth::Extensible;
use URI (); use URI ();
use URL::Encode 'url_params_mixed'; use URL::Encode 'url_params_mixed';
use App::Netdisco::Util::Device 'match_devicetype'; use App::Netdisco::Util::Device 'match_to_setting';
# build view settings for port connected nodes and devices # build view settings for port connected nodes and devices
set('connected_properties' => [ set('connected_properties' => [
@@ -20,7 +20,7 @@ hook 'before_template' => sub {
my $tokens = shift; my $tokens = shift;
# allow checking of discoverability of remote connected device # allow checking of discoverability of remote connected device
$tokens->{has_snmp} = sub { not match_devicetype(shift, 'discover_no_type') }; $tokens->{has_snmp} = sub { not match_to_setting(shift, 'discover_no_type') };
my $defaults = var('sidebar_defaults')->{'device_ports'} my $defaults = var('sidebar_defaults')->{'device_ports'}
or return; or return;

View File

@@ -27,6 +27,8 @@ get '/ajax/content/admin/undiscoveredneighbors' => require_role admin => sub {
# create a new row object to avoid hitting the DB in get_device() # create a new row object to avoid hitting the DB in get_device()
my $dev = schema('netdisco')->resultset('Device')->new({ip => $r->{remote_ip}}); my $dev = schema('netdisco')->resultset('Device')->new({ip => $r->{remote_ip}});
next unless is_discoverable( $dev, $r->{remote_type} ); next unless is_discoverable( $dev, $r->{remote_type} );
next if (not setting('discover_waps')) and $r->{remote_is_wap};
next if (not setting('discover_phones')) and $r->{remote_is_phone};
push @discoverable_results, $r; push @discoverable_results, $r;
} }
return unless scalar @discoverable_results; return unless scalar @discoverable_results;

View File

@@ -125,18 +125,13 @@ sub make_link_infostring {
(my $left_name = lc($link->{left_dns} || $link->{left_name} || $link->{left_ip})) =~ s/$domain$//; (my $left_name = lc($link->{left_dns} || $link->{left_name} || $link->{left_ip})) =~ s/$domain$//;
(my $right_name = lc($link->{right_dns} || $link->{right_name} || $link->{right_ip})) =~ s/$domain$//; (my $right_name = lc($link->{right_dns} || $link->{right_name} || $link->{right_ip})) =~ s/$domain$//;
if ($link->{aggports} == 1) { my @zipped = List::MoreUtils::zip6
return sprintf '<b>%s:%s</b> (%s)<br><b>%s:%s</b> (%s)', @{$link->{left_port}}, @{$link->{left_descr}},
$left_name, $link->{left_port}->[0], @{$link->{right_port}}, @{$link->{right_descr}};
($link->{left_descr}->[0] || 'no description'),
$right_name, $link->{right_port}->[0], return join '<br><br>', map { sprintf '<b>%s:%s</b> (%s)<br><b>%s:%s</b> (%s)',
($link->{right_descr}->[0] || 'no description'); $left_name, $_->[0], ($_->[1] || 'no description'),
} $right_name, $_->[2], ($_->[3] || 'no description') } @zipped;
else {
return sprintf '<b>%s:(%s)</b><br><b>%s:(%s)</b>',
$left_name, join(',', @{$link->{left_port}}),
$right_name, join(',', @{$link->{right_port}});
}
} }
ajax '/ajax/data/device/netmap' => require_login sub { ajax '/ajax/data/device/netmap' => require_login sub {
@@ -179,17 +174,6 @@ ajax '/ajax/data/device/netmap' => require_login sub {
]) : ()) ]) : ())
}, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' }); }, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' });
if ($vlan) {
$links = $links->search({
-or => [
{ 'left_vlans.vlan' => $vlan },
{ 'right_vlans.vlan' => $vlan },
],
}, {
join => [qw/left_vlans right_vlans/],
});
}
while (my $link = $links->next) { while (my $link = $links->next) {
push @{$data{'links'}}, { push @{$data{'links'}}, {
FROMID => $link->{left_ip}, FROMID => $link->{left_ip},
@@ -217,10 +201,19 @@ ajax '/ajax/data/device/netmap' => require_login sub {
join => 'throughput', join => 'throughput',
})->with_times; })->with_times;
# filter by vlan for all or neighbors only
if ($vlan) {
$devices = $devices->search(
{ 'vlans.vlan' => $vlan },
{ join => 'vlans' }
);
}
DEVICE: while (my $device = $devices->next) { DEVICE: while (my $device = $devices->next) {
# if in neighbors or vlan mode then use %ok_dev to filter # if in neighbors mode then use %ok_dev to filter
next DEVICE if (($mapshow eq 'neighbors') or $vlan) next DEVICE if ($device->ip ne $qdev->ip)
and (not $ok_dev{$device->ip}); and ($mapshow eq 'neighbors')
and (not $ok_dev{$device->ip}); # showing only neighbors but no link
# if location picked then filter # if location picked then filter
next DEVICE if ((scalar @lgrplist) and ((!defined $device->location) next DEVICE if ((scalar @lgrplist) and ((!defined $device->location)

View File

@@ -25,25 +25,6 @@ get '/ajax/content/device/ports' => require_login sub {
if ($f) { if ($f) {
if (($prefer eq 'vlan') or (not $prefer and $f =~ m/^\d+$/)) { if (($prefer eq 'vlan') or (not $prefer and $f =~ m/^\d+$/)) {
return unless $f =~ m/^\d+$/; return unless $f =~ m/^\d+$/;
if (param('invert')) {
$set = $set->search({
'me.vlan' => { '!=' => $f },
'port_vlans.vlan' => [
'-or' => { '!=' => $f }, { '=' => undef }
],
}, { join => 'port_vlans' });
}
else {
$set = $set->search({
-or => {
'me.vlan' => $f,
'port_vlans.vlan' => $f,
},
}, { join => 'port_vlans' });
}
return unless $set->count;
} }
else { else {
if (param('partial')) { if (param('partial')) {
@@ -120,7 +101,7 @@ get '/ajax/content/device/ports' => require_login sub {
# now begin to join tables depending on the selected columns/options # now begin to join tables depending on the selected columns/options
# get vlans on the port # get vlans on the port
# leave this query dormant (lazy) unless c_vmember is set # leave this query dormant (lazy) unless c_vmember is set or vlan filtering
my $vlans = $set->search({}, { my $vlans = $set->search({}, {
select => [ select => [
'port', 'port',
@@ -131,7 +112,7 @@ get '/ajax/content/device/ports' => require_login sub {
group_by => 'me.port', group_by => 'me.port',
}); });
if (param('c_vmember')) { if (param('c_vmember') or ($prefer eq 'vlan') or (not $prefer and $f =~ m/^\d+$/)) {
$vlans = { map {( $vlans = { map {(
$_->port => { $_->port => {
# DBIC smart enough to work out this should be an arrayref :) # DBIC smart enough to work out this should be an arrayref :)
@@ -194,13 +175,37 @@ get '/ajax/content/device/ports' => require_login sub {
# also get remote LLDP inventory if asked for # also get remote LLDP inventory if asked for
$set = $set->with_remote_inventory if param('n_inventory'); $set = $set->with_remote_inventory if param('n_inventory');
# sort ports (empty set would be a 'no records' msg) # run query
my $results = [ sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all ]; my @results = $set->all;
return unless scalar @$results;
# filter for tagged vlan using existing agg query,
# which is better than join inflation
if (($prefer eq 'vlan') or (not $prefer and $f =~ m/^\d+$/)) {
if (param('invert')) {
@results = grep {
(!defined $_->vlan or $_->vlan ne $f)
and
(0 == scalar grep {defined and $_ ne $f} @{ $vlans->{$_->port}->{vlan_set} })
} @results;
}
else {
@results = grep {
($_->vlan eq $f)
or
(scalar grep {defined and $_ eq $f} @{ $vlans->{$_->port}->{vlan_set} })
} @results;
}
}
# sort ports
@results = sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } @results;
# empty set would be a 'no records' msg
return unless scalar @results;
if (request->is_ajax) { if (request->is_ajax) {
template 'ajax/device/ports.tt', { template 'ajax/device/ports.tt', {
results => $results, results => \@results,
nodes => $nodes_name, nodes => $nodes_name,
ips => $ips_name, ips => $ips_name,
device => $device, device => $device,
@@ -210,7 +215,7 @@ get '/ajax/content/device/ports' => require_login sub {
else { else {
header( 'Content-Type' => 'text/comma-separated-values' ); header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/device/ports_csv.tt', { template 'ajax/device/ports_csv.tt', {
results => $results, results => \@results,
nodes => $nodes_name, nodes => $nodes_name,
ips => $ips_name, ips => $ips_name,
device => $device, device => $device,

View File

@@ -110,12 +110,12 @@ get '/ajax/content/report/ipinventory' => require_login sub {
'ip', 'mac', 'dns', 'time_last', 'time_first', 'ip', 'mac', 'dns', 'time_last', 'time_first',
'active', 'node', 'age' 'active', 'node', 'age'
], ],
order_by => [{-asc => 'ip'}, {-desc => 'active'}], order_by => [{-asc => 'ip'}, {-desc => 'active'}, {-asc => 'node'}],
} }
)->as_query; )->as_query;
my $rs; my $rs;
if ( $start && $end ) { if ( $start and $end ) {
$start = $start . ' 00:00:00'; $start = $start . ' 00:00:00';
$end = $end . ' 23:59:59'; $end = $end . ' 23:59:59';

View File

@@ -27,35 +27,51 @@ ajax '/ajax/content/search/node' => require_login sub {
my @active = (param('archived') ? () : (-bool => 'active')); my @active = (param('archived') ? () : (-bool => 'active'));
my (@times, @wifitimes, @porttimes); my (@times, @wifitimes, @porttimes);
if ($start and $end) { if ( $start and $end ) {
$start = $start . ' 00:00:00'; $start = $start . ' 00:00:00';
$end = $end . ' 23:59:59'; $end = $end . ' 23:59:59';
if ($agenot) { if ($agenot) {
@times = (-or => [ @times = (-or => [
time_first => [ { '<', $start }, undef ], time_first => [ undef ],
time_last => { '>', $end }, time_last => [ { '<', $start }, { '>', $end } ]
]); ]);
@wifitimes = (-or => [ @wifitimes = (-or => [
time_last => { '<', $start }, time_first => [ undef ],
time_last => { '>', $end }, time_last => [ { '<', $start }, { '>', $end } ]
]); ]);
@porttimes = (-or => [ @porttimes = (-or => [
creation => { '<', $start }, creation => [ undef ],
creation => { '>', $end }, creation => [ { '<', $start }, { '>', $end } ]
]); ]);
} }
else { else {
@times = (-and => [ @times = (-or => [
time_first => { '>=', $start }, -and => [
time_last => { '<=', $end }, time_first => undef,
]); time_last => undef,
@wifitimes = (-and => [ ],
-and => [
time_last => { '>=', $start }, time_last => { '>=', $start },
time_last => { '<=', $end }, time_last => { '<=', $end },
],
]); ]);
@porttimes = (-and => [ @wifitimes = (-or => [
-and => [
time_first => undef,
time_last => undef,
],
-and => [
time_last => { '>=', $start },
time_last => { '<=', $end },
],
]);
@porttimes = (-or => [
creation => undef,
-and => [
creation => { '>=', $start }, creation => { '>=', $start },
creation => { '<=', $end }, creation => { '<=', $end },
],
]); ]);
} }
} }

View File

@@ -49,7 +49,7 @@ ajax '/ajax/data/port/typeahead' => require_login sub {
if $port; if $port;
my $results = [ my $results = [
map {{ label => (sprintf "%s (%s)", $_->port, $_->name), value => $_->port }} map {{ label => (sprintf "%s (%s)", $_->port, ($_->name || '')), value => $_->port }}
sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all
]; ];

View File

@@ -45,7 +45,7 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
# only enqueue if device is not already discovered, # only enqueue if device is not already discovered,
# discover_* config permits the discovery # discover_* config permits the discovery
foreach my $neighbor (@to_discover) { foreach my $neighbor (@to_discover) {
my ($ip, $remote_type, $remote_id) = @$neighbor; my ($ip, $remote_id) = @$neighbor;
if ($seen_ip{ $ip }++) { if ($seen_ip{ $ip }++) {
debug sprintf debug sprintf
' queue - skip: IP %s is already queued from %s', ' queue - skip: IP %s is already queued from %s',
@@ -63,13 +63,6 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
my $newdev = get_device($ip); my $newdev = get_device($ip);
next if $newdev->in_storage; next if $newdev->in_storage;
if (not is_discoverable($newdev, $remote_type)) {
debug sprintf
' queue - skip: %s of type [%s] excluded by discover_* config',
$ip, ($remote_type || '');
next;
}
# risk of things going wrong...? # risk of things going wrong...?
# https://quickview.cloudapps.cisco.com/quickview/bug/CSCur12254 # https://quickview.cloudapps.cisco.com/quickview/bug/CSCur12254
@@ -127,6 +120,7 @@ sub store_neighbors {
my $c_port = $snmp->c_port; my $c_port = $snmp->c_port;
my $c_id = $snmp->c_id; my $c_id = $snmp->c_id;
my $c_platform = $snmp->c_platform; my $c_platform = $snmp->c_platform;
my $c_cap = $snmp->c_cap;
# cache the device ports to save hitting the database for many single rows # cache the device ports to save hitting the database for many single rows
vars->{'device_ports'} = vars->{'device_ports'} =
@@ -176,6 +170,7 @@ sub store_neighbors {
my $remote_port = undef; my $remote_port = undef;
my $remote_type = Encode::decode('UTF-8', $c_platform->{$entry} || ''); my $remote_type = Encode::decode('UTF-8', $c_platform->{$entry} || '');
my $remote_id = Encode::decode('UTF-8', $c_id->{$entry}); my $remote_id = Encode::decode('UTF-8', $c_id->{$entry});
my $remote_cap = $c_cap->{$entry} || [];
next unless $remote_ip; next unless $remote_ip;
my $r_netaddr = NetAddr::IP::Lite->new($remote_ip); my $r_netaddr = NetAddr::IP::Lite->new($remote_ip);
@@ -246,7 +241,15 @@ sub store_neighbors {
# what we came here to do.... discover the neighbor # what we came here to do.... discover the neighbor
debug sprintf ' [%s] neigh - %s with ID [%s] on %s', debug sprintf ' [%s] neigh - %s with ID [%s] on %s',
$device->ip, $remote_ip, ($remote_id || ''), $port; $device->ip, $remote_ip, ($remote_id || ''), $port;
push @to_discover, [$remote_ip, $remote_type, $remote_id];
if (is_discoverable($remote_ip, $remote_type, $remote_cap)) {
push @to_discover, [$remote_ip, $remote_id];
}
else {
debug sprintf
' [%s] neigh - skip: %s of type [%s] excluded by discover_* config',
$device->ip, $remote_ip, ($remote_type || '');
}
$remote_port = $c_port->{$entry}; $remote_port = $c_port->{$entry};
if (defined $remote_port) { if (defined $remote_port) {

View File

@@ -8,7 +8,7 @@ use App::Netdisco::Transport::SNMP ();
use Dancer::Plugin::DBIC 'schema'; use Dancer::Plugin::DBIC 'schema';
use Encode; use Encode;
use App::Netdisco::Util::Device 'match_devicetype'; use App::Netdisco::Util::Device 'match_to_setting';
register_worker({ phase => 'main', driver => 'snmp' }, sub { register_worker({ phase => 'main', driver => 'snmp' }, sub {
my ($job, $workerconf) = @_; my ($job, $workerconf) = @_;
@@ -86,12 +86,12 @@ register_worker({ phase => 'main', driver => 'snmp' }, sub {
my $remote_type = Encode::decode('UTF-8', $c_platform->{$idx} || ''); my $remote_type = Encode::decode('UTF-8', $c_platform->{$idx} || '');
$properties{ $port }->{remote_is_wap} = 'true' $properties{ $port }->{remote_is_wap} = 'true'
if scalar grep {match_devicetype($_, 'wap_capabilities')} @$remote_cap if scalar grep {match_to_setting($_, 'wap_capabilities')} @$remote_cap
or match_devicetype($remote_type, 'wap_platforms'); or match_to_setting($remote_type, 'wap_platforms');
$properties{ $port }->{remote_is_phone} = 'true' $properties{ $port }->{remote_is_phone} = 'true'
if scalar grep {match_devicetype($_, 'phone_capabilities')} @$remote_cap if scalar grep {match_to_setting($_, 'phone_capabilities')} @$remote_cap
or match_devicetype($remote_type, 'phone_platforms'); or match_to_setting($remote_type, 'phone_platforms');
next unless scalar grep {defined && m/^inventory$/} @{ $rem_media_cap->{$idx} }; next unless scalar grep {defined && m/^inventory$/} @{ $rem_media_cap->{$idx} };

View File

@@ -238,6 +238,7 @@ register_worker({ phase => 'early', driver => 'snmp' }, sub {
# must do this after building %interfaces so that we can set is_master # must do this after building %interfaces so that we can set is_master
foreach my $sidx (keys %$agg_ports) { foreach my $sidx (keys %$agg_ports) {
my $slave = $interfaces->{$sidx} or next; my $slave = $interfaces->{$sidx} or next;
next unless defined $agg_ports->{$sidx}; # slave without a master?!
my $master = $interfaces->{ $agg_ports->{$sidx} } or next; my $master = $interfaces->{ $agg_ports->{$sidx} } or next;
next unless exists $interfaces{$slave} and exists $interfaces{$master}; next unless exists $interfaces{$slave} and exists $interfaces{$master};

View File

@@ -7,7 +7,7 @@ use aliased 'App::Netdisco::Worker::Status';
use App::Netdisco::Transport::SNMP (); use App::Netdisco::Transport::SNMP ();
use App::Netdisco::Util::Permission 'check_acl_no'; use App::Netdisco::Util::Permission 'check_acl_no';
use App::Netdisco::Util::PortMAC 'get_port_macs'; use App::Netdisco::Util::PortMAC 'get_port_macs';
use App::Netdisco::Util::Device 'match_devicetype'; use App::Netdisco::Util::Device 'match_to_setting';
use App::Netdisco::Util::Node 'check_mac'; use App::Netdisco::Util::Node 'check_mac';
use App::Netdisco::Util::SNMP 'snmp_comm_reindex'; use App::Netdisco::Util::SNMP 'snmp_comm_reindex';
use Dancer::Plugin::DBIC 'schema'; use Dancer::Plugin::DBIC 'schema';
@@ -342,7 +342,7 @@ sub walk_fwtable {
# neighbors otherwise it would kill the DB with device lookups. # neighbors otherwise it would kill the DB with device lookups.
my $neigh_cannot_macsuck = eval { # can fail my $neigh_cannot_macsuck = eval { # can fail
check_acl_no(($device_port->neighbor || "0 but true"), 'macsuck_unsupported') || check_acl_no(($device_port->neighbor || "0 but true"), 'macsuck_unsupported') ||
match_devicetype($device_port->remote_type, 'macsuck_unsupported_type') }; match_to_setting($device_port->remote_type, 'macsuck_unsupported_type') };
if ($device_port->is_uplink) { if ($device_port->is_uplink) {
if ($neigh_cannot_macsuck) { if ($neigh_cannot_macsuck) {

View File

@@ -229,9 +229,9 @@ devices_no: []
devices_only: [] devices_only: []
discover_no: [] discover_no: []
discover_only: [] discover_only: []
discover_no_type: discover_no_type: []
- '(?i)phone' discover_waps: true
- '(?i)(?:wap|wireless)' discover_phones: false
discover_min_age: 0 discover_min_age: 0
macsuck_no: [] macsuck_no: []
macsuck_only: [] macsuck_only: []

View File

@@ -39,20 +39,18 @@ device_auth:
# ¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸ # ¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸
#
# SOME MORE INTERESTING SETTINGS WHERE THE DEFAULTS ARE PROBABLY OKAY
#
# ¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸ # ¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸
# --------------------------------------------------------------- # discover Wireless Access Points, but not IP Phones
# OTHER INTERESTING SETTINGS WHERE THE DEFAULTS ARE PROBABLY OKAY
# ---------------------------------------------------------------
# do not discover IP Phones or Wireless Access Points.
# usually these are visible as device neighbors but don't support # usually these are visible as device neighbors but don't support
# SNMP, which just clogs up the job queue. # SNMP, which just clogs up the job queue.
# ``````````````````````````````````````````````````````````````` # ```````````````````````````````````````````````````````````````
#discover_no_type: #discover_waps: true
# - '(?i)phone' #disover_phones: false
# - '(?i)(?:wap|wireless)'
# this is the schedule for automatically keeping netdisco up-to-date; # this is the schedule for automatically keeping netdisco up-to-date;
# these are good defaults, so only uncomment if needing to change. # these are good defaults, so only uncomment if needing to change.