Files
netdisco/Netdisco/lib/Netdisco/Web/Search.pm
2012-04-09 22:55:10 +01:00

262 lines
8.1 KiB
Perl

package Netdisco::Web::Search;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use NetAddr::IP::Lite ':lower';
use Net::MAC ();
use List::MoreUtils ();
use Net::DNS ();
hook 'before' => sub {
# view settings for node options
var('node_options' => [
{ name => 'stamps', label => 'Time Stamps', default => 'on' },
]);
# view settings for device options
var('device_options' => [
{ name => 'matchall', label => 'Match All Options', default => 'on' },
]);
# new searches will use these defaults in their sidebars
var('search_node' => uri_for('/search', {tab => 'node'}));
var('search_device' => uri_for('/search', {tab => 'device'}));
foreach my $col (@{ var('node_options') }) {
next unless $col->{default} eq 'on';
var('search_node')->query_param($col->{name}, 'checked');
}
foreach my $col (@{ var('device_options') }) {
next unless $col->{default} eq 'on';
var('search_device')->query_param($col->{name}, 'checked');
}
if (request->path eq uri_for('/search')->path
or index(request->path, uri_for('/ajax/content/search')->path) == 0) {
foreach my $col (@{ var('node_options') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'node';
}
foreach my $col (@{ var('device_options') }) {
next unless $col->{default} eq 'on';
params->{$col->{name}} = 'checked'
if not param('tab') or param('tab') ne 'device';
}
# used in the device search sidebar template to set selected items
foreach my $opt (qw/model vendor os_ver/) {
my $p = (ref [] eq ref param($opt) ? param($opt)
: (param($opt) ? [param($opt)] : []));
var("${opt}_lkp" => { map { $_ => 1 } @$p });
}
}
};
# device with various properties or a default match-all
ajax '/ajax/content/search/device' => sub {
my $has_opt = List::MoreUtils::any {param($_)}
qw/name location dns ip description model os_ver vendor/;
my $set;
if ($has_opt) {
$set = schema('netdisco')->resultset('Device')->search_by_field(scalar params);
}
else {
my $q = param('q');
return unless $q;
$set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/device.tt', {
results => $set,
}, { layout => undef };
};
# 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;
content_type('text/html');
my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0);
my @active = (param('archived') ? () : (-bool => 'active'));
if (! $mac->get_error) {
my $sightings = schema('netdisco')->resultset('Node')
->search_by_mac({mac => $mac->as_IEEE, @active});
my $ips = schema('netdisco')->resultset('NodeIp')
->search_by_mac({mac => $mac->as_IEEE, @active});
my $ports = schema('netdisco')->resultset('DevicePort')
->search({mac => $mac->as_IEEE});
return unless $sightings->count
or $ips->count
or $ports->count;
template 'ajax/search/node_by_mac.tt', {
ips => $ips,
sightings => $sightings,
ports => $ports,
}, { layout => undef };
}
else {
my $set;
if (my $ip = NetAddr::IP::Lite->new($node)) {
# search_by_ip() will extract cidr notation if necessary
$set = schema('netdisco')->resultset('NodeIp')
->search_by_ip({ip => $ip, @active});
}
else {
if (schema('netdisco')->resultset('NodeIp')->has_dns_col) {
if (param('partial')) {
$node = "\%$node\%";
}
elsif (setting('domain_suffix')) {
$node .= setting('domain_suffix')
if index($node, setting('domain_suffix')) == -1;
}
$set = schema('netdisco')->resultset('NodeIp')
->search_by_dns({dns => $node, @active});
}
elsif (setting('domain_suffix')) {
$node .= setting('domain_suffix')
if index($node, setting('domain_suffix')) == -1;
my $q = Net::DNS::Resolver->new->query($node);
if ($q) {
foreach my $rr ($q->answer) {
next unless $rr->type eq 'A';
$node = $rr->address;
}
}
else {
return;
}
$set = schema('netdisco')->resultset('NodeIp')
->search_by_ip({ip => $node, @active});
}
}
return unless $set and $set->count;
template 'ajax/search/node_by_ip.tt', {
macs => $set,
archive_filter => {@active},
}, { layout => undef };
}
};
# devices carrying vlan xxx
ajax '/ajax/content/search/vlan' => sub {
my $q = param('q');
return unless $q;
my $set;
if ($q =~ m/^\d+$/) {
$set = schema('netdisco')->resultset('Device')->carrying_vlan({vlan => $q});
}
else {
$set = schema('netdisco')->resultset('Device')->carrying_vlan_name({name => $q});
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/vlan.tt', {
results => $set,
}, { layout => undef };
};
# device ports with a description (er, name) matching
ajax '/ajax/content/search/port' => sub {
my $q = param('q');
return unless $q;
my $set;
if ($q =~ m/^\d+$/) {
$set = schema('netdisco')->resultset('DevicePort')->search({vlan => $q});
}
else {
$set = schema('netdisco')->resultset('DevicePort')->search({name => $q});
}
return unless $set->count;
content_type('text/html');
template 'ajax/search/port.tt', {
results => $set,
}, { layout => undef };
};
get '/search' => sub {
my $q = param('q');
if (not param('tab')) {
if (not $q) {
redirect uri_for('/');
}
# pick most likely tab for initial results
if ($q =~ m/^\d+$/) {
params->{'tab'} = 'vlan';
}
else {
my $s = schema('netdisco');
if ($q =~ m{^[a-f0-9.:/]+$}i) {
my $ip = NetAddr::IP::Lite->new($q);
if ($ip and $s->resultset('Device')->search_by_field({ip => $q})->count) {
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('DevicePort')
->search({name => "\%$q\%"})->count) {
params->{'tab'} = 'port';
}
}
params->{'tab'} ||= 'node';
}
}
# used in the device search sidebar to populate select inputs
var('model_list' => [
schema('netdisco')->resultset('Device')->get_distinct('model')
]);
var('os_ver_list' => [
schema('netdisco')->resultset('Device')->get_distinct('os_ver')
]);
var('vendor_list' => [
schema('netdisco')->resultset('Device')->get_distinct('vendor')
]);
# list of tabs
var('tabs' => [
{ id => 'device', label => 'Device' },
{ id => 'node', label => 'Node' },
{ id => 'vlan', label => 'VLAN' },
{ id => 'port', label => 'Port' },
]);
var('node_ip_has_dns_col' =>
schema('netdisco')->resultset('NodeIp')->has_dns_col);
template 'search';
};
true;