From e52f789b450a1979aaf04626c2e34459a3d9ef6f Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Thu, 9 Nov 2023 19:54:34 +0000 Subject: [PATCH] #1009 neighbour map depth and/or until end of lldp chains * sidebar and style changes * switch server code to new params and values * working algo for cloud and neighbor depth * store map positions for lldp cloud with depth * color the root node uniquely * restrict all devices, depth, cloud map to < 1000 devices --- lib/App/Netdisco/DB.pm | 2 +- lib/App/Netdisco/DB/Result/NetmapPositions.pm | 2 + lib/App/Netdisco/Web/Device.pm | 1 + .../Netdisco/Web/Plugin/Device/Neighbors.pm | 55 ++++++++++++++++--- share/config.yml | 4 +- share/public/css/netdisco.css | 5 ++ .../App-Netdisco-DB-83-84-PostgreSQL.sql | 7 +++ share/views/ajax/device/netmap.tt | 2 +- share/views/sidebar/device/netmap.tt | 22 ++++++-- 9 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 share/schema_versions/App-Netdisco-DB-83-84-PostgreSQL.sql diff --git a/lib/App/Netdisco/DB.pm b/lib/App/Netdisco/DB.pm index 27b463d4..616633a3 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 = 83; # schema version used for upgrades, keep as integer + $VERSION = 84; # schema version used for upgrades, keep as integer use Path::Class; use File::ShareDir 'dist_dir'; diff --git a/lib/App/Netdisco/DB/Result/NetmapPositions.pm b/lib/App/Netdisco/DB/Result/NetmapPositions.pm index f5b467fa..1779376e 100644 --- a/lib/App/Netdisco/DB/Result/NetmapPositions.pm +++ b/lib/App/Netdisco/DB/Result/NetmapPositions.pm @@ -17,6 +17,8 @@ __PACKAGE__->add_columns( { data_type => "text[]", is_nullable => 0 }, "vlan", { data_type => "integer", is_nullable => 0, default => 0 }, + "depth", + { data_type => "integer", is_nullable => 0, default => 0 }, "positions", { data_type => "text", is_nullable => 0 }, ); diff --git a/lib/App/Netdisco/Web/Device.pm b/lib/App/Netdisco/Web/Device.pm index 1b0872f0..cbfdcfed 100644 --- a/lib/App/Netdisco/Web/Device.pm +++ b/lib/App/Netdisco/Web/Device.pm @@ -86,6 +86,7 @@ get '/device' => require_login sub { template 'device', { netdisco_device => $first, display_name => ($others ? $first->ip : ($first->dns || $first->ip)), + device_count => schema(vars->{'tenant'})->resultset('Device')->count(), lgroup_list => [ schema(vars->{'tenant'})->resultset('Device')->get_distinct_col('location') ], hgroup_list => setting('host_group_displaynames'), device => params->{'tab'}, diff --git a/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm b/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm index 0c94af4a..5b1fd196 100644 --- a/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm +++ b/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm @@ -30,7 +30,9 @@ ajax '/ajax/data/device/netmappositions' => require_login sub { undef $vlan if (defined $vlan and $vlan !~ m/^\d+$/); my $mapshow = param('mapshow'); - return if !defined $mapshow or $mapshow !~ m/^(?:all|neighbors)$/; + return if !defined $mapshow or $mapshow !~ m/^(?:all|cloud|depth)$/; + my $depth = param('depth') || 1; + return if $depth !~ m/^\d+$/; # list of groups selected by user and passed in param my $hgroup = (ref [] eq ref param('hgroup') ? param('hgroup') : [param('hgroup')]); @@ -56,7 +58,8 @@ ajax '/ajax/data/device/netmappositions' => require_login sub { return unless scalar keys %clean; my $posrow = schema(vars->{'tenant'})->resultset('NetmapPositions')->find({ - device => (($mapshow eq 'neighbors') ? $qdev->ip : undef), + device => ($mapshow ne 'all' ? $qdev->ip : undef), + depth => ($mapshow eq 'depth' ? $depth : 0), host_groups => \[ '= ?', [host_groups => [sort @hgrplist]] ], locations => \[ '= ?', [locations => [sort @lgrplist]] ], vlan => ($vlan || 0), @@ -67,7 +70,8 @@ ajax '/ajax/data/device/netmappositions' => require_login sub { } else { schema(vars->{'tenant'})->resultset('NetmapPositions')->create({ - device => (($mapshow eq 'neighbors') ? $qdev->ip : undef), + device => ($mapshow ne 'all' ? $qdev->ip : undef), + depth => ($mapshow eq 'depth' ? $depth : 0), host_groups => [sort @hgrplist], locations => [sort @lgrplist], vlan => ($vlan || 0), @@ -143,8 +147,9 @@ ajax '/ajax/data/device/netmap' => require_login sub { undef $vlan if (defined $vlan and $vlan !~ m/^\d+$/); my $colorby = (param('colorby') || 'speed'); - my $mapshow = (param('mapshow') || 'neighbors'); - $mapshow = 'neighbors' if $mapshow !~ m/^(?:all|neighbors)$/; + my $mapshow = (param('mapshow') || 'depth'); + my $depth = (param('depth') || 1); + $mapshow = 'depth' if $mapshow !~ m/^(?:all|cloud|depth)$/; $mapshow = 'all' unless $qdev->in_storage; # list of groups selected by user and passed in param @@ -169,7 +174,7 @@ ajax '/ajax/data/device/netmap' => require_login sub { my %seen_link = (); my $links = schema(vars->{'tenant'})->resultset('Virtual::DeviceLinks')->search({ - ($mapshow eq 'neighbors' ? ( -or => [ + (($mapshow eq 'depth' and $depth == 1) ? ( -or => [ { left_ip => $qdev->ip }, { right_ip => $qdev->ip }, ]) : ()) @@ -193,10 +198,41 @@ ajax '/ajax/data/device/netmap' => require_login sub { ++$seen_link{$link->{left_ip} ."\0". $link->{right_ip}}; } + # filter by lldp cloud or depth + # this is O(N^2) or worse + + my %cloud = ($qdev->ip => 1); + my $seen_cloud = scalar keys %cloud; + my $passes = ($mapshow eq 'cloud' ? 999 : $depth); + + if ($mapshow eq 'cloud' or ($mapshow eq 'depth' and $depth > 1)) { + while ($seen_cloud > 0 and $passes > 0) { + --$passes; + $seen_cloud = 0; + + foreach my $cip (keys %cloud) { + foreach my $okip (keys %ok_dev) { + next if exists $cloud{$okip}; + + if (exists $seen_link{$cip ."\0". $okip} + or exists $seen_link{$okip ."\0". $cip}) { + + ++$cloud{$okip}; + ++$seen_cloud; + } + } + } + } + } + elsif ($mapshow eq 'depth' and $depth == 1) { + %cloud = %ok_dev; + } + # DEVICES (NODES) my $posrow = schema(vars->{'tenant'})->resultset('NetmapPositions')->find({ - device => (($mapshow eq 'neighbors') ? $qdev->ip : undef), + device => ($mapshow ne 'all' ? $qdev->ip : undef), + depth => ($mapshow eq 'depth' ? $depth : 0), host_groups => \[ '= ?', [host_groups => [sort @hgrplist]] ], locations => \[ '= ?', [locations => [sort @lgrplist]] ], vlan => ($vlan || 0), @@ -219,8 +255,8 @@ ajax '/ajax/data/device/netmap' => require_login sub { DEVICE: while (my $device = $devices->next) { # if in neighbors mode then use %ok_dev to filter next DEVICE if ($device->ip ne $qdev->ip) - and ($mapshow eq 'neighbors') - and (not $ok_dev{$device->ip}); # showing only neighbors but no link + and ($mapshow ne 'all') + and (not $cloud{$device->ip}); # showing only neighbors but no link # if location picked then filter next DEVICE if ((scalar @lgrplist) and ((!defined $device->location) @@ -249,6 +285,7 @@ ajax '/ajax/data/device/netmap' => require_login sub { ID => $device->ip, SIZEVALUE => (param('dynamicsize') ? $color_lkp{speed} : 3000), ((exists $color_lkp{$colorby}) ? (COLORVALUE => $color_lkp{$colorby}) : ()), + (($device->ip eq $qdev->ip) ? (COLORVALUE => 'ROOTNODE') : ()), LABEL => (param('showips') ? ($device->ip .' '. $name) : $name), ORIG_LABEL => $name, INFOSTRING => make_node_infostring($device), diff --git a/share/config.yml b/share/config.yml index 7d1a1c1c..508403b6 100644 --- a/share/config.yml +++ b/share/config.yml @@ -159,7 +159,9 @@ sidebar_defaults: device_netmap: showips: { default: null } showspeed: { default: null } - mapshow: { default: neighbors } + mapshow: { default: depth } + depth: { default: 1 } + too_many_devices: { default: 1000 } colorby: { default: speed } dynamicsize: { default: checked } report_moduleinventory: diff --git a/share/public/css/netdisco.css b/share/public/css/netdisco.css index 8a712c16..7ad931bc 100644 --- a/share/public/css/netdisco.css +++ b/share/public/css/netdisco.css @@ -494,8 +494,13 @@ td > form.nd_inline-form { vertical-align: text-bottom; } #nd_vlan-entry { + margin-top: 7px; width: 45px; } +#nd_mapshow-hops { + width: 30px; + margin-bottom: 0px; +} /* netmap maximise icon */ #nd2_netmap-fullscreen { diff --git a/share/schema_versions/App-Netdisco-DB-83-84-PostgreSQL.sql b/share/schema_versions/App-Netdisco-DB-83-84-PostgreSQL.sql new file mode 100644 index 00000000..128f4697 --- /dev/null +++ b/share/schema_versions/App-Netdisco-DB-83-84-PostgreSQL.sql @@ -0,0 +1,7 @@ +BEGIN; + +ALTER TABLE netmap_positions ADD COLUMN "depth" integer DEFAULT 0 NOT NULL; + +UPDATE netmap_positions SET depth = 0 WHERE device IS NOT NULL; + +COMMIT; diff --git a/share/views/ajax/device/netmap.tt b/share/views/ajax/device/netmap.tt index a2bd8f45..cc5f7aea 100644 --- a/share/views/ajax/device/netmap.tt +++ b/share/views/ajax/device/netmap.tt @@ -216,7 +216,7 @@ function saveMapPositions() { graph.inspect().main.nodes.each(function(n) { n.fixed = true }); $.post( '[% uri_for('/ajax/data/device/netmappositions') | none %]' - ,$("#nd_vlan-entry, #nd_hgroup-select, #nd_lgroup-select, #nq, input[name='mapshow']").serialize() + ,$("#nd_vlan-entry, #nd_mapshow-hops, #nd_hgroup-select, #nd_lgroup-select, #nq, input[name='mapshow']").serialize() + '&positions=' + JSON.stringify(graph.positions()) ); // toastr.success('Saved map positions.'); diff --git a/share/views/sidebar/device/netmap.tt b/share/views/sidebar/device/netmap.tt index a72c3d25..1a8b3b7c 100644 --- a/share/views/sidebar/device/netmap.tt +++ b/share/views/sidebar/device/netmap.tt @@ -60,26 +60,37 @@
+ [% IF device_count < (vars.sidebar_defaults.device_netmap.too_many_devices || 1000) %]
- - + +
+
+ + +
+ [% END %] [% IF hgroup_list.size %] + Device Groups:
[% END %] [% IF lgroup_list.size %] + Device Locations: