implement smart search tab selection, and Node search

This commit is contained in:
Oliver Gorwits
2012-01-08 01:10:37 +00:00
parent 54d6422894
commit fc672f0524
10 changed files with 206 additions and 16 deletions

View File

@@ -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
# 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;

View File

@@ -39,6 +39,49 @@ __PACKAGE__->set_primary_key("mac", "ip");
# Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02
# 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;

View File

@@ -11,10 +11,10 @@ sub carrying_vlan {
'port_vlans.vlan' => $vlan,
},
{
join => [qw/ port_vlans vlans /],
prefetch => 'vlans',
order_by => [qw/ me.dns me.ip /],
columns => [qw/ me.ip me.dns me.model me.os me.vendor /],
join => 'port_vlans',
prefetch => 'vlans',
},
);
}

View 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;

View File

@@ -22,6 +22,21 @@ ajax '/ajax/content/search/:thing' => sub {
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
ajax '/ajax/content/search/vlan' => sub {
my $vlan = param('q');
@@ -48,6 +63,12 @@ ajax '/ajax/content/search/vlan' => 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');
if ($q and not param('tab')) {
# pick most likely tab for initial results
@@ -55,20 +76,42 @@ get '/search' => sub {
params->{'tab'} = 'vlan';
}
else {
my $s = schema('netdisco');
if ($q =~ m/^[a-f0-9.:]+$/i) {
if ($s->resultset('Device')->find($q)) {
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) {
redirect '/';
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
var('tabs' => [
{ id => 'device', label => 'Device' },

View File

@@ -3,6 +3,14 @@ body {
padding-top: 50px;
}
.well {
margin-right: 15px;
}
.nd_content {
margin-left: 225px !important;
}
.nd_loginalert {
margin-top: -40px;
}
@@ -34,13 +42,23 @@ body {
vertical-align: top;
}
.nd_legendlabel {
float: right;
line-height: 1.2;
margin-top: 2px;
margin-right: 3px;
}
.nd_search {
margin-top: 15px;
}
.nd_linkcell {
.nd_stealthlink {
text-decoration: none !important;
color: #404040;
}
.nd_linkcell {
display: block;
padding: 0px;
height: 100%;

View 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 &rarr; 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>&nbsp;</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>&nbsp;</td>
<td>MAC &rarr; 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>

View File

@@ -14,7 +14,7 @@
[% FOREACH method IN col.key.split('\.') %]
[% SET val = val.$method %]
[% 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 %]
</tr>
[% END %]

View File

@@ -1,6 +1,5 @@
<p><em>Node Search Options</em></p>
<input name="tab" value="node" type="hidden"/>
<div class="clearfix input-prepend">
<label class="add-on">
<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 %]/>
</label>
<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>
</div>

View File

@@ -24,7 +24,7 @@
</div>
</div>
<div class="content">
<div class="nd_content content">
<ul id="search_results" class="tabs" data-tabs="on">
[% FOREACH tab IN vars.tabs %]
<li[% ' class="active"' IF params.tab == tab.id %]><a href="#[% tab.id %]_pane">[% tab.label %]</a></li>