Device Neighbor Map can have max depth and VLAN filter
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
@@ -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(<<ENDSQL
|
||||
SELECT dp.ip AS left_ip, di.ip AS right_ip
|
||||
FROM ( SELECT device_port.ip, device_port.remote_ip
|
||||
SELECT dp.ip AS left_ip, dp.port AS left_port, di.ip AS right_ip, dp.remote_port AS right_port
|
||||
FROM ( SELECT device_port.ip, device_port.port, device_port.remote_ip, device_port.remote_port
|
||||
FROM device_port
|
||||
WHERE device_port.remote_port IS NOT NULL
|
||||
GROUP BY device_port.ip, device_port.remote_ip
|
||||
GROUP BY device_port.ip, device_port.port, device_port.remote_ip, device_port.remote_port
|
||||
ORDER BY device_port.ip) dp
|
||||
LEFT JOIN device_ip di ON dp.remote_ip = di.alias
|
||||
WHERE di.ip IS NOT NULL
|
||||
@@ -26,9 +26,23 @@ __PACKAGE__->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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
<i class="nd_sidebar-toggle icon-wrench icon-large" id="nd_sidebar-toggle-img-out"
|
||||
rel="tooltip" data-placement="left" data-offset="5" data-title="Show Sidebar"></i>
|
||||
<i class="icon-question-sign icon-large" id="nd_netmap-help" rel="popover"
|
||||
data-title="Neighbor Map Controls"
|
||||
data-html="true"
|
||||
data-content="
|
||||
<ul>
|
||||
<li>Click and drag to pan</li>
|
||||
<li>Mouse-wheel scroll to zoom</li>
|
||||
<li>Hover to hightlight neighbors</li>
|
||||
<li>Click to view device's map</li>
|
||||
</ul>"
|
||||
data-placement='left' data-trigger='click'></i>
|
||||
<div class="container-fluid">
|
||||
<div class="nd_sidebar nd_sidebar-pinned">
|
||||
<div class="well">
|
||||
|
||||
@@ -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 %]');
|
||||
|
||||
@@ -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});
|
||||
|
||||
24
Netdisco/share/views/sidebar/device/netmap.tt
Normal file
24
Netdisco/share/views/sidebar/device/netmap.tt
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
<span class="nd_sidebar-title"><em>Neighbor Map Controls</em></span>
|
||||
<input name="q" value="[% params.q | html_entity %]" type="hidden"/>
|
||||
<div class="clearfix nd_netmap-sidebar">
|
||||
<ul class="muted">
|
||||
<li>Click and drag to pan</li>
|
||||
<li>Scroll to zoom</li>
|
||||
<li>Hover shows neighbors</li>
|
||||
<li>Click to center device</li>
|
||||
</ul>
|
||||
|
||||
<em class="muted nd_sidebar-label">Draw to Depth:</em><br/>
|
||||
<input id="nd_port-query" placeholder="" type="number"
|
||||
name="depth" value="[% params.depth | html_entity %]" type="text"
|
||||
rel="tooltip" data-placement="left" data-offset="5" data-title="Max Depth"/>
|
||||
|
||||
<em class="muted nd_sidebar-label">Filter by VLAN:</em><br/>
|
||||
<input id="nd_port-query" placeholder="" type="number"
|
||||
name="vlan" value="[% params.vlan | html_entity %]" type="text"
|
||||
rel="tooltip" data-placement="left" data-offset="5" data-title="VLAN"/>
|
||||
</div>
|
||||
<button id="[% tab.id %]_submit" type="submit" class="btn btn-info">
|
||||
<i class="icon-pencil icon-large pull-left nd_navbar-icon"></i> Redraw Map</button>
|
||||
|
||||
Reference in New Issue
Block a user