diff --git a/lib/Netdot/Model.pm b/lib/Netdot/Model.pm index 009c86e58..1b98b9b18 100644 --- a/lib/Netdot/Model.pm +++ b/lib/Netdot/Model.pm @@ -6,6 +6,7 @@ use Time::Local; use Net::DNS; use Digest::MD5 qw(md5_hex); use Scalar::Util qw(blessed); +use Class::DBI::AbstractSearch; =head1 NAME diff --git a/lib/Netdot/Model/Zone.pm b/lib/Netdot/Model/Zone.pm index 353bdcd53..657b356cf 100644 --- a/lib/Netdot/Model/Zone.pm +++ b/lib/Netdot/Model/Zone.pm @@ -24,41 +24,43 @@ DNS Zone Class We override the base method to add functionality: - - Return the most specific domain name. - - If given: + - Return the most specific domain name. + - Perform case-insensitive searches for the name of the zone. + + If given: - name=>dns.cs.local.domain + name => dns.cs.local.domain - we will look for a Zone object in this order: + we will look for a Zone object in this order: dns.cs.local.domain (not found) cs.local.domain (not found) local.domain (found) + return 'local.domain' - Search the ZoneAlias table in addition to the Zone table. - Arguments: - Hash with key/value pairs - Returns: - See Class::DBI search - Examples: - Zone->search(name=>'some.domain.name') + Arguments: + Hash with key/value pairs + Returns: + See Class::DBI search + Examples: + my $zone_obj = Zone->search(name => 'some.domain.name'); =cut sub search { my ($class, @args) = @_; $class->isa_class_method('search'); - + @args = %{ $args[0] } if ref $args[0] eq "HASH"; - my $opts = @args % 2 ? pop @args : {}; + my $opts = @args % 2 ? pop @args : {}; my %argv = @args; - if ( exists $argv{id} && $argv{id} =~ /\D+/ ){ - # No use searching for non-digits in id field - $argv{id} = 0; + if (exists $argv{id} && $argv{id} =~ /\D+/) { + # No use searching for non-digits in id field + $argv{id} = 0; } my (@result, $result); @@ -69,42 +71,48 @@ sub search { } if (@result || $result) { - return wantarray ? @result : $result; - }elsif ( defined $argv{name} ){ - if ( my $alias = ZoneAlias->search(name=>$argv{name})->first ) { - return $class->SUPER::search(id => $alias->zone->id, $opts); - }elsif ( $argv{name} =~ /\./ && !Ipblock->matches_v4($argv{name}) ){ - my @sections = split '\.', $argv{name}; - - # first try to search for the RFC2317 reverse if it exists - if ( $argv{name} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\.in-addr\.arpa$/o ) { - my $address = join('.',($4, $3, $2, $1)); - if ( my $ipb = Ipblock->search(address=>$address)->first ){ - if ( my $subnet = $ipb->parent ){ - my $subnetaddr = $subnet->address; - my $prefix = $subnet->prefix; - my @octs = split('\.', $subnetaddr); - $argv{name} = $octs[3]."-".$prefix.".$octs[2].$octs[1].$octs[0].in-addr.arpa"; - $logger->debug(sub{ "Zone::search: $argv{name}" }); - if ( $class->SUPER::search(%argv, $opts) ){ - $logger->debug(sub{ "Zone::search: found: ", $argv{name} }); - return $class->SUPER::search(%argv, $opts); - } - } - } - } - while ( @sections ){ - $argv{name} = join '.', @sections; - $logger->debug(sub{ "Zone::search: $argv{name}" }); - if ( $class->SUPER::search(%argv, $opts) ){ - # We call the method again to not mess - # with CDBI's wantarray checks - $logger->debug(sub{ "Zone::search: found: ", $argv{name} }); - return $class->SUPER::search(%argv, $opts); - } - shift @sections; - } - } + return wantarray ? @result : $result; + } + elsif (defined $argv{name}) { + if (my $alias = ZoneAlias->search_where(name => ['lower(name) = ?', lc($argv{name})])->first) { + return $class->SUPER::search(id => $alias->zone->id, $opts); + } + elsif ($argv{name} =~ /\./ && !Ipblock->matches_v4($argv{name})) { + my @sections = split '\.', $argv{name}; + + # first try to search for the RFC2317 reverse if it exists + if ($argv{name} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\.in-addr\.arpa$/) { + my $address = join('.',($4, $3, $2, $1)); + if (my $ipb = Ipblock->search(address => $address)->first) { + if (my $subnet = $ipb->parent) { + my $subnetaddr = $subnet->address; + my $prefix = $subnet->prefix; + my @octs = split('\.', $subnetaddr); + $argv{name} = $octs[3]."-".$prefix.".$octs[2].$octs[1].$octs[0].in-addr.arpa"; + $logger->debug(sub{ "Zone::search: $argv{name}" }); + if ($class->SUPER::search(%argv, $opts)) { + $logger->debug(sub{ "Zone::search: found: ", $argv{name} }); + return $class->SUPER::search(%argv, $opts); + } + } + } + } + while (@sections) { + my $new_name = join '.', @sections; + $argv{name} = [ + 'lower(name) = ?', + lc($new_name), + ]; + $logger->debug(sub{ "Zone::search: $new_name" }); + if ($class->SUPER::search_where(\%argv, $opts)) { + # We call the method again to not mess + # with CDBI's wantarray checks + $logger->debug(sub{ "Zone::search: found: $new_name" }); + return $class->SUPER::search_where(\%argv, $opts); + } + shift @sections; + } + } } return wantarray ? @result : $result; } diff --git a/t/Zone.t b/t/Zone.t index 742fa74a9..25f146eb5 100644 --- a/t/Zone.t +++ b/t/Zone.t @@ -5,8 +5,20 @@ use lib "lib"; BEGIN { use_ok('Netdot::Model::Zone'); } -my $obj = Zone->insert({name=>'domain.name'}); -isa_ok($obj, 'Netdot::Model::Zone', 'insert'); +my $obj = Zone->insert( + { + name => 'domain.name', + }, +); +isa_ok($obj, 'Netdot::Model::Zone', 'insert zone'); + +my $alias = ZoneAlias->insert( + { + name => 'alias.name', + zone => $obj->id, + }, +); +isa_ok($alias, 'Netdot::Model::ZoneAlias', 'insert zone alias'); lives_and { is(Zone->_dot_arpa_to_ip('1.in-addr.arpa'), '1.0.0.0/8', 'IPv4 /8 .arpa zone to address') }; lives_and { is(Zone->_dot_arpa_to_ip('2.1.in-addr.arpa'), '1.2.0.0/16', 'IPv4 /16 .arpa zone to address') }; @@ -28,9 +40,16 @@ lives_and { is(Zone->_dot_arpa_to_ip('c.b.a.9.8.7.6.5.4.3.2.1.ip6.arpa'), '1234: is(Zone->search(name=>'sub.domain.name')->first, $obj, 'search scalar' ); is_deeply([Zone->search(name=>'sub.domain.name')], [$obj], 'search array' ); +is(Zone->search(name=>'sub.DoMaIn.NaMe')->first, $obj, 'case insensitive search scalar' ); +is_deeply([Zone->search(name=>'sub.DoMaIn.NaMe')], [$obj], 'case insensitive search array' ); is(Zone->search(name=>'fake')->first, undef, 'search empty scalar' ); is_deeply([Zone->search(name=>'fake')], [], 'search empty array' ); +is(Zone->search(name=>'alias.name')->first, $obj, 'alias search scalar' ); +is_deeply([Zone->search(name=>'alias.name')], [$obj], 'alias search array' ); +is(Zone->search(name=>'AlIaS.NaMe')->first, $obj, 'case insensitive alias search scalar' ); +is_deeply([Zone->search(name=>'AlIaS.NaMe')], [$obj], 'case insensitive alias search array' ); + is(Zone->search_like(name=>'domain.name')->first, $obj, 'search_like scalar' ); is_deeply([Zone->search_like(name=>'domain.name')], [$obj], 'search_like array' ); is(Zone->search_like(name=>'fake')->first, undef, 'search_like empty scalar' );