implement Inventory page feature
This commit is contained in:
@@ -10,6 +10,7 @@ use HTML::Entities (); # to ensure dependency is met
|
|||||||
use Netdisco::Web::AuthN;
|
use Netdisco::Web::AuthN;
|
||||||
use Netdisco::Web::Search;
|
use Netdisco::Web::Search;
|
||||||
use Netdisco::Web::Device;
|
use Netdisco::Web::Device;
|
||||||
|
use Netdisco::Web::Inventory;
|
||||||
|
|
||||||
hook 'before_template' => sub {
|
hook 'before_template' => sub {
|
||||||
my $tokens = shift;
|
my $tokens = shift;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ post '/login' => sub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
redirect '/?failed=1';
|
redirect uri_for('/', {failed => 1});
|
||||||
};
|
};
|
||||||
|
|
||||||
get '/logout' => sub {
|
get '/logout' => sub {
|
||||||
|
|||||||
23
Netdisco/lib/Netdisco/Web/Inventory.pm
Normal file
23
Netdisco/lib/Netdisco/Web/Inventory.pm
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package Netdisco::Web::Inventory;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
|
get '/inventory' => sub {
|
||||||
|
template 'inventory', {
|
||||||
|
models => scalar schema('netdisco')->resultset('Device')->search({},{
|
||||||
|
select => [ 'vendor', 'model', { count => 'ip' } ],
|
||||||
|
as => [qw/vendor model count/],
|
||||||
|
group_by => [qw/vendor model/],
|
||||||
|
order_by => [{-asc => 'vendor'}, {-desc => 'count'}, {-asc => 'model'}],
|
||||||
|
}),
|
||||||
|
releases => scalar schema('netdisco')->resultset('Device')->search({},{
|
||||||
|
select => [ 'os', 'os_ver', { count => 'ip' } ],
|
||||||
|
as => [qw/os os_ver count/],
|
||||||
|
group_by => [qw/os os_ver/],
|
||||||
|
order_by => [{-asc => 'os'}, {-desc => 'count'}, {-asc => 'os_ver'}],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -185,10 +185,12 @@ get '/search' => sub {
|
|||||||
schema('netdisco')->resultset('Device')->get_distinct('vendor')
|
schema('netdisco')->resultset('Device')->get_distinct('vendor')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
params->{'q'} ||= '_'; # FIXME a cheat Inventory, for now
|
|
||||||
|
|
||||||
my $q = param('q');
|
my $q = param('q');
|
||||||
if ($q and not param('tab')) {
|
if (not param('tab')) {
|
||||||
|
if (not $q) {
|
||||||
|
redirect uri_for('/');
|
||||||
|
}
|
||||||
|
|
||||||
# pick most likely tab for initial results
|
# pick most likely tab for initial results
|
||||||
if ($q =~ m/^\d+$/) {
|
if ($q =~ m/^\d+$/) {
|
||||||
params->{'tab'} = 'vlan';
|
params->{'tab'} = 'vlan';
|
||||||
|
|||||||
@@ -17,11 +17,16 @@ body {
|
|||||||
padding-top: 11px;
|
padding-top: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* on both main content and sidebar, hidden overflow is weird and wrong?! */
|
/* on both main content and sidebar, default is hidden */
|
||||||
.tab-content {
|
.tab-content {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ajax results should fill all available */
|
||||||
|
.tab-content table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
/* various styles to adjust the hero box used for homepage + login */
|
/* various styles to adjust the hero box used for homepage + login */
|
||||||
|
|
||||||
@@ -38,6 +43,35 @@ body {
|
|||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
/* styles for Reports */
|
||||||
|
|
||||||
|
/* from Bootstrap doc style sheet */
|
||||||
|
.nd_show-grid [class*="span"] {
|
||||||
|
background-color: cornsilk;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
min-height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
/* styles for Inventory */
|
||||||
|
|
||||||
|
#nd_dev_age_form {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nd_inv_tbl_head {
|
||||||
|
text-align: center;
|
||||||
|
color: lightSlateGray;
|
||||||
|
margin-top: 6px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
/* results table links */
|
/* results table links */
|
||||||
|
|
||||||
|
|||||||
BIN
Netdisco/public/img/glyphicons-halflings-white.png
Normal file
BIN
Netdisco/public/img/glyphicons-halflings-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
Netdisco/public/img/glyphicons-halflings.png
Normal file
BIN
Netdisco/public/img/glyphicons-halflings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -31,11 +31,6 @@
|
|||||||
<a class="close" data-dismiss="alert">×</a>
|
<a class="close" data-dismiss="alert">×</a>
|
||||||
Log in to the Demo with username "demo" and password "demo".
|
Log in to the Demo with username "demo" and password "demo".
|
||||||
</div>
|
</div>
|
||||||
[% ELSE %]
|
|
||||||
<div class="alert alert-success fade in">
|
|
||||||
<a class="close" data-dismiss="alert">×</a>
|
|
||||||
Hit <strong>Enter</strong> in the Search box to view the current Inventory (<em>temporary feature</em>).
|
|
||||||
</div>
|
|
||||||
[% END %]
|
[% END %]
|
||||||
<div class="hero-unit">
|
<div class="hero-unit">
|
||||||
<h2>Welcome to Netdisco</h2>
|
<h2>Welcome to Netdisco</h2>
|
||||||
|
|||||||
62
Netdisco/views/inventory.tt
Normal file
62
Netdisco/views/inventory.tt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<div class="container">
|
||||||
|
[% IF models.count %]
|
||||||
|
<div class="row">
|
||||||
|
<div class="span6">
|
||||||
|
<h3 class="nd_inv_tbl_head">By Platform</h3>
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Vendor</th>
|
||||||
|
<th>Model</th>
|
||||||
|
<th>Count</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
[% FOREACH platform IN models.all %]
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<a class="nd_stealthlink"
|
||||||
|
href="[% uri_for('/search') %]?tab=device&matchall=on&vendor=[% platform.vendor %]">
|
||||||
|
[% platform.vendor %]</a>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<a class="nd_linkcell"
|
||||||
|
href="[% uri_for('/search') %]?tab=device&matchall=on&model=[% platform.model %]">
|
||||||
|
[% platform.model %]</a>
|
||||||
|
</th>
|
||||||
|
<th>[% platform.get_column('count') %]</th>
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="span6">
|
||||||
|
<h3 class="nd_inv_tbl_head">By Software Release</h3>
|
||||||
|
<table class="table table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>OS</th>
|
||||||
|
<th>Version</th>
|
||||||
|
<th>Count</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
[% FOREACH release IN releases.all %]
|
||||||
|
<tr>
|
||||||
|
<th>[% release.os %]</th>
|
||||||
|
<th>
|
||||||
|
<a class="nd_linkcell"
|
||||||
|
href="[% uri_for('/search') %]?tab=device&matchall=on&os_ver=[% release.os_ver %]">
|
||||||
|
[% release.os_ver %]</a>
|
||||||
|
</th>
|
||||||
|
<th>[% release.get_column('count') %]</th>
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
[% ELSE %]
|
||||||
|
<div class="span4 alert alert-info">No devices found. Do you need to run a Discover?</div>
|
||||||
|
[% END %]
|
||||||
|
</div>
|
||||||
@@ -43,12 +43,13 @@
|
|||||||
<a class="brand" href="[% uri_for('/') %]">Netdisco</a>
|
<a class="brand" href="[% uri_for('/') %]">Netdisco</a>
|
||||||
[% IF session.user %]
|
[% IF session.user %]
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="active"><a href="[% uri_for('/') %]">Home</a></li>
|
<li[% ' class="active"' IF vars('nav') == 'inventory' %]>
|
||||||
|
<a href="[% uri_for('/inventory') %]">Inventory</a>
|
||||||
|
</li>
|
||||||
[% IF more_dd.size %]
|
[% IF more_dd.size %]
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
More <b class="caret"></b>
|
More <b class="caret"></b></a>
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
[% FOREACH title IN more_dd.keys.sort %]
|
[% FOREACH title IN more_dd.keys.sort %]
|
||||||
<li><a href="[% uri_for(more_dd.$title) %]">[% title %]</a></li>
|
<li><a href="[% uri_for(more_dd.$title) %]">[% title %]</a></li>
|
||||||
@@ -59,14 +60,13 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<form class="navbar-search pull-left" method="get" action="[% uri_for('/search') %]">
|
<form class="navbar-search pull-left" method="get" action="[% uri_for('/search') %]">
|
||||||
<input placeholder="Find Anything" class="search-query span3" id="nq" name="q" type="text"/>
|
<input placeholder="Find Anything" class="search-query span3" id="nq" name="q" type="text"/>
|
||||||
<img id="navsearchgo" class="navbar_icon" src="[% uri_base %]/images/crystalclear_mag.png"/>
|
<i id="navsearchgo" class="icon-search icon-white navbar_icon"></i>
|
||||||
</form>
|
</form>
|
||||||
<ul class="nav pull-right">
|
<ul class="nav pull-right">
|
||||||
<li class="nd_navbartext">Logged in as </li>
|
<li class="nd_navbartext">Logged in as </li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
[% session.user %] <b class="caret"></b>
|
[% session.user %] <b class="caret"></b></a>
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
[% FOREACH item IN user_dd %]
|
[% FOREACH item IN user_dd %]
|
||||||
<li><a href="[% uri_for(item.link) %]">[% item.title %]</a></li>
|
<li><a href="[% uri_for(item.link) %]">[% item.title %]</a></li>
|
||||||
|
|||||||
31
Netdisco/views/report.tt
Normal file
31
Netdisco/views/report.tt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row nd_show-grid">
|
||||||
|
<div class="span10 offset1">
|
||||||
|
<form id="nd_dev_age_form" class="form-inline">
|
||||||
|
Find Devices
|
||||||
|
<select name="age_type" class="span2">
|
||||||
|
<option value="first">First Discovered</option>
|
||||||
|
<option value="last" selected="selected">Last Updated</option>
|
||||||
|
</select>
|
||||||
|
<select name="age_bool" class="span2">
|
||||||
|
<option value="in">less than</option>
|
||||||
|
<option value="not_in" selected="selected">more than</option>
|
||||||
|
</select>
|
||||||
|
<select name="age_num" class="span1">
|
||||||
|
[% FOREACH count IN [1..32] %]
|
||||||
|
<option[% ' selected="selected"' IF count == 3 %]>[% count %]</option>
|
||||||
|
[% END %]
|
||||||
|
</select>
|
||||||
|
<select name="age_unit" class="span2">
|
||||||
|
<option>days</option>
|
||||||
|
<option>weeks</option>
|
||||||
|
<option selected="selected">months</option>
|
||||||
|
<option>years</option>
|
||||||
|
</select>
|
||||||
|
ago.
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="icon-search icon-white"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -65,14 +65,12 @@
|
|||||||
<li>
|
<li>
|
||||||
<span rel="tooltip" data-placement="left"
|
<span rel="tooltip" data-placement="left"
|
||||||
data-offset="5" title="Free if Down for this period of time">
|
data-offset="5" title="Free if Down for this period of time">
|
||||||
<select id="nd_days_select" name="age_num"/>
|
<select id="nd_days_select" name="age_num">
|
||||||
[% SET count = 1 %]
|
[% FOREACH count IN [1..32] %]
|
||||||
[% WHILE count < 32 %]
|
|
||||||
<option[% ' selected="selected"' IF params.age_num == count %]>[% count %]</option>
|
<option[% ' selected="selected"' IF params.age_num == count %]>[% count %]</option>
|
||||||
[% SET count = count + 1 %]
|
|
||||||
[% END %]
|
[% END %]
|
||||||
</select>
|
</select>
|
||||||
<select id="nd_age_select" name="age_unit"/>
|
<select id="nd_age_select" name="age_unit">
|
||||||
[% FOREACH unit IN [ 'days', 'weeks', 'months', 'years' ] %]
|
[% FOREACH unit IN [ 'days', 'weeks', 'months', 'years' ] %]
|
||||||
<option[% ' selected="selected"' IF params.age_unit == unit %]>[% unit %]</option>
|
<option[% ' selected="selected"' IF params.age_unit == unit %]>[% unit %]</option>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|||||||
Reference in New Issue
Block a user