diff --git a/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm b/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm index e0716cec..19fa41dd 100644 --- a/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm +++ b/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm @@ -43,6 +43,54 @@ sub with_times { }); } +=head2 search_aliases( $name or $ip or $prefix ) + +Tries to find devices in Netdisco which have an identity corresponding to +C<$name>, C<$ip> or C<$prefix>. The name can be partial, in which case an +automatic case-insensitive partial-match search is performed. + +The search is across all aliases of the device, as well as its "root IP" +identity. Note that this search will try B to use DNS, in case the current +name for an IP does not correspond to the data within Netdisco. + +=cut + +sub search_aliases { + my ($rs, $q) = @_; + $q ||= '255.255.255.255'; # hack to return empty resultset on error + + # rough approximation of IP addresses (v4 in v6 not supported). + # this helps us avoid triggering any DNS. + my $by_ip = ($q =~ m{^(?:[.0-9/]+|[:0-9a-f/]+)$}i) ? 1 : 0; + + my $clause; + if ($by_ip) { + my $ip = NetAddr::IP::Lite->new($q); + $clause = [ + 'me.ip' => { '<<=' => $ip->cidr }, + 'device_ips.alias' => { '<<=' => $ip->cidr }, + ]; + } + else { + $q = "\%$q\%" if $q !~ m/\%/; + $clause = [ + 'me.ip::text' => { '-ilike' => $q }, + 'device_ips.alias::text' => { '-ilike' => $q }, + ]; + } + + return $rs->search( + { + -or => $clause, + }, + { + order_by => [qw/ me.dns me.ip /], + join => 'device_ips', + distinct => 1, + } + ); +} + =head2 search_by_field( \%cond, \%attrs? ) This variant of the standard C method returns a ResultSet of Device diff --git a/Netdisco/lib/App/Netdisco/Web/Search.pm b/Netdisco/lib/App/Netdisco/Web/Search.pm index 61e83f66..2ec3dea9 100644 --- a/Netdisco/lib/App/Netdisco/Web/Search.pm +++ b/Netdisco/lib/App/Netdisco/Web/Search.pm @@ -63,57 +63,42 @@ hook 'before_template' => sub { get '/search' => sub { my $q = param('q'); + my $s = schema('netdisco'); + if (not param('tab')) { - if (not $q) { - redirect uri_for('/'); - } + 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); - my $nd = $s->resultset('Device')->search_by_field({ip => $q}); - if ($ip and $nd->count) { - if ($nd->count == 1) { - # redirect to device details for the one device - redirect uri_for('/device', - {tab => 'details', q => $q, f => ''}); - } - params->{'tab'} = 'device'; - } - else { - # this will match for MAC addresses - # and partial IPs (subnets?) - params->{'tab'} = 'node'; + my $nd = $s->resultset('Device')->search_aliases($q); + + if ($nd and $nd->count) { + if ($nd->count == 1) { + # redirect to device details for the one device + redirect uri_for('/device', + {tab => 'details', q => $q, f => ''}); } + + # multiple devices + params->{'tab'} = 'device'; } - else { - my $nd = $s->resultset('Device')->search({dns => { '-ilike' => "\%$q\%" }}); - if ($nd->count) { - if ($nd->count == 1) { - # redirect to device details for the one device - redirect uri_for('/device', - {tab => 'details', q => $nd->first->dns, f => ''}); - } - params->{'tab'} = 'device'; - } - elsif ($s->resultset('DevicePort') - ->search({name => "\%$q\%"})->count) { - params->{'tab'} = 'port'; - } + elsif ($s->resultset('DevicePort') + ->search({name => "\%$q\%"})->count) { + params->{'tab'} = 'port'; } - params->{'tab'} ||= 'node'; } + + # if all else fails + params->{'tab'} ||= 'node'; } # used in the device search sidebar to populate select inputs - my $model_list = [ schema('netdisco')->resultset('Device')->get_distinct_col('model') ]; - my $os_ver_list = [ schema('netdisco')->resultset('Device')->get_distinct_col('os_ver') ]; - my $vendor_list = [ schema('netdisco')->resultset('Device')->get_distinct_col('vendor') ]; + my $model_list = [ $s->resultset('Device')->get_distinct_col('model') ]; + my $os_ver_list = [ $s->resultset('Device')->get_distinct_col('os_ver') ]; + my $vendor_list = [ $s->resultset('Device')->get_distinct_col('vendor') ]; template 'search', { model_list => $model_list,