diff --git a/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm b/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm index fd46020c..64e200a7 100644 --- a/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm +++ b/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm @@ -202,44 +202,6 @@ sub neighbor { return eval { $row->neighbor_alias->device || undef }; } -=head2 is_free( $quantity, $unit ) - -This method can be used to evaluate whether a device port could be considered -unused, based on the last time it changed from the "up" state to a "down" -state. - -Pass in two parameters, first the C<$quantity> which must be an integer, and -second the C<$unit> which must be a string of either C, C, -C or C. - -A True value is returned if the port is in a "down" state but administratively -"up", yet last changed state at or further back than the parameters' time -window. If the port is not in the correct state, or last changed state more -recently, then a False value is returned. - -=cut - -sub is_free { - my ($row, $num, $unit) = @_; - return unless $num =~ m/^\d+$/ - and $unit =~ m/(?:days|weeks|months|years)/; - - return 0 unless - ($row->up_admin and $row->up_admin eq 'up') - and ($row->up and $row->up eq 'down'); - - my $quan = { - days => (60 * 60 * 24), - weeks => (60 * 60 * 24 * 7), - months => (60 * 60 * 24 * 31), - years => (60 * 60 * 24 * 365), - }; - my $total = $num * $quan->{$unit}; - - my $diff_sec = $row->lastchange / 100; - return ($diff_sec >= $total ? 1 : 0); -} - =head1 ADDITIONAL COLUMNS =head2 tagged_vlans_count @@ -265,4 +227,16 @@ between the date stamp and time stamp. That is: sub lastchange_stamp { return (shift)->get_column('lastchange_stamp') } +=head2 is_free + +This method can be used to evaluate whether a device port could be considered +unused, based on the last time it changed from the "up" state to a "down" +state. + +See the C and C modifiers to C. + +=cut + +sub is_free { return (shift)->get_column('is_free') } + 1; diff --git a/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm b/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm index 1679a195..84bb8b7a 100644 --- a/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm +++ b/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm @@ -35,6 +35,75 @@ sub with_times { }); } +=head2 with_free_ports + +This is a modifier for any C (including the helpers below) which +will add the following additional synthesized columns to the result set: + +=over 4 + +=item is_free + +=back + +In the C<$cond> hash (the first parameter) pass in the C which must +be an integer, and the C which must be a string of either C, +C, C or C. + +=cut + +sub with_is_free { + my ($rs, $cond, $attrs) = @_; + + my $interval = (delete $cond->{age_num}) .' '. (delete $cond->{age_unit}); + + return $rs + ->search_rs($cond, $attrs) + ->search({}, + { + '+select' => [ + \["up != 'up' and " + ."age(to_timestamp(extract(epoch from device.last_discover) " + ."- (device.uptime - lastchange)/100)) " + ."> ?::interval", + [{} => $interval]], + ], + '+as' => [qw/ is_free /], + join => 'device', + }); +} + +=head2 only_free_ports + +This is a modifier for any C (including the helpers below) which +will restrict results based on whether the port is considered "free". + +In the C<$cond> hash (the first parameter) pass in the C which must +be an integer, and the C which must be a string of either C, +C, C or C. + +=cut + +sub only_free_ports { + my ($rs, $cond, $attrs) = @_; + + my $interval = (delete $cond->{age_num}) .' '. (delete $cond->{age_unit}); + + return $rs + ->search_rs($cond, $attrs) + ->search( + { + 'up' => { '!=' => 'up' }, + },{ + where => + \["age(to_timestamp(extract(epoch from device.last_discover) " + ."- (device.uptime - lastchange)/100)) " + ."> ?::interval", + [{} => $interval]], + join => 'device' }, + ); +} + =head2 with_vlan_count This is a modifier for any C (including the helpers below) which diff --git a/Netdisco/lib/Netdisco/Web/Device.pm b/Netdisco/lib/Netdisco/Web/Device.pm index b9d04948..a2087fe0 100644 --- a/Netdisco/lib/Netdisco/Web/Device.pm +++ b/Netdisco/lib/Netdisco/Web/Device.pm @@ -93,6 +93,13 @@ ajax '/ajax/content/device/ports' => sub { } } + # filter for free ports if asked + my $free_filter = (param('free') ? 'only_free_ports' : 'with_is_free'); + $set = $set->$free_filter({ + age_num => (param('age_num') || 3), + age_unit => (param('age_unit') || 'months') + }); + # make sure query asks for formatted timestamps when needed $set = $set->with_times if param('c_lastchange'); @@ -108,12 +115,8 @@ ajax '/ajax/content/device/ports' => sub { prefetch => [{$nodes_name => 'ips'}, {neighbor_alias => 'device'}], }) if param('c_connected'); - # sort, and filter by free ports - # the filter could be in the template but here allows a 'no records' msg - my $results = [ sort { &netdisco::sort_port($a->port, $b->port) } - grep { not param('free') - or $_->is_free(param('age_num'), param('age_unit')) } $set->all ]; - + # sort ports (empty set would be a 'no records' msg) + my $results = [ sort { &netdisco::sort_port($a->port, $b->port) } $set->all ]; return unless scalar @$results; content_type('text/html'); diff --git a/Netdisco/views/ajax/device/ports.tt b/Netdisco/views/ajax/device/ports.tt index d4a09acf..de91f54d 100644 --- a/Netdisco/views/ajax/device/ports.tt +++ b/Netdisco/views/ajax/device/ports.tt @@ -16,7 +16,7 @@ s [% ELSIF row.stp == 'blocking' %] b - [% ELSIF row.is_free(params.age_num, params.age_unit) %] + [% ELSIF params.free OR row.is_free %] f [% ELSIF row.up_admin == 'up' AND row.up == 'down' %] d