diff --git a/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm b/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm index 28ea4a0d..a0cd65c4 100644 --- a/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm +++ b/Netdisco/lib/Netdisco/DB/Result/DevicePort.pm @@ -64,7 +64,17 @@ __PACKAGE__->set_primary_key("port", "ip"); # Created by DBIx::Class::Schema::Loader v0.07015 @ 2012-01-07 14:20:02 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lcbweb0loNwHoWUuxTN/hA -__PACKAGE__->belongs_to( device => 'Netdisco::DB::Result::Device', 'ip' ); +__PACKAGE__->belongs_to( device => 'Netdisco::DB::Result::Device', 'ip', + { + '+select' => [ + \"replace(age(timestamp 'epoch' + uptime / 100 * interval '1 second', timestamp '1970-01-01 00:00:00-00')::text, 'mon', 'month')", + \"to_char(last_discover, 'YYYY-MM-DD HH24:MI')", + \"to_char(last_macsuck, 'YYYY-MM-DD HH24:MI')", + \"to_char(last_arpnip, 'YYYY-MM-DD HH24:MI')", + ], + '+as' => [qw/ uptime last_discover last_macsuck last_arpnip /], + }, +); __PACKAGE__->has_many( port_vlans_tagged => 'Netdisco::DB::Result::DevicePortVlan', sub { my $args = shift; @@ -91,4 +101,25 @@ sub native_vlan { return eval { $row->native_port_vlan->vlan || undef }; }; +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); +} + 1; diff --git a/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm b/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm index 56756a9a..254d0dba 100644 --- a/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm +++ b/Netdisco/lib/Netdisco/DB/ResultSet/DevicePort.pm @@ -4,6 +4,24 @@ use base 'DBIx::Class::ResultSet'; use strict; use warnings FATAL => 'all'; +sub by_ip { + my ($set, $ip) = @_; + return $set unless $ip; + + return $set->search( + { + 'me.ip' => $ip, + }, + { + '+select' => [ + \"to_char(last_discover - (uptime - lastchange) / 100 * interval '1 second', 'YYYY-MM-DD HH24:MI:SS')", + ], + '+as' => [qw/ lastchange_stamp /], + join => 'device', + } + ); +} + sub by_mac { my ($set, $mac) = @_; return $set unless $mac; diff --git a/Netdisco/lib/Netdisco/Web.pm b/Netdisco/lib/Netdisco/Web.pm index e7fe36a4..f5df4d44 100644 --- a/Netdisco/lib/Netdisco/Web.pm +++ b/Netdisco/lib/Netdisco/Web.pm @@ -10,6 +10,7 @@ use HTML::Entities (); # to ensure dependency is met use NetAddr::IP::Lite ':lower'; use Net::MAC (); use List::MoreUtils (); +use netdisco (); # for sort_port hook 'before' => sub { if (! session('user') && request->path !~ m{^/login}) { @@ -33,15 +34,15 @@ hook 'before' => sub { { name => 'c_port', label => 'Port', default => 'on' }, { name => 'c_descr', label => 'Description', default => '' }, { name => 'c_type', label => 'Type', default => '' }, - { name => 'c_duplex', label => 'Duplex', default => 'on' }, + { name => 'c_duplex', label => 'Duplex', default => '' }, { name => 'c_lastchange', label => 'Last Change', default => '' }, { name => 'c_name', label => 'Name', default => 'on' }, - { name => 'c_speed', label => 'Speed', default => 'on' }, + { name => 'c_speed', label => 'Speed', default => '' }, { name => 'c_mac', label => 'Port MAC', default => '' }, { name => 'c_mtu', label => 'MTU', default => '' }, { name => 'c_vlan', label => 'Native VLAN', default => 'on' }, { name => 'c_vmember', label => 'VLAN Membership', default => 'on' }, - { name => 'c_connected', label => 'Connected Devices', default => '' }, + { name => 'c_connected', label => 'Connected Devices', default => 'on' }, { name => 'c_stp', label => 'Spanning Tree', default => '' }, { name => 'c_up', label => 'Status', default => '' }, ]); @@ -88,6 +89,22 @@ ajax '/ajax/content/device/:thing' => sub { }; # device ports with a description (er, name) matching +ajax '/ajax/content/device/ports' => sub { + my $ip = param('ip'); + return unless $ip; + + my $set = schema('netdisco')->resultset('DevicePort')->by_ip($ip); + return unless $set->count; + + my $results = [ sort { &netdisco::sort_port($a->port, $b->port) } $set->all ]; + + content_type('text/html'); + template 'ajax/device/ports.tt', { + results => $results, + }, { layout => undef }; +}; + +# device details table ajax '/ajax/content/device/details' => sub { my $ip = param('ip'); return unless $ip; diff --git a/Netdisco/public/css/style.css b/Netdisco/public/css/style.css index fdc0f391..ae60bb21 100644 --- a/Netdisco/public/css/style.css +++ b/Netdisco/public/css/style.css @@ -78,6 +78,7 @@ form .clearfix.success input { /* somewhere between span1 and span2 is desirable */ .nd_days_select { width: 56px; + margin-left: -2px !important; } /* search/filter button placement */ @@ -137,3 +138,7 @@ form .clearfix.success input { .nd_port_query { margin-left: -2px !important; } + +.center_cell { + text-align: center; +} diff --git a/Netdisco/views/ajax/device/ports.tt b/Netdisco/views/ajax/device/ports.tt new file mode 100644 index 00000000..250726c9 --- /dev/null +++ b/Netdisco/views/ajax/device/ports.tt @@ -0,0 +1,91 @@ + + + + + [% FOREACH item IN vars.port_columns %] + [% NEXT UNLESS params.${item.name} %] + [% item.label %] + [% END %] + + + + [% FOREACH row in results %] + [% NEXT IF params.free AND NOT row.is_free(params.age_num, params.age_unit) %] + + + [% IF params.c_port %] + + [% END %] + [% IF params.c_descr %] + + [% END %] + [% IF params.c_type %] + + [% END %] + [% IF params.c_duplex %] + + [% END %] + [% IF params.c_lastchange %] + + [% END %] + [% IF params.c_name %] + + [% END %] + [% IF params.c_speed %] + + [% END %] + [% IF params.c_mac %] + + [% END %] + [% IF params.c_mtu %] + + [% END %] + [% IF params.c_vlan %] + + [% END %] + [% IF params.c_vmember %] + + [% END %] + [% IF params.c_connected %] + + [% END %] + [% IF params.c_stp %] + + [% END %] + [% IF params.c_up %] + + [% END %] + + [% END %] + +
+ [% IF row.up_admin == 'down' %] + s + [% ELSIF row.stp == 'blocking' %] + b + [% ELSIF row.is_free(params.age_num, params.age_unit) %] + f + [% ELSIF row.up_admin == 'up' AND row.up == 'down' %] + d + [% END %] + + [% row.port | html_entity %] + [% row.descr | html_entity %][% row.type | html_entity %] + [% IF row.up == 'up' AND row.duplex %] + [% row.duplex | html_entity %] / [% row.duplex_admin | html_entity %] + [% END %] + [% row.get_column('lastchange_stamp') | html_entity %][% row.name | html_entity %][% row.speed | html_entity %][% row.mac | html_entity %][% row.mtu | html_entity %] + [% row.vlan | html_entity %] + + [% SET count = 1 %] + [% FOREACH vlan IN row.tagged_vlans %] + [% vlan.vlan | html_entity %] + [% SET count = count + 1 %] + [% IF count > 25 %] + (more...) + [% LAST %] + [% ELSE %] + [% ', ' IF NOT loop.last %] + [% END %] + [% END %] + [% row.connected | html_entity %][% row.stp | html_entity %][% row.up | html_entity %]
diff --git a/Netdisco/views/inc/device/ports.tt b/Netdisco/views/inc/device/ports.tt index 56d53bfd..f06edf0c 100644 --- a/Netdisco/views/inc/device/ports.tt +++ b/Netdisco/views/inc/device/ports.tt @@ -3,27 +3,27 @@
- + rel="twipsy" data-placement="right" data-offset="5" title="Filter by Port, Name or VLAN"/>