implement smart search tab selection, and Node search
This commit is contained in:
@@ -48,6 +48,11 @@ __PACKAGE__->set_primary_key("mac", "switch", "port");
|
|||||||
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
|
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:sGGyKEfUkoIFVtmj1wnH7A
|
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:sGGyKEfUkoIFVtmj1wnH7A
|
||||||
|
|
||||||
|
__PACKAGE__->belongs_to( device => 'Netdisco::DB::Result::Device',
|
||||||
|
{ 'foreign.ip' => 'self.switch' } );
|
||||||
|
__PACKAGE__->belongs_to( device_port => 'Netdisco::DB::Result::DevicePort',
|
||||||
|
{ 'foreign.ip' => 'self.switch', 'foreign.port' => 'self.port' } );
|
||||||
|
__PACKAGE__->has_many( ips => 'Netdisco::DB::Result::NodeIp',
|
||||||
|
{ 'foreign.mac' => 'self.mac' } );
|
||||||
|
|
||||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -39,6 +39,49 @@ __PACKAGE__->set_primary_key("mac", "ip");
|
|||||||
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
|
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9+CuvuVWH88WxAf6IBij8g
|
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9+CuvuVWH88WxAf6IBij8g
|
||||||
|
|
||||||
|
__PACKAGE__->has_many( nodeips => 'Netdisco::DB::Result::NodeIp',
|
||||||
|
{ 'foreign.mac' => 'self.mac' } );
|
||||||
|
__PACKAGE__->has_many( nodes => 'Netdisco::DB::Result::Node',
|
||||||
|
{ 'foreign.mac' => 'self.mac' } );
|
||||||
|
|
||||||
|
sub tidy_nodeips {
|
||||||
|
my ($row, $archive) = @_;
|
||||||
|
|
||||||
|
return $row->nodeips(
|
||||||
|
{
|
||||||
|
ip => { '!=' => $row->ip },
|
||||||
|
($archive ? () : (active => 1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order_by => {'-desc' => 'time_last'},
|
||||||
|
columns => [qw/ mac ip dns active /],
|
||||||
|
'+select' => [
|
||||||
|
\"to_char(time_first, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
\"to_char(time_last, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
],
|
||||||
|
'+as' => [qw/ time_first time_last /],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub tidy_nodes {
|
||||||
|
my ($row, $archive) = @_;
|
||||||
|
|
||||||
|
return $row->nodes(
|
||||||
|
{
|
||||||
|
($archive ? () : (active => 1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order_by => {'-desc' => 'time_last'},
|
||||||
|
columns => [qw/ mac switch port oui active device.dns /],
|
||||||
|
'+select' => [
|
||||||
|
\"to_char(time_first, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
\"to_char(time_last, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
],
|
||||||
|
'+as' => [qw/ time_first time_last /],
|
||||||
|
join => 'device',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ sub carrying_vlan {
|
|||||||
'port_vlans.vlan' => $vlan,
|
'port_vlans.vlan' => $vlan,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
join => [qw/ port_vlans vlans /],
|
|
||||||
prefetch => 'vlans',
|
|
||||||
order_by => [qw/ me.dns me.ip /],
|
order_by => [qw/ me.dns me.ip /],
|
||||||
columns => [qw/ me.ip me.dns me.model me.os me.vendor /],
|
columns => [qw/ me.ip me.dns me.model me.os me.vendor /],
|
||||||
|
join => 'port_vlans',
|
||||||
|
prefetch => 'vlans',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
25
Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm
Normal file
25
Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package Netdisco::DB::ResultSet::NodeIp;
|
||||||
|
use base 'DBIx::Class::ResultSet';
|
||||||
|
|
||||||
|
sub by_ip {
|
||||||
|
my ($set, $ip, $archive) = @_;
|
||||||
|
return $set unless $ip;
|
||||||
|
|
||||||
|
return $set->search(
|
||||||
|
{
|
||||||
|
ip => $ip,
|
||||||
|
($archive ? () : (active => 1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order_by => {'-desc' => 'time_last'},
|
||||||
|
columns => [qw/ mac ip dns active /],
|
||||||
|
'+select' => [
|
||||||
|
\"to_char(time_first, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
\"to_char(time_last, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
],
|
||||||
|
'+as' => [qw/ time_first time_last /],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -22,6 +22,21 @@ ajax '/ajax/content/search/:thing' => sub {
|
|||||||
return '<p>Hello '. param('thing') .'.</p>';
|
return '<p>Hello '. param('thing') .'.</p>';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# nodes matching the param as an IP or DNS hostname or MAC
|
||||||
|
ajax '/ajax/content/search/node' => sub {
|
||||||
|
my $node = param('q');
|
||||||
|
return unless $node;
|
||||||
|
|
||||||
|
my $set = schema('netdisco')->resultset('NodeIp')
|
||||||
|
->by_ip($node, param('archived'));
|
||||||
|
return unless $set->count;
|
||||||
|
|
||||||
|
content_type('text/html');
|
||||||
|
template 'content/node.tt', {
|
||||||
|
results => $set,
|
||||||
|
}, { layout => undef };
|
||||||
|
};
|
||||||
|
|
||||||
# devices carrying vlan xxx
|
# devices carrying vlan xxx
|
||||||
ajax '/ajax/content/search/vlan' => sub {
|
ajax '/ajax/content/search/vlan' => sub {
|
||||||
my $vlan = param('q');
|
my $vlan = param('q');
|
||||||
@@ -48,6 +63,12 @@ ajax '/ajax/content/search/vlan' => sub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
get '/search' => sub {
|
get '/search' => sub {
|
||||||
|
# set up default search options for each type
|
||||||
|
if (not param('tab') or param('tab') ne 'node') {
|
||||||
|
params->{'stamps'} = 'checked';
|
||||||
|
params->{'vendor'} = 'checked';
|
||||||
|
}
|
||||||
|
|
||||||
my $q = param('q');
|
my $q = param('q');
|
||||||
if ($q and not param('tab')) {
|
if ($q and not param('tab')) {
|
||||||
# pick most likely tab for initial results
|
# pick most likely tab for initial results
|
||||||
@@ -55,20 +76,42 @@ get '/search' => sub {
|
|||||||
params->{'tab'} = 'vlan';
|
params->{'tab'} = 'vlan';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
my $s = schema('netdisco');
|
||||||
|
if ($q =~ m/^[a-f0-9.:]+$/i) {
|
||||||
|
if ($s->resultset('Device')->find($q)) {
|
||||||
params->{'tab'} = 'device';
|
params->{'tab'} = 'device';
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
# this will match for MAC addresses
|
||||||
|
# and partial IPs (subnets?)
|
||||||
|
params->{'tab'} = 'node';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($s->resultset('Device')->search({
|
||||||
|
dns => { '-ilike' => "\%$q\%" },
|
||||||
|
})->count) {
|
||||||
|
params->{'tab'} = 'device';
|
||||||
|
}
|
||||||
|
elsif ($s->resultset('NodeIp')->search({
|
||||||
|
dns => { '-ilike' => "\%$q\%" },
|
||||||
|
})->count) {
|
||||||
|
params->{'tab'} = 'node';
|
||||||
|
}
|
||||||
|
elsif ($s->resultset('DevicePort')->search({
|
||||||
|
name => { '-ilike' => "\%$q\%" },
|
||||||
|
})->count) {
|
||||||
|
params->{'tab'} = 'port';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params->{'tab'} ||= 'device';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elsif (not $q) {
|
elsif (not $q) {
|
||||||
redirect '/';
|
redirect '/';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
# set up default search options for each type
|
|
||||||
if (param('tab') and param('tab') ne 'node') {
|
|
||||||
params->{'stamps'} = 'checked';
|
|
||||||
params->{'vendor'} = 'checked';
|
|
||||||
}
|
|
||||||
|
|
||||||
# list of tabs
|
# list of tabs
|
||||||
var('tabs' => [
|
var('tabs' => [
|
||||||
{ id => 'device', label => 'Device' },
|
{ id => 'device', label => 'Device' },
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ body {
|
|||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.well {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nd_content {
|
||||||
|
margin-left: 225px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.nd_loginalert {
|
.nd_loginalert {
|
||||||
margin-top: -40px;
|
margin-top: -40px;
|
||||||
}
|
}
|
||||||
@@ -34,13 +42,23 @@ body {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nd_legendlabel {
|
||||||
|
float: right;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.nd_search {
|
.nd_search {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nd_linkcell {
|
.nd_stealthlink {
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
color: #404040;
|
color: #404040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nd_linkcell {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
57
Netdisco/views/content/node.tt
Normal file
57
Netdisco/views/content/node.tt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<table class="bordered-table condensed-table zebra-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>MAC</th>
|
||||||
|
<th>Match</th>
|
||||||
|
<th>Device or Node</th>
|
||||||
|
[% IF params.stamps %]
|
||||||
|
<th>First Seen</th>
|
||||||
|
<th>Last Seen</th>
|
||||||
|
[% END %]
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</tbody>
|
||||||
|
[% WHILE (row = results.next) %]
|
||||||
|
<tr>
|
||||||
|
<td><a class="nd_linkcell" href="/search?tab=node&stamps=on&vendor=on&q=[% row.mac | uri %]">[% row.mac %]</a></td>
|
||||||
|
<td>IP → MAC</td>
|
||||||
|
<td><a href="/search?tab=node&stamps=on&vendor=on&q=[% row.ip | uri %]">[% row.ip %]</a>
|
||||||
|
[% ' (' _ row.dns _ ')' IF row.dns %]
|
||||||
|
[% ' <span class="label warning">a</span>' IF NOT row.active %]
|
||||||
|
</td>
|
||||||
|
[% IF params.stamps %]
|
||||||
|
<td>[% row.time_first %]</td>
|
||||||
|
<td>[% row.time_last %]</td>
|
||||||
|
[% END %]
|
||||||
|
</tr>
|
||||||
|
[% FOREACH node IN row.tidy_nodes(params.archived) %]
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>Switch Port</td>
|
||||||
|
<td><a href="/device?q=[% node.switch | url %]&port=[% node.port | url %]">[% node.switch %] [ [% node.port %] ]</a>
|
||||||
|
[% ' (' _ node.device.dns _ ')' IF node.device.dns %]
|
||||||
|
[% ' <span class="label warning">a</span>' IF NOT node.active %]
|
||||||
|
</td>
|
||||||
|
[% IF params.stamps %]
|
||||||
|
<td>[% node.time_first %]</td>
|
||||||
|
<td>[% node.time_last %]</td>
|
||||||
|
[% END %]
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
[% FOREACH nodeip IN row.tidy_nodeips(params.archived) %]
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>MAC → IP</td>
|
||||||
|
<td><a href="/search?tab=node&stamps=on&vendor=on&q=[% nodeip.ip | uri %]">[% nodeip.ip %]</a>
|
||||||
|
[% ' (' _ nodeip.dns _ ')' IF nodeip.dns %]
|
||||||
|
[% ' <span class="label warning">a</span>' IF NOT nodeip.active %]
|
||||||
|
</td>
|
||||||
|
[% IF params.stamps %]
|
||||||
|
<td>[% nodeip.time_first %]</td>
|
||||||
|
<td>[% nodeip.time_last %]</td>
|
||||||
|
[% END %]
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
[% FOREACH method IN col.key.split('\.') %]
|
[% FOREACH method IN col.key.split('\.') %]
|
||||||
[% SET val = val.$method %]
|
[% SET val = val.$method %]
|
||||||
[% END %]
|
[% END %]
|
||||||
<td><a class="nd_linkcell" href="[% hyperlink(row) %]">[% val %]</a></td>
|
<td><a class="nd_linkcell nd_stealthlink" href="[% hyperlink(row) %]">[% val %]</a></td>
|
||||||
[% END %]
|
[% END %]
|
||||||
</tr>
|
</tr>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
<p><em>Node Search Options</em></p>
|
<p><em>Node Search Options</em></p>
|
||||||
<input name="tab" value="node" type="hidden"/>
|
|
||||||
<div class="clearfix input-prepend">
|
<div class="clearfix input-prepend">
|
||||||
<label class="add-on">
|
<label class="add-on">
|
||||||
<input type="checkbox" id="stamps" name="stamps"[% ' checked="checked"' IF params.stamps %]/>
|
<input type="checkbox" id="stamps" name="stamps"[% ' checked="checked"' IF params.stamps %]/>
|
||||||
@@ -22,6 +21,6 @@
|
|||||||
<input type="checkbox" id="archived" name="archived"[% ' checked="checked"' IF params.archived %]/>
|
<input type="checkbox" id="archived" name="archived"[% ' checked="checked"' IF params.archived %]/>
|
||||||
</label>
|
</label>
|
||||||
<label for="archived">
|
<label for="archived">
|
||||||
<span class="nd_searchcheckbox uneditable-input">Archived Data</span>
|
<span class="nd_searchcheckbox uneditable-input">Archived Data <span class="nd_legendlabel label warning">a</span></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="nd_content content">
|
||||||
<ul id="search_results" class="tabs" data-tabs="on">
|
<ul id="search_results" class="tabs" data-tabs="on">
|
||||||
[% FOREACH tab IN vars.tabs %]
|
[% FOREACH tab IN vars.tabs %]
|
||||||
<li[% ' class="active"' IF params.tab == tab.id %]><a href="#[% tab.id %]_pane">[% tab.label %]</a></li>
|
<li[% ' class="active"' IF params.tab == tab.id %]><a href="#[% tab.id %]_pane">[% tab.label %]</a></li>
|
||||||
|
|||||||
Reference in New Issue
Block a user