From 2897eda68481f248d61ae4e46b1f2113df0e3923 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Tue, 3 Sep 2019 09:09:55 +0100 Subject: [PATCH] #587 #561 update pseudo devices to better support ssh arpnip this patch resets all pseudo devices to have no layer3 support but adds a feature to the pseudo devices admin panel to enable layer3 support. it also changes arpnip and arpwalk behaviour to always permit the action if layer3 is available (ignoring the vendor). documentation will need updating to tell users to create pseudo devices with layer3 support when they want to arpnip an unsupported platform. arpnip with ssh/cli against a supported platform (one that can be discovered) will continue to work normally. Squashed commit of the following: commit 9dad5be81db0569a757978af4e0560431ba3d3ad Author: Oliver Gorwits Date: Tue Sep 3 09:03:53 2019 +0100 allow pseudo with layer 3 to run arpnip commit 7d97943fcde6240c2c2aa21c4bc38268d96ff1ec Author: Oliver Gorwits Date: Tue Sep 3 08:59:10 2019 +0100 allow pseudo devices with layer 2/3 capability commit d1fdf574e33d2454f00a28c0b04b494b45d8a920 Author: Oliver Gorwits Date: Tue Sep 3 08:55:41 2019 +0100 move pseudo and layer checks to is_able from is_able_now commit e0f72ef67df2128b67d3197cd6c2df8191173a8b Author: Oliver Gorwits Date: Tue Sep 3 08:51:42 2019 +0100 ports defaults to one commit 86ba01270cc4dd2b2167faa7dabfce1c319ee7af Author: Oliver Gorwits Date: Tue Sep 3 08:50:45 2019 +0100 add tooltip for arpnip toggle commit cdd2470228d07777567ffafe00b797376c1bc177 Author: Oliver Gorwits Date: Tue Sep 3 08:34:46 2019 +0100 simplify template commit 46236d68eae4b9bd8fcd47098782143051a62064 Author: Oliver Gorwits Date: Sun Sep 1 23:53:56 2019 +0100 a fix up for pseudo devices which need layer 3 commit 016d249efc83eeeff949ef120a30de2b7f5563cd Author: Oliver Gorwits Date: Sun Sep 1 20:37:11 2019 +0100 do not wrap buttons commit 1ec1402e0c0951a3de1e3f3492443f89299324bb Author: Oliver Gorwits Date: Sun Sep 1 20:33:03 2019 +0100 implement user settable layer-three service for pseudo devices commit a267efa3d8138e4a6b54f5585f19be423253626e Author: Oliver Gorwits Date: Sun Sep 1 18:39:22 2019 +0100 only set layer if successful action commit b108be5e23bee319dd761ae5970161cea4717d31 Author: Oliver Gorwits Date: Sun Sep 1 18:32:19 2019 +0100 should defer SNMP against pseudo devices commit 897ba3a62956a0f6f9bb708f5971e1f4d1474653 Merge: e0ddbaab a7348900 Author: Oliver Gorwits Date: Sun Sep 1 14:54:36 2019 +0100 Merge branch 'master' into og-pseudo-vs-cli-arpnip commit e0ddbaab0807fcfd7eb33de3a8b1df9ca394d99f Author: Oliver Gorwits Date: Mon Aug 26 11:35:13 2019 +0100 as last commit, for discover commit 61f9c89040912391ac94a67e1c2ab93c8fb207a9 Author: Oliver Gorwits Date: Sun Aug 25 23:55:38 2019 +0100 move pseudo and layer checks into is_*able functions commit 8b010d40238d5c7bb566249181da20a6ac7a68f6 Author: Oliver Gorwits Date: Sun Aug 25 18:38:11 2019 +0100 any device completing macsuck/arpnip must have that layer commit a11bce7863b29c12ed6219f7a828ed5340a1b521 Author: Oliver Gorwits Date: Sun Aug 25 18:33:27 2019 +0100 clean up device layers commit d2661bff61717dc74347cd9b3b5602bc6cb87bd9 Author: Oliver Gorwits Date: Sun Aug 25 18:18:02 2019 +0100 first make arpnip behave like other jobs towards pseudo devices --- lib/App/Netdisco/JobQueue/PostgreSQL.pm | 13 ++++++++ lib/App/Netdisco/Transport/SNMP.pm | 6 ++++ lib/App/Netdisco/Util/Device.pm | 30 +++++++++++++------ .../Web/Plugin/AdminTask/PseudoDevice.pm | 5 +++- lib/App/Netdisco/Worker/Plugin/Arpnip.pm | 3 -- .../Netdisco/Worker/Plugin/Arpnip/Nodes.pm | 28 +++++++++-------- lib/App/Netdisco/Worker/Plugin/Discover.pm | 3 -- lib/App/Netdisco/Worker/Plugin/Macsuck.pm | 6 ---- share/public/css/netdisco.css | 7 +++++ .../App-Netdisco-DB-58-59-PostgreSQL.sql | 6 ++++ share/views/ajax/admintask/pseudodevice.tt | 24 ++++++++++++++- share/views/ajax/admintask/users.tt | 4 +-- share/views/js/admintask.js | 3 +- 13 files changed, 99 insertions(+), 39 deletions(-) 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/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/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/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/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/share/public/css/netdisco.css b/share/public/css/netdisco.css index 71136174..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; diff --git a/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql b/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql index cd073f78..53983c95 100644 --- a/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql +++ b/share/schema_versions/App-Netdisco-DB-58-59-PostgreSQL.sql @@ -2,4 +2,10 @@ 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 2378a358..f5fbfa05 100644 --- a/share/views/ajax/admintask/users.tt +++ b/share/views/ajax/admintask/users.tt @@ -26,7 +26,7 @@ - + @@ -62,7 +62,7 @@ - +