diff --git a/Netdisco/Changes b/Netdisco/Changes index 012928be..6b988239 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -4,6 +4,7 @@ * [#75] Device module inventory report / search * [#70] SSID Search (port) + * [#72] Search on Vendor / OUI [ENHANCEMENTS] diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm new file mode 100644 index 00000000..06ac0742 --- /dev/null +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Report/NodeVendor.pm @@ -0,0 +1,104 @@ +package App::Netdisco::Web::Plugin::Report::NodeVendor; + +use Dancer ':syntax'; +use Dancer::Plugin::DBIC; +use Dancer::Plugin::Auth::Extensible; + +use App::Netdisco::Web::Plugin; + +register_report( + { category => 'Node', + tag => 'nodevendor', + label => 'Node Vendor Inventory', + provides_csv => 1, + } +); + +hook 'before' => sub { + + return + unless ( + request->path eq uri_for('/report/nodevendor')->path + or index( request->path, + uri_for('/ajax/content/report/nodevendor')->path ) == 0 + ); + + params->{'limit'} ||= 1024; + params->{'order'} ||= 'MAC'; + +}; + +hook 'before_template' => sub { + my $tokens = shift; + + return + unless ( + request->path eq uri_for('/report/nodevendor')->path + or index( request->path, + uri_for('/ajax/content/report/nodevendor')->path ) == 0 + ); + + # used in the search sidebar template to set selected items + foreach my $opt (qw/vendor/) { + my $p = ( + ref [] eq ref param($opt) + ? param($opt) + : ( param($opt) ? [ param($opt) ] : [] ) + ); + $tokens->{"${opt}_lkp"} = { map { $_ => 1 } @$p }; + } +}; + +get '/ajax/content/report/nodevendor' => require_login sub { + + my $vendor = param('vendor'); + + my $rs = schema('netdisco')->resultset('Node'); + + if ( defined $vendor ) { + + my $match = $vendor eq 'blank' ? undef : $vendor; + + my $order = { + MAC => 'me.mac', + Device => 'me.switch', + Vendor => 'oui.company' + }; + + $rs = $rs->search( { 'oui.abbrev' => $match } ) + ->prefetch( [qw/ oui device /] ); + + unless ( param('archived') ) { + $rs = $rs->search( { -bool => 'me.active' } ); + } + + $rs = $rs->order_by( $order->{ param('order') } ) + ->limit( param('limit') )->hri; + } + else { + $rs = $rs->search( + { -bool => 'me.active' }, + { join => 'oui', + select => [ 'oui.abbrev', { count => 'me.mac' } ], + as => [qw/ vendor count /], + group_by => [qw/ oui.abbrev /] + } + )->order_by( { -desc => 'count' } )->hri; + } + + return unless $rs->has_rows; + + if ( request->is_ajax ) { + template 'ajax/report/nodevendor.tt', + { results => $rs, opt => $vendor }, + { layout => undef }; + } + else { + header( 'Content-Type' => 'text/comma-separated-values' ); + template 'ajax/report/nodevendor_csv.tt', + { results => $rs, opt => $vendor }, + { layout => undef }; + } +}; + +1; diff --git a/Netdisco/lib/App/Netdisco/Web/Report.pm b/Netdisco/lib/App/Netdisco/Web/Report.pm index 1dccd5fc..15ddf2ba 100644 --- a/Netdisco/lib/App/Netdisco/Web/Report.pm +++ b/Netdisco/lib/App/Netdisco/Web/Report.pm @@ -8,7 +8,7 @@ get '/report/*' => require_login sub { my ($tag) = splat; # used in the report search sidebar to populate select inputs - my ( $domain_list, $class_list, $ssid_list ); + my ( $domain_list, $class_list, $ssid_list, $vendor_list ); if ( $tag eq 'netbios' ) { $domain_list = [ schema('netdisco')->resultset('NodeNbt') @@ -22,6 +22,18 @@ get '/report/*' => require_login sub { $ssid_list = [ schema('netdisco')->resultset('DevicePortSsid') ->get_distinct_col('ssid') ]; } + elsif ( $tag eq 'nodevendor' ) { + $vendor_list = [ + schema('netdisco')->resultset('Node')->search( + {}, + { join => 'oui', + columns => ['oui.abbrev'], + order_by => 'oui.abbrev', + group_by => 'oui.abbrev', + } + )->get_column('abbrev')->all + ]; + } # trick the ajax into working as if this were a tabbed page params->{tab} = $tag; @@ -33,6 +45,7 @@ get '/report/*' => require_login sub { domain_list => $domain_list, class_list => $class_list, ssid_list => $ssid_list, + vendor_list => $vendor_list, }; }; diff --git a/Netdisco/share/config.yml b/Netdisco/share/config.yml index 318f7140..baf8194e 100644 --- a/Netdisco/share/config.yml +++ b/Netdisco/share/config.yml @@ -51,6 +51,7 @@ web_plugins: - Report::ModuleInventory - Report::Netbios - Report::NodeMultiIPs + - Report::NodeVendor - Report::PhonesDiscovered - Report::SsidInventory - Report::VlanInventory diff --git a/Netdisco/share/views/ajax/report/nodevendor.tt b/Netdisco/share/views/ajax/report/nodevendor.tt new file mode 100644 index 00000000..fc80b8a3 --- /dev/null +++ b/Netdisco/share/views/ajax/report/nodevendor.tt @@ -0,0 +1,59 @@ +[% USE Number.Format %] +[% IF opt %] + + + + + + + + + + [% WHILE (row = results.next) %] + + + + + + [% END %] + +
MACVendorDevice (Port)
+ + [% row.mac.upper | html_entity %][% '   ' IF NOT row.active %] + + + [% row.oui.abbrev | html_entity %] ( [% row.oui.company | html_entity %] ) + [% ELSE %] + href="[% uri_for('/report/nodevendor') %]?vendor=blank"> + (Unknown Vendor) + [% END %] + + + [% row.device.dns || row.device.name || row.switch | html_entity %] ( [% row.port | html_entity %] ) +
+[% ELSE %] + + + + + + + + + [% WHILE (row = results.next) %] + + + + + [% END %] + +
VendorCount
+ + [% row.vendor || '(Unknown Vendor)' | html_entity %] + [% row.count | format_number %]
+[% END %] diff --git a/Netdisco/share/views/ajax/report/nodevendor_csv.tt b/Netdisco/share/views/ajax/report/nodevendor_csv.tt new file mode 100644 index 00000000..356d7b9c --- /dev/null +++ b/Netdisco/share/views/ajax/report/nodevendor_csv.tt @@ -0,0 +1,26 @@ +[% USE CSV -%] +[% IF opt %] + [% CSV.dump(['MAC' 'Vendor' 'Company' 'Device' 'Port']) %] + + [% WHILE (row = results.next) %] + [% mylist = [] %] + [% device = row.device.dns || row.device.name || row.switch %] + [% FOREACH col IN [ row.mac.upper row.oui.abbrev row.oui.company device row.port ] %] + [% mylist.push(col) %] + [% END %] + [% CSV.dump(mylist) %] + + [% END %] +[% ELSE %] + [% CSV.dump(['Vendor' 'Count']) %] + + [% WHILE (row = results.next) %] + [% mylist = [] %] + [% vendor = row.vendor || '(Unknown Vendor)' %] + [% FOREACH col IN [ vendor row.count ] %] + [% mylist.push(col) %] + [% END %] + [% CSV.dump(mylist) %] + + [% END %] +[% END %] \ No newline at end of file diff --git a/Netdisco/share/views/sidebar/report/nodevendor.tt b/Netdisco/share/views/sidebar/report/nodevendor.tt new file mode 100644 index 00000000..cf2539ea --- /dev/null +++ b/Netdisco/share/views/sidebar/report/nodevendor.tt @@ -0,0 +1,60 @@ + +

Node Vendor Search

+
+ + + +
+ +
+
+ +
+ + + +
+ +
+ + +
+
+
+ + +