diff --git a/Netdisco/lib/Netdisco/DB/Result/Node.pm b/Netdisco/lib/Netdisco/DB/Result/Node.pm index 228bdbc4..118c8c09 100644 --- a/Netdisco/lib/Netdisco/DB/Result/Node.pm +++ b/Netdisco/lib/Netdisco/DB/Result/Node.pm @@ -50,10 +50,20 @@ __PACKAGE__->set_primary_key("mac", "switch", "port"); __PACKAGE__->belongs_to( device => 'Netdisco::DB::Result::Device', { 'foreign.ip' => 'self.switch' }, { join_type => 'LEFT' } ); + +# device port may have been deleted (reconfigured modules?) but node remains __PACKAGE__->belongs_to( device_port => 'Netdisco::DB::Result::DevicePort', - { 'foreign.ip' => 'self.switch', 'foreign.port' => 'self.port' }, { join_type => 'LEFT' } ); + { 'foreign.ip' => 'self.switch', 'foreign.port' => 'self.port' }, + { join_type => 'LEFT' } +); + __PACKAGE__->has_many( ips => 'Netdisco::DB::Result::NodeIp', { 'foreign.mac' => 'self.mac', 'foreign.active' => 'self.active' } ); + __PACKAGE__->belongs_to( oui => 'Netdisco::DB::Result::Oui', 'oui' ); +# accessors for custom formatted columns +sub time_first_stamp { return (shift)->get_column('time_first_stamp') } +sub time_last_stamp { return (shift)->get_column('time_last_stamp') } + 1; diff --git a/Netdisco/lib/Netdisco/DB/ResultSet/Node.pm b/Netdisco/lib/Netdisco/DB/ResultSet/Node.pm index fbbce049..23e19b1a 100644 --- a/Netdisco/lib/Netdisco/DB/ResultSet/Node.pm +++ b/Netdisco/lib/Netdisco/DB/ResultSet/Node.pm @@ -4,25 +4,62 @@ use base 'DBIx::Class::ResultSet'; use strict; use warnings FATAL => 'all'; -sub by_mac { - my ($set, $archive, $mac) = @_; - return $set unless $mac; +=head1 search_by_mac( \%cond, \%attrs? ) - return $set->search( - { - 'me.mac' => $mac, - ($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', - }, + my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1}); + +Like C, this returns a C<$resultset> of matching rows from the Node +table. + +=over 4 + +=item * + +The C parameter must be a hashref containing a key C with +the value to search for. + +=item * + +Results are ordered by time last seen. + +=item * + +Additional columns C and C provide +preformatted timestamps of the C and C fields. + +=item * + +A JOIN is performed on the Device table and the Device C column +prefetched. + +=back + +To limit results only to active nodes, set C<< {active => 1} >> in C. + +=cut + +sub search_by_mac { + my ($rs, $cond, $attrs) = @_; + + die "mac address required for search_by_mac\n" + if ref {} ne ref $cond or !exists $cond->{mac}; + + $cond->{'me.mac'} = delete $cond->{mac}; + $attrs ||= {}; + + return $rs + ->search_rs($cond, %$attrs) + ->search({}, + { + order_by => {'-desc' => 'time_last'}, + '+columns' => [qw/ 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_stamp time_last_stamp /], + join => 'device', + }, ); } diff --git a/Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm b/Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm index c3027966..b9f9bfd4 100644 --- a/Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm +++ b/Netdisco/lib/Netdisco/DB/ResultSet/NodeIp.pm @@ -13,69 +13,176 @@ sub has_dns_col { my $search_attr = { order_by => {'-desc' => 'time_last'}, - columns => [qw/ mac ip active oui.company /], + '+columns' => [qw/ oui.company /], '+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 /], + '+as' => [qw/ time_first_stamp time_last_stamp /], join => 'oui' }; -sub by_ip { - my ($set, $archive, $ip) = @_; - return $set unless $ip; +=head1 search_by_ip( \%cond, \%attrs? ) - my $op = '='; - if ('NetAddr::IP::Lite' eq ref $ip) { - $op = '<<=' if $ip->num > 1; + my $set = $rs->search_by_ip({ip => '123.123.123.123', active => 1}); + +Like C, this returns a C<$resultset> of matching rows from the +NodeIp table. + +=over 4 + +=item * + +The C parameter must be a hashref containing a key C with the value +to search for. Value can either be a simple string of IPv4 or IPv6, or a +NetAddr::IP object in which case all results within the CIDR/Prefix will be +retrieved. + +=item * + +Results are ordered by time last seen. + +=item * + +Additional columns C and C provide +preformatted timestamps of the C and C fields. + +=item * + +A JOIN is performed on the OUI table and the OUI C column prefetched. + +=back + +To limit results only to active IPs, set C<< {active => 1} >> in C. + +=cut + +sub search_by_ip { + my ($rs, $cond, $attrs) = @_; + + die "ip address required for search_by_ip\n" + if ref {} ne ref $cond or !exists $cond->{ip}; + $attrs ||= {}; + + # handle either plain text IP or NetAddr::IP (/32 or CIDR) + my ($op, $ip) = ('=', delete $cond->{ip}); + + if ('NetAddr::IP::Lite' eq ref $ip and $ip->num > 1) { + $op = '<<='; $ip = $ip->cidr; } + $cond->{ip} = { $op => $ip }; - return $set->search( - { - ip => { $op => $ip }, - ($archive ? () : (active => 1)), - }, - { - %$search_attr, - ( $set->has_dns_col ? ('+columns' => 'dns') : () ), - } - ); + $rs = $rs->search_rs({}, {'+columns' => 'dns'}) + if $rs->has_dns_col; + + return $rs + ->search_rs($cond, %$attrs) + ->search({}, $search_attr); } -sub by_name { - my ($set, $archive, $name) = @_; - return $set unless $name; - die "do not call by_name unless you have a dns col on the node_ip table." - if not $set->has_dns_col; +=head1 search_by_name( \%cond, \%attrs? ) - return $set->search( - { - dns => { '-ilike' => $name }, - ($archive ? () : (active => 1)), - }, - { - %$search_attr, - '+columns' => 'dns', - } - ); + my $set = $rs->search_by_name({dns => 'foo.example.com', active => 1}); + +Like C, this returns a C<$resultset> of matching rows from the +NodeIp table. + +=over 4 + +=item * + +The NodeIp table must have a C column for this search to work. Typically +this column is the IP's DNS PTR record, cached at the time of Netdisco Arpnip. + +=item * + +The C parameter must be a hashref containing a key C with the value +to search for. The value may optionally include SQL wildcard characters. + +=item * + +Results are ordered by time last seen. + +=item * + +Additional columns C and C provide +preformatted timestamps of the C and C fields. + +=item * + +A JOIN is performed on the OUI table and the OUI C column prefetched. + +=back + +To limit results only to active IPs, set C<< {active => 1} >> in C. + +=cut + +sub search_by_dns { + my ($rs, $cond, $attrs) = @_; + + die "search_by_dns requires a dns col on the node_ip table.\n" + if not $rs->has_dns_col; + + die "dns field required for search_by_dns\n" + if ref {} ne ref $cond or !exists $cond->{dns}; + + $cond->{dns} = { '-ilike' => delete $cond->{dns} }; + $attrs ||= {}; + + return $rs + ->search_rs($cond, %$attrs) + ->search_rs({}, {'+columns' => 'dns'}) + ->search({}, $search_attr); } -sub by_mac { - my ($set, $archive, $mac) = @_; - return $set unless $mac; +=head1 search_by_mac( \%cond, \%attrs? ) - return $set->search( - { - mac => $mac, - ($archive ? () : (active => 1)), - }, - { - %$search_attr, - ( $set->has_dns_col ? ('+columns' => 'dns') : () ), - } - ); + my $set = $rs->search_by_mac({mac => '00:11:22:33:44:55', active => 1}); + +Like C, this returns a C<$resultset> of matching rows from the +NodeIp table. + +=over 4 + +=item * + +The C parameter must be a hashref containing a key C with the value +to search for. + +=item * + +Results are ordered by time last seen. + +=item * + +Additional columns C and C provide +preformatted timestamps of the C and C fields. + +=item * + +A JOIN is performed on the OUI table and the OUI C column prefetched. + +=back + +To limit results only to active IPs, set C<< {active => 1} >> in C. + +=cut + +sub search_by_mac { + my ($rs, $cond, $attrs) = @_; + + die "mac address required for search_by_mac\n" + if ref {} ne ref $cond or !exists $cond->{mac}; + $attrs ||= {}; + + $rs = $rs->search_rs({}, {'+columns' => 'dns'}) + if $rs->has_dns_col; + + return $rs + ->search_rs($cond, %$attrs) + ->search({}, $search_attr); } 1; diff --git a/Netdisco/lib/Netdisco/Web/Search.pm b/Netdisco/lib/Netdisco/Web/Search.pm index c7057c56..7adf3fe4 100644 --- a/Netdisco/lib/Netdisco/Web/Search.pm +++ b/Netdisco/lib/Netdisco/Web/Search.pm @@ -65,13 +65,15 @@ ajax '/ajax/content/search/node' => sub { content_type('text/html'); my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0); + my @active = (param('archived') ? () : (active => 1)); + if (eval { $mac->as_IEEE }) { my $sightings = schema('netdisco')->resultset('Node') - ->by_mac(param('archived'), $mac->as_IEEE); + ->search_by_mac({mac => $mac->as_IEEE, @active}); my $ips = schema('netdisco')->resultset('NodeIp') - ->by_mac(param('archived'), $mac->as_IEEE); + ->search_by_mac({mac => $mac->as_IEEE, @active}); my $ports = schema('netdisco')->resultset('DevicePort') ->by_mac($mac->as_IEEE); @@ -90,9 +92,9 @@ ajax '/ajax/content/search/node' => sub { my $set; if (my $ip = NetAddr::IP::Lite->new($node)) { - # by_ip() will extract cidr notation if necessary + # search_by_ip() will extract cidr notation if necessary $set = schema('netdisco')->resultset('NodeIp') - ->by_ip(param('archived'), $ip); + ->search_by_ip({ip => $ip, @active}); } else { if (schema('netdisco')->resultset('NodeIp')->has_dns_col) { @@ -104,7 +106,7 @@ ajax '/ajax/content/search/node' => sub { if index($node, setting('domain_suffix')) == -1; } $set = schema('netdisco')->resultset('NodeIp') - ->by_name(param('archived'), $node); + ->search_by_dns({dns => $node, @active}); } elsif (setting('domain_suffix')) { $node .= setting('domain_suffix') @@ -120,13 +122,16 @@ ajax '/ajax/content/search/node' => sub { return; } $set = schema('netdisco')->resultset('NodeIp') - ->by_ip(param('archived'), $node); + ->search_by_ip({ip => $node, @active}); } } return unless $set and $set->count; template 'ajax/search/node_by_ip.tt', { - results => $set, + macs => $set, + # a callback for the templates, which + # allows modification of the DB query before execution + archive_filter => sub { (shift)->search({@active}) }, }, { layout => undef }; } }; diff --git a/Netdisco/views/ajax/search/node_by_ip.tt b/Netdisco/views/ajax/search/node_by_ip.tt index 2ad75a90..f5e82295 100644 --- a/Netdisco/views/ajax/search/node_by_ip.tt +++ b/Netdisco/views/ajax/search/node_by_ip.tt @@ -14,7 +14,7 @@ - [% WHILE (row = results.next) %] + [% WHILE (row = macs.next) %] [% row.mac %] @@ -27,11 +27,11 @@ [% ' a' IF NOT row.active %] [% IF params.stamps %] - [% row.time_first %] - [% row.time_last %] + [% row.time_first_stamp %] + [% row.time_last_stamp %] [% END %] - [% FOREACH node IN row.node_sightings(params.archived) %] + [% FOREACH node IN archive_filter(row.node_sightings) %]   [% IF params.vendor %] @@ -43,12 +43,12 @@ [% ' a' IF NOT node.active %] [% IF params.stamps %] - [% node.time_first %] - [% node.time_last %] + [% node.time_first_stamp %] + [% node.time_last_stamp %] [% END %] [% END %] - [% FOREACH nodeip IN row.ip_aliases(params.archived) %] + [% FOREACH nodeip IN archive_filter(row.ip_aliases) %]   [% IF params.vendor %] @@ -60,8 +60,8 @@ [% ' a' IF NOT nodeip.active %] [% IF params.stamps %] - [% nodeip.time_first %] - [% nodeip.time_last %] + [% nodeip.time_first_stamp %] + [% nodeip.time_last_stamp %] [% END %] [% END %] diff --git a/Netdisco/views/ajax/search/node_by_mac.tt b/Netdisco/views/ajax/search/node_by_mac.tt index da7850eb..b0ac3f5a 100644 --- a/Netdisco/views/ajax/search/node_by_mac.tt +++ b/Netdisco/views/ajax/search/node_by_mac.tt @@ -40,8 +40,8 @@ [% ' a' IF NOT row.active %] [% IF params.stamps %] - [% row.time_first %] - [% rw.time_last %] + [% row.time_first_stamp %] + [% rw.time_last_stamp %] [% END %] [% SET first_row = 0 %] @@ -71,8 +71,8 @@ [% ' a' IF NOT node.active %] [% IF params.stamps %] - [% node.time_first %] - [% node.time_last %] + [% node.time_first_stamp %] + [% node.time_last_stamp %] [% END %] [% SET first_row = 0 %]