update Node and NodeIp Result/ResultSet to have smarter value-add subs

This commit is contained in:
Oliver Gorwits
2012-02-04 22:23:19 +00:00
parent 85f531358a
commit 6a2f3fdf3a
6 changed files with 244 additions and 85 deletions

View File

@@ -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;

View File

@@ -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<search()>, this returns a C<$resultset> of matching rows from the Node
table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<mac> with
the value to search for.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the Device table and the Device C<dns> column
prefetched.
=back
To limit results only to active nodes, set C<< {active => 1} >> in C<cond>.
=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',
},
);
}

View File

@@ -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<search()>, this returns a C<$resultset> of matching rows from the
NodeIp table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<ip> 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<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=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<search()>, this returns a C<$resultset> of matching rows from the
NodeIp table.
=over 4
=item *
The NodeIp table must have a C<dns> 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<cond> parameter must be a hashref containing a key C<dns> 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<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=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<search()>, this returns a C<$resultset> of matching rows from the
NodeIp table.
=over 4
=item *
The C<cond> parameter must be a hashref containing a key C<mac> with the value
to search for.
=item *
Results are ordered by time last seen.
=item *
Additional columns C<time_first_stamp> and C<time_last_stamp> provide
preformatted timestamps of the C<time_first> and C<time_last> fields.
=item *
A JOIN is performed on the OUI table and the OUI C<company> column prefetched.
=back
To limit results only to active IPs, set C<< {active => 1} >> in C<cond>.
=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;

View File

@@ -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 };
}
};

View File

@@ -14,7 +14,7 @@
</tr>
</thead>
</tbody>
[% WHILE (row = results.next) %]
[% WHILE (row = macs.next) %]
<tr>
<td><a class="nd_linkcell"
href="[% uri_for('/search') %]?[% vars.query_defaults.node %]&q=[% row.mac | uri %]">[% row.mac %]</a></td>
@@ -27,11 +27,11 @@
[% ' <span class="label warning">a</span>' IF NOT row.active %]
</td>
[% IF params.stamps %]
<td>[% row.time_first %]</td>
<td>[% row.time_last %]</td>
<td>[% row.time_first_stamp %]</td>
<td>[% row.time_last_stamp %]</td>
[% END %]
</tr>
[% FOREACH node IN row.node_sightings(params.archived) %]
[% FOREACH node IN archive_filter(row.node_sightings) %]
<tr>
<td>&nbsp;</td>
[% IF params.vendor %]
@@ -43,12 +43,12 @@
[% ' <span class="label warning">a</span>' IF NOT node.active %]
</td>
[% IF params.stamps %]
<td>[% node.time_first %]</td>
<td>[% node.time_last %]</td>
<td>[% node.time_first_stamp %]</td>
<td>[% node.time_last_stamp %]</td>
[% END %]
</tr>
[% END %]
[% FOREACH nodeip IN row.ip_aliases(params.archived) %]
[% FOREACH nodeip IN archive_filter(row.ip_aliases) %]
<tr>
<td>&nbsp;</td>
[% IF params.vendor %]
@@ -60,8 +60,8 @@
[% ' <span class="label warning">a</span>' IF NOT nodeip.active %]
</td>
[% IF params.stamps %]
<td>[% nodeip.time_first %]</td>
<td>[% nodeip.time_last %]</td>
<td>[% nodeip.time_first_stamp %]</td>
<td>[% nodeip.time_last_stamp %]</td>
[% END %]
</tr>
[% END %]

View File

@@ -40,8 +40,8 @@
[% ' <span class="label warning">a</span>' IF NOT row.active %]
</td>
[% IF params.stamps %]
<td>[% row.time_first %]</td>
<td>[% rw.time_last %]</td>
<td>[% row.time_first_stamp %]</td>
<td>[% rw.time_last_stamp %]</td>
[% END %]
</tr>
[% SET first_row = 0 %]
@@ -71,8 +71,8 @@
[% ' <span class="label warning">a</span>' IF NOT node.active %]
</td>
[% IF params.stamps %]
<td>[% node.time_first %]</td>
<td>[% node.time_last %]</td>
<td>[% node.time_first_stamp %]</td>
<td>[% node.time_last_stamp %]</td>
[% END %]
</tr>
[% SET first_row = 0 %]