diff --git a/lib/App/Netdisco/DB/ResultSet/NodeIp.pm b/lib/App/Netdisco/DB/ResultSet/NodeIp.pm index 1a54e88b..e08417b5 100644 --- a/lib/App/Netdisco/DB/ResultSet/NodeIp.pm +++ b/lib/App/Netdisco/DB/ResultSet/NodeIp.pm @@ -122,8 +122,9 @@ to search for. The value may optionally include SQL wildcard characters. =item * -The C parameter may optionally have a C parameter which is a -regular expression of domain names - one of which must match the results. +If C is a plain string, then the C parameter may optionally have a +C parameter which is a regular expression of domain names - one of +which must match the results. =item * @@ -150,14 +151,37 @@ sub search_by_dns { die "dns field required for search_by_dns\n" if ref {} ne ref $cond or !exists $cond->{dns}; - (my $suffix = (delete $cond->{suffix} || '')) + my $dns_field = delete $cond->{dns}; + + (my $suffix = ($cond->{suffix} || '')) =~ s|\Q(?^\E[-xismu]*|(?|g; - $cond->{dns} = [ -and => - { '-ilike' => delete $cond->{dns} }, - { '~*' => "***:$suffix" }, - ]; + if (q{} eq ref $dns_field and exists $cond->{suffix}) { + (my $stripped_dns_field = $dns_field) =~ s/\.\%$//; + (my $fqdn_field = $stripped_dns_field) .= '%'; + $stripped_dns_field =~ s/$cond->{suffix}$// if $cond->{suffix}; + $stripped_dns_field .= '.%'; + $cond->{dns} = [ -or => + [ -and => + { '-ilike' => $stripped_dns_field }, + { '~*' => "***:$suffix" }, + ], + [ -and => + { '-ilike' => $dns_field }, + { '~*' => "***:$suffix" }, + ], + { '-ilike' => $fqdn_field }, + ]; + } + elsif (q{} ne ref $dns_field) { + $cond->{dns} = $dns_field; + } + else { + $cond->{dns} = { '-ilike' => $dns_field }; + } + + delete $cond->{suffix}; return $rs ->search_rs({}, $order_by_time_last_and_join_oui) ->search($cond, $attrs); diff --git a/lib/App/Netdisco/Web/Plugin/Search/Node.pm b/lib/App/Netdisco/Web/Plugin/Search/Node.pm index f548d120..30014fd0 100644 --- a/lib/App/Netdisco/Web/Plugin/Search/Node.pm +++ b/lib/App/Netdisco/Web/Plugin/Search/Node.pm @@ -10,6 +10,7 @@ use NetAddr::MAC (); use POSIX qw/strftime/; use App::Netdisco::Web::Plugin; +use App::Netdisco::Util::DNS 'ipv4_from_hostname'; use App::Netdisco::Util::Web 'sql_match'; register_search_tab({ @@ -208,42 +209,57 @@ get '/ajax/content/search/node' => require_login sub { } } + my $have_rows = 0; my $set = schema(vars->{'tenant'})->resultset('NodeNbt') ->search_by_name({nbname => $likeval, @active, @times}); + ++$have_rows if $set->has_rows; - unless ( $set->has_rows ) { + unless ( $have_rows ) { if ($node =~ m{^(?:$RE{net}{IPv4}|$RE{net}{IPv6})(?:/\d+)?$}i and my $ip = NetAddr::IP::Lite->new($node)) { # search_by_ip() will extract cidr notation if necessary $set = schema(vars->{'tenant'})->resultset('NodeIp') ->search_by_ip({ip => $ip, @active, @times}); + ++$have_rows if $set->has_rows; } else { $set = schema(vars->{'tenant'})->resultset('NodeIp') ->search_by_dns({ ($using_wildcards ? (dns => $likeval) : - (dns => "${likeval}.\%", - suffix => setting('domain_suffix'))), + (dns => "${likeval}.\%", suffix => setting('domain_suffix'))), @active, @times, }); + ++$have_rows if $set->has_rows; + + # try DNS lookup as fallback + if (not $using_wildcards and not $have_rows) { + my $resolved_ip = ipv4_from_hostname($node); + + if ($resolved_ip) { + $set = schema(vars->{'tenant'})->resultset('NodeIp') + ->search_by_ip({ip => $resolved_ip, @active, @times}); + ++$have_rows if $set->has_rows; + } + } # if the user selects Vendor search opt, then # we'll try the OUI company name as a fallback - if (param('show_vendor') and not $set->has_rows) { + if (param('show_vendor') and not $have_rows) { $set = schema(vars->{'tenant'})->resultset('NodeIp') ->with_times ->search( {'oui.company' => { -ilike => ''.sql_match($node)}, @times}, {'prefetch' => 'oui'}, ); + ++$have_rows if $set->has_rows; } } } - return unless $set and $set->has_rows; + return unless $set and ($have_rows or $set->has_rows); $set = $set->search_rs({}, { order_by => 'me.mac' }); return template 'ajax/search/node_by_ip.tt', {