From 6079c7dfed25b01a4d32fe5d7bde1d6a154f7e73 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 29 Dec 2013 16:23:46 +0000 Subject: [PATCH] Device Neighbor Map can have max depth and VLAN filter --- Netdisco/Changes | 1 + .../Netdisco/DB/Result/Virtual/DeviceLinks.pm | 20 +++++++++++-- .../Netdisco/Web/Plugin/Device/Neighbors.pm | 30 +++++++++++++++++-- Netdisco/share/public/css/netdisco.css | 22 +++++++------- Netdisco/share/views/ajax/device/netmap.tt | 20 +++++++++---- Netdisco/share/views/device.tt | 11 ------- Netdisco/share/views/js/common.js | 7 ++++- Netdisco/share/views/js/device.js | 8 ----- Netdisco/share/views/sidebar/device/netmap.tt | 24 +++++++++++++++ 9 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 Netdisco/share/views/sidebar/device/netmap.tt diff --git a/Netdisco/Changes b/Netdisco/Changes index 810b3cfc..80e8c60b 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,7 @@ * Add IP Phones discovered through LLDP/CDP report * Add device/node/vlan/port specific search from Navbar + * [#3] [#47] Device Neighbor Map can have max depth and VLAN filter [ENHANCEMENTS] diff --git a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm index c2f58ea2..545e9ce2 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/Virtual/DeviceLinks.pm @@ -10,11 +10,11 @@ __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); __PACKAGE__->table('device_links'); __PACKAGE__->result_source_instance->is_virtual(1); __PACKAGE__->result_source_instance->view_definition(<add_columns( 'left_ip' => { data_type => 'inet', }, + 'left_port' => { + data_type => 'text', + }, 'right_ip' => { data_type => 'inet', }, + 'right_port' => { + data_type => 'text', + }, ); +__PACKAGE__->has_many('left_vlans', 'App::Netdisco::DB::Result::DevicePortVlan', + { 'foreign.ip' => 'self.left_ip', 'foreign.port' => 'self.left_port' }, + { join_type => 'INNER' } ); + +__PACKAGE__->has_many('right_vlans', 'App::Netdisco::DB::Result::DevicePortVlan', + { 'foreign.ip' => 'self.right_ip', 'foreign.port' => 'self.right_port' }, + { join_type => 'INNER' } ); + 1; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm index d4706671..dc283eba 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm @@ -23,8 +23,11 @@ sub _get_name { } sub _add_children { - my ($ptr, $childs) = @_; + my ($ptr, $childs, $step, $limit) = @_; + + return $step if $limit and $step > $limit; my @legit = (); + my $max = $step; foreach my $c (@$childs) { next if exists var('seen')->{$c}; @@ -39,14 +42,24 @@ sub _add_children { for (my $i = 0; $i < @legit; $i++) { $ptr->[$i]->{children} = []; - _add_children($ptr->[$i]->{children}, var('links')->{$legit[$i]}); + my $nm = _add_children($ptr->[$i]->{children}, var('links')->{$legit[$i]}, + ($step + 1), $limit); + $max = $nm if $nm > $max; } + + return $max; } # d3 seems not to use proper ajax semantics, so get instead of ajax get '/ajax/data/device/netmap' => require_login sub { my $q = param('q'); + my $vlan = param('vlan'); + undef $vlan if (defined $vlan and $vlan !~ m/^\d+$/); + + my $depth = param('depth'); + undef $depth if (defined $depth and $depth !~ m/^\d+$/); + my $device = schema('netdisco')->resultset('Device') ->search_for_device($q) or send_error('Bad device', 400); my $start = $device->ip; @@ -59,9 +72,19 @@ get '/ajax/data/device/netmap' => require_login sub { var(links => {}); my $rs = schema('netdisco')->resultset('Virtual::DeviceLinks')->search({}, { + columns => [qw/left_ip right_ip/], result_class => 'DBIx::Class::ResultClass::HashRefInflator', }); + if ($vlan) { + $rs = $rs->search({ + 'left_vlans.vlan' => $vlan, + 'right_vlans.vlan' => $vlan, + }, { + join => [qw/left_vlans right_vlans/], + }); + } + while (my $l = $rs->next) { var('links')->{ $l->{left_ip} } ||= []; push @{ var('links')->{ $l->{left_ip} } }, $l->{right_ip}; @@ -75,7 +98,8 @@ get '/ajax/data/device/netmap' => require_login sub { ); var(seen => {$start => 1}); - _add_children($tree{children}, var('links')->{$start}); + my $max = _add_children($tree{children}, var('links')->{$start}, 1, $depth); + $tree{scale} = $max; content_type('application/json'); to_json(\%tree); diff --git a/Netdisco/share/public/css/netdisco.css b/Netdisco/share/public/css/netdisco.css index 65d154d4..aa3caa7b 100644 --- a/Netdisco/share/public/css/netdisco.css +++ b/Netdisco/share/public/css/netdisco.css @@ -395,18 +395,6 @@ td > form.nd_inline-form { display: none; } -/* question mark image with popover for netmap instructions */ -#nd_netmap-help { - position: fixed; - top: 160px; - right: 7px; - z-index: 1; - color: #555; - font-size: 20px; - cursor: pointer; - display: none; -} - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* style customization for many items which appear in the sidebar */ @@ -416,6 +404,11 @@ td > form.nd_inline-form { margin-bottom: 12px; } +/* labels in simple sidebars */ +.nd_sidebar-label { + margin-left: 7px; +} + /* fixup for prepended checkbox in sidebar */ .nd_searchcheckbox { width: 121px; @@ -567,6 +560,11 @@ form .clearfix.success input { padding-top: 3px !important; } +.nd_netmap-sidebar { + margin-top: 40px; + margin-left: -9px; +} + .icons-ul { margin-left: 22px; } diff --git a/Netdisco/share/views/ajax/device/netmap.tt b/Netdisco/share/views/ajax/device/netmap.tt index 1999bfee..10a2b452 100644 --- a/Netdisco/share/views/ajax/device/netmap.tt +++ b/Netdisco/share/views/ajax/device/netmap.tt @@ -3,10 +3,6 @@ var winHeight = window.innerHeight; var winWidth = window.innerWidth; -var tree = d3.layout.tree() - .size([360, winHeight]) - .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); - // links in the initial tree drawing use this generator var treeLink = d3.svg.diagonal.radial() .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; }); @@ -60,7 +56,10 @@ function to_class(name) { return 'nd_' + name.replace(/\./g, "_") } // handler for clicking on a circle - redirect to that device's netmap function circleClick(d) { - window.location = '[% uri_for('/device') %]?tab=netmap&q=' + d.fullname; + window.location = '[% uri_for('/device') %]?tab=netmap' + + '&q=' + d.fullname + + '&depth=[% params.depth | uri %]' + + '&vlan=[% params.vlan | uri %]'; } // handler for mouseover on a circle - show that device's real neighbors @@ -92,7 +91,15 @@ $.getJSON('[% uri_for('/ajax/data/device/alldevicelinks') %]', function(data) { neighbors_data = data; // draw the tree - d3.json("[% uri_for('/ajax/data/device/netmap') %]?&q=[% params.q | uri %]", function(error, root) { + d3.json("[% uri_for('/ajax/data/device/netmap') %]?" + + '&q=[% params.q | uri %]' + + '&depth=[% params.depth | uri %]' + + '&vlan=[% params.vlan | uri %]', function(error, root) { + var tree = d3.layout.tree() + // magic number "8" for scaling (network depth). seems to make things look right for me. + .size([360, (winHeight / 8 * (root['scale'] || 0))]) + .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); + var nodes = tree.nodes(root), links = tree.links(nodes); @@ -171,4 +178,5 @@ $.getJSON('[% uri_for('/ajax/data/device/alldevicelinks') %]', function(data) { }); // jquery getJSON for all connections +// vim: ft=javascript diff --git a/Netdisco/share/views/device.tt b/Netdisco/share/views/device.tt index 3a9284ae..ff44c120 100644 --- a/Netdisco/share/views/device.tt +++ b/Netdisco/share/views/device.tt @@ -1,16 +1,5 @@ -
diff --git a/Netdisco/share/views/js/common.js b/Netdisco/share/views/js/common.js index 3048ee62..986d529e 100644 --- a/Netdisco/share/views/js/common.js +++ b/Netdisco/share/views/js/common.js @@ -95,7 +95,12 @@ $('#nd_sidebar-reset-link').attr('href', uri_base + '/device?tab=[% tab.tag %]&reset=on&' + $('#ports_form') .find('input[name="q"],input[name="f"],input[name="partial"],input[name="invert"]') - .serialize()) + .serialize()); + + [% ELSIF tab.tag == 'netmap' %] + // form reset icon on netmap tab + $('#nd_sidebar-reset-link').attr('href', uri_base + '/device?tab=[% tab.tag %]&reset=on&' + + $('#netmap_form').find('input[name="q"]').serialize()); [% END %] do_search(event, '[% tab.tag %]'); diff --git a/Netdisco/share/views/js/device.js b/Netdisco/share/views/js/device.js index e8439baa..072065c5 100644 --- a/Netdisco/share/views/js/device.js +++ b/Netdisco/share/views/js/device.js @@ -19,14 +19,6 @@ // changes, and only reset when they submit or cancel the change var dirty = false; - // show or hide netmap help button - if (tab == 'netmap') { - $('#nd_netmap-help').show(); - } - else { - $('#nd_netmap-help').hide(); - } - // activate modals, tooltips and popovers $('.nd_modal').modal({show: false}); $("[rel=tooltip]").tooltip({live: true}); diff --git a/Netdisco/share/views/sidebar/device/netmap.tt b/Netdisco/share/views/sidebar/device/netmap.tt new file mode 100644 index 00000000..46f14d86 --- /dev/null +++ b/Netdisco/share/views/sidebar/device/netmap.tt @@ -0,0 +1,24 @@ + + Neighbor Map Controls + +
+
    +
  • Click and drag to pan
  • +
  • Scroll to zoom
  • +
  • Hover shows neighbors
  • +
  • Click to center device
  • +
+ + Draw to Depth:
+ + + Filter by VLAN:
+ +
+ +