From 9f2920467639efa6757ae1bf6cc478553a6b41bf Mon Sep 17 00:00:00 2001 From: "Eric A. Miller" Date: Mon, 4 Nov 2013 19:45:49 -0500 Subject: [PATCH] Squashed commit of the following: commit 4b6f3de36cbf508c05fbf7f18acee1758bc838ed Merge: 94ed8b8 200dd2d Author: Eric A. Miller Date: Mon Nov 4 19:30:04 2013 -0500 Merge branch 'master' into em-poe-report commit 94ed8b8cf01963030a40829fd71bff529750719e Author: Eric A. Miller Date: Sun Nov 3 23:34:07 2013 -0500 Show PoE module statistics in device details if available commit 652974b312c11d6017cf6f3c0693d62085774e80 Author: Eric A. Miller Date: Sun Nov 3 23:31:02 2013 -0500 Move PoE statistics to model from controller Add missing div in PoE status report commit f94f68b1c84d23b2f252d985509757d95be5c0d0 Author: Eric A. Miller Date: Tue Oct 22 22:38:45 2013 -0400 Device PoE status report --- .../App/Netdisco/DB/Result/DevicePortPower.pm | 11 +++ .../lib/App/Netdisco/DB/Result/DevicePower.pm | 10 +++ .../lib/App/Netdisco/DB/ResultSet/Device.pm | 90 +++++++++++++++++++ .../App/Netdisco/Web/Plugin/Device/Details.pm | 9 +- .../Web/Plugin/Report/DevicePoeStatus.pm | 35 ++++++++ Netdisco/share/config.yml | 1 + Netdisco/share/views/ajax/device/details.tt | 13 +++ .../views/ajax/report/devicepoestatus.tt | 60 +++++++++++++ .../views/ajax/report/devicepoestatus_csv.tt | 25 ++++++ 9 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 Netdisco/lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm create mode 100644 Netdisco/share/views/ajax/report/devicepoestatus.tt create mode 100644 Netdisco/share/views/ajax/report/devicepoestatus_csv.tt diff --git a/Netdisco/lib/App/Netdisco/DB/Result/DevicePortPower.pm b/Netdisco/lib/App/Netdisco/DB/Result/DevicePortPower.pm index 4798f9ca..34274f5b 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/DevicePortPower.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/DevicePortPower.pm @@ -43,4 +43,15 @@ __PACKAGE__->belongs_to( port => 'App::Netdisco::DB::Result::DevicePort', { 'foreign.ip' => 'self.ip', 'foreign.port' => 'self.port', }); +=head2 device_module + +Returns the entry from the C table for which this Power entry +applies. + +=cut + +__PACKAGE__->belongs_to( device_module => 'App::Netdisco::DB::Result::DevicePower', { + 'foreign.ip' => 'self.ip', 'foreign.module' => 'self.module', +}); + 1; diff --git a/Netdisco/lib/App/Netdisco/DB/Result/DevicePower.pm b/Netdisco/lib/App/Netdisco/DB/Result/DevicePower.pm index 2f2cbbe5..f15ce604 100644 --- a/Netdisco/lib/App/Netdisco/DB/Result/DevicePower.pm +++ b/Netdisco/lib/App/Netdisco/DB/Result/DevicePower.pm @@ -35,5 +35,15 @@ Returns the entry from the C table on which this power module was discov __PACKAGE__->belongs_to( device => 'App::Netdisco::DB::Result::Device', 'ip' ); +=head2 ports + +Returns the set of PoE ports associated with a power module. + +=cut + +__PACKAGE__->has_many( ports => 'App::Netdisco::DB::Result::DevicePortPower', { + 'foreign.ip' => 'self.ip', 'foreign.module' => 'self.module', +} ); + # You can replace this text with custom code or comments, and it will be preserved on regeneration 1; diff --git a/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm b/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm index 5c1d0f91..014039b9 100644 --- a/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm +++ b/Netdisco/lib/App/Netdisco/DB/ResultSet/Device.pm @@ -575,4 +575,94 @@ sub delete { return $self->next::method(); } +=head2 with_poestats_as_hashref + +This is a modifier for C which returns a hash reference with the +power_modules hash augmented with the following statistics as keys: + +=over 4 + +=item capable_ports + +Count of ports which have the ability to supply PoE. + +=item disabled_ports + +Count of ports with PoE administratively disabled. + +=item powered_ports + +Count of ports which are delivering power. + +=item errored_ports + +Count of ports either reporting a fault or in test mode. + +=item pwr_committed + +Total power that has been negotiated and therefore committed on ports +actively supplying power. + +=item pwr_delivering + +Total power as measured on ports actively supplying power. + +=back + +=cut + +sub with_poestats_as_hashref { + my ( $rs, $cond, $attrs ) = @_; + + my @return = $rs->search( + {}, + { result_class => 'DBIx::Class::ResultClass::HashRefInflator', + prefetch => { power_modules => 'ports' }, + order_by => { -asc => [qw/me.ip power_modules.module/] } + })->all; + + my $poemax = { + 'class0' => 15.4, + 'class1' => 4.0, + 'class2' => 7.0, + 'class3' => 15.4, + 'class4' => 30.0 + }; + + foreach my $device (@return) { + foreach my $module (@{$device->{power_modules}}) { + $module->{capable_ports} = 0; + $module->{disabled_ports} = 0; + $module->{powered_ports} = 0; + $module->{errored_ports} = 0; + $module->{pwr_committed} = 0; + $module->{pwr_delivering} = 0; + + foreach my $port ( @{$module->{ports}} ) { + $module->{capable_ports}++; + + if ( $port->{admin} eq 'false' ) { + $module->{disabled_ports}++; + } + elsif ( $port->{status} ne 'searching' + and $port->{status} ne 'deliveringPower' ) + { + $module->{errored_ports}++; + } + elsif ( $port->{status} eq 'deliveringPower' ) { + $module->{powered_ports}++; + if ( defined $port->{power} and $port->{power} ) { + $module->{pwr_delivering} += int( $port->{power} / 1000 ); + $module->{pwr_committed} += $poemax->{ $port->{class} }; + } + else { + $module->{pwr_committed} += $poemax->{ $port->{class} }; + } + } + } + } + } + return \@return; +} + 1; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm index 7288fdfc..b37ce642 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Device/Details.pm @@ -13,11 +13,16 @@ register_device_tab({ tag => 'details', label => 'Details' }); ajax '/ajax/content/device/details' => require_login sub { my $q = param('q'); my $device = schema('netdisco')->resultset('Device') - ->with_times()->search_for_device($q) or send_error('Bad device', 400); + ->search_for_device($q) or send_error('Bad device', 400); + + my $results + = schema('netdisco')->resultset('Device') + ->search( { 'me.ip' => $device->ip } )->with_times() + ->with_poestats_as_hashref; content_type('text/html'); template 'ajax/device/details.tt', { - d => $device, + d => $results->[0], }, { layout => undef }; }; diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm new file mode 100644 index 00000000..54a98d2d --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/DevicePoeStatus.pm @@ -0,0 +1,35 @@ +package App::Netdisco::Web::Plugin::Report::DevicePoeStatus; + +use Dancer ':syntax'; +use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; + +use App::Netdisco::Web::Plugin; + +register_report( + { category => 'Device', + tag => 'devicepoestatus', + label => 'Power over Ethernet (PoE) Status', + provides_csv => 1, + } +); + +get '/ajax/content/report/devicepoestatus' => require_login sub { + my $results = schema('netdisco')->resultset('Device') + ->with_poestats_as_hashref; + + return unless scalar $results; + + if ( request->is_ajax ) { + template 'ajax/report/devicepoestatus.tt', { results => $results, }, + { layout => undef }; + } + else { + header( 'Content-Type' => 'text/comma-separated-values' ); + template 'ajax/report/devicepoestatus_csv.tt', + { results => $results, }, + { layout => undef }; + } +}; + +1; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 57626670..08ac901c 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -38,6 +38,7 @@ web_plugins: - Report::ApRadioChannelPower - Report::HalfDuplex - Report::DeviceByLocation + - Report::DevicePoeStatus - Report::DuplexMismatch - Report::SsidInventory - Report::VlanInventory diff --git a/Netdisco/share/views/ajax/device/details.tt b/Netdisco/share/views/ajax/device/details.tt index 21c3cfde..e0a1f398 100644 --- a/Netdisco/share/views/ajax/device/details.tt +++ b/Netdisco/share/views/ajax/device/details.tt @@ -86,6 +86,19 @@
PS1 [[% d.ps1_type | html_entity %]]: [% d.ps1_status | html_entity %]
PS2 [[% d.ps2_type | html_entity %]]: [% d.ps2_status | html_entity %] + [% IF d.power_modules.size %] + + PoE Status + + [% FOREACH m IN d.power_modules %] + [% UNLESS m.module == 1 %] +
+ [% END %] + Module [% m.module %]: [% m.status | html_entity %], [% m.capable_ports %] power-capable ports, [% m.powered_ports %] powered ([% m.disabled_ports %] admin disabled, [% m.errored_ports %] errors), [% m.pwr_committed %]/[% m.power %] watts committed. + [% END %] + + + [% END %] MAC Address [% d.mac | html_entity %] diff --git a/Netdisco/share/views/ajax/report/devicepoestatus.tt b/Netdisco/share/views/ajax/report/devicepoestatus.tt new file mode 100644 index 00000000..df168682 --- /dev/null +++ b/Netdisco/share/views/ajax/report/devicepoestatus.tt @@ -0,0 +1,60 @@ +[% USE Number.Format %] +
+[% count = 0 %] +[% FOREACH row IN results %] + [% NEXT UNLESS row.power_modules.size %] + [% count = count + 1 %] +
+ +
+
+ + + + + + + + + + + + + + + + [% FOREACH m IN row.power_modules %] + + + + + + + + + + + + [% END %] + +
PoE
Module
Power
(W)
SupplyCapable
Ports
Powered
Ports
Disabled
Ports
Errored
Ports
Committed
(W)
Delivering
(W)
[% m.module %][% m.power | format_number %][% m.status %][% m.capable_ports %][% m.powered_ports %][% m.disabled_ports %][% m.errored_ports %][% m.pwr_committed | format_number %][% m.pwr_delivering | format_number %]
+
+
+
+[%END%] +
+ + diff --git a/Netdisco/share/views/ajax/report/devicepoestatus_csv.tt b/Netdisco/share/views/ajax/report/devicepoestatus_csv.tt new file mode 100644 index 00000000..8f19ca6f --- /dev/null +++ b/Netdisco/share/views/ajax/report/devicepoestatus_csv.tt @@ -0,0 +1,25 @@ +[% USE CSV -%] +[% CSV.dump([ 'Device' 'Model' 'Device Location' 'PoE Module' 'Power (W)' + 'Supply' 'Capable Ports' 'Powered Ports' 'Disabled Ports' + 'Errored Ports' 'Committed (W)' 'Delivering (W)' ]) %] + +[% FOREACH row IN results %] + [% NEXT UNLESS row.power_modules.size %] + [% mydlist = [] %] + [% mydevice = row.dns || row.name %] + [% mydlist.push(mydevice) %] + [% mydlist.push(row.model) %] + [% mydlist.push(row.location) %] + [% FOREACH m IN row.power_modules %] + [% myplist = [] %] + [% FOREACH col IN [ m.module m.power m.status m.capable_ports + m.powered_ports m.disabled_ports m.errored_ports + m.pwr_committed m.pwr_delivering + ] %] + [% myplist.push(col) %] + [% END %] + [% CALL mydlist.splice(3, 9, myplist ) %] + [% CSV.dump(mydlist) %] + + [% END %] +[%END%] \ No newline at end of file