implement "no devices" prompt for admin users to do first discover
This commit is contained in:
@@ -55,4 +55,46 @@ __PACKAGE__->add_columns(
|
|||||||
__PACKAGE__->set_primary_key("job");
|
__PACKAGE__->set_primary_key("job");
|
||||||
|
|
||||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||||
|
|
||||||
|
=head1 ADDITIONAL COLUMNS
|
||||||
|
|
||||||
|
=head2 entererd_stamp
|
||||||
|
|
||||||
|
Formatted version of the C<entered> field, accurate to the minute.
|
||||||
|
|
||||||
|
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
|
||||||
|
between the date stamp and time stamp. That is:
|
||||||
|
|
||||||
|
2012-02-06 12:49
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub entered_stamp { return (shift)->get_column('entered_stamp') }
|
||||||
|
|
||||||
|
=head2 started_stamp
|
||||||
|
|
||||||
|
Formatted version of the C<started> field, accurate to the minute.
|
||||||
|
|
||||||
|
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
|
||||||
|
between the date stamp and time stamp. That is:
|
||||||
|
|
||||||
|
2012-02-06 12:49
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub started_stamp { return (shift)->get_column('started_stamp') }
|
||||||
|
|
||||||
|
=head2 finished_stamp
|
||||||
|
|
||||||
|
Formatted version of the C<finished> field, accurate to the minute.
|
||||||
|
|
||||||
|
The format is somewhat like ISO 8601 or RFC3339 but without the middle C<T>
|
||||||
|
between the date stamp and time stamp. That is:
|
||||||
|
|
||||||
|
2012-02-06 12:49
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub finished_stamp { return (shift)->get_column('finished_stamp') }
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
41
Netdisco/lib/App/Netdisco/DB/ResultSet/Admin.pm
Normal file
41
Netdisco/lib/App/Netdisco/DB/ResultSet/Admin.pm
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package App::Netdisco::DB::ResultSet::Admin;
|
||||||
|
use base 'DBIx::Class::ResultSet';
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings FATAL => 'all';
|
||||||
|
|
||||||
|
=head1 ADDITIONAL METHODS
|
||||||
|
|
||||||
|
=head2 with_times
|
||||||
|
|
||||||
|
This is a modifier for any C<search()> (including the helpers below) which
|
||||||
|
will add the following additional synthesized columns to the result set:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item entered_stamp
|
||||||
|
|
||||||
|
=item started_stamp
|
||||||
|
|
||||||
|
=item finished_stamp
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub with_times {
|
||||||
|
my ($rs, $cond, $attrs) = @_;
|
||||||
|
|
||||||
|
return $rs
|
||||||
|
->search_rs($cond, $attrs)
|
||||||
|
->search({},
|
||||||
|
{
|
||||||
|
'+columns' => {
|
||||||
|
entered_stamp => \"to_char(entered, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
started_stamp => \"to_char(started, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
finished_stamp => \"to_char(finished, 'YYYY-MM-DD HH24:MI')",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -16,6 +16,7 @@ use App::Netdisco::Web::Report;
|
|||||||
use App::Netdisco::Web::AdminTask;
|
use App::Netdisco::Web::AdminTask;
|
||||||
use App::Netdisco::Web::TypeAhead;
|
use App::Netdisco::Web::TypeAhead;
|
||||||
use App::Netdisco::Web::PortControl;
|
use App::Netdisco::Web::PortControl;
|
||||||
|
use App::Netdisco::Web::JobControl;
|
||||||
|
|
||||||
sub _load_web_plugins {
|
sub _load_web_plugins {
|
||||||
my $plugin_list = shift;
|
my $plugin_list = shift;
|
||||||
@@ -54,6 +55,12 @@ hook 'before_template' => sub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
get '/' => sub {
|
get '/' => sub {
|
||||||
|
if (var('user') and var('user')->admin) {
|
||||||
|
if (schema('netdisco')->resultset('Device')->count == 0) {
|
||||||
|
var('nodevices' => true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template 'index';
|
template 'index';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
25
Netdisco/lib/App/Netdisco/Web/JobControl.pm
Normal file
25
Netdisco/lib/App/Netdisco/Web/JobControl.pm
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package App::Netdisco::Web::JobControl;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
|
post '/admin/discover' => sub {
|
||||||
|
return unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
|
my $ip = NetAddr::IP::Lite->new(param('device'));
|
||||||
|
return unless $ip
|
||||||
|
and $ip->addr ne '0.0.0.0';
|
||||||
|
|
||||||
|
schema('netdisco')->resultset('Admin')->create({
|
||||||
|
device => $ip->addr,
|
||||||
|
action => 'discover',
|
||||||
|
status => 'queued',
|
||||||
|
username => session('user'),
|
||||||
|
userip => request->remote_address,
|
||||||
|
});
|
||||||
|
|
||||||
|
status(302);
|
||||||
|
header(Location => uri_for('/admin/jobqueue')->path_query());
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
27
Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm
Normal file
27
Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package App::Netdisco::Web::Plugin::AdminTask::JobQueue;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::Ajax;
|
||||||
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
|
use App::Netdisco::Web::Plugin;
|
||||||
|
|
||||||
|
register_admin_task({
|
||||||
|
tag => 'jobqueue',
|
||||||
|
label => 'Job Queue',
|
||||||
|
});
|
||||||
|
|
||||||
|
ajax '/ajax/content/admin/jobqueue' => sub {
|
||||||
|
return unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
|
my $set = schema('netdisco')->resultset('Admin')
|
||||||
|
->with_times
|
||||||
|
->search({}, {order_by => { -desc => [qw/entered device action/] }});
|
||||||
|
|
||||||
|
content_type('text/html');
|
||||||
|
template 'ajax/admintask/jobqueue.tt', {
|
||||||
|
results => $set,
|
||||||
|
}, { layout => undef };
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -13,7 +13,7 @@ register_admin_task({
|
|||||||
});
|
});
|
||||||
|
|
||||||
sub _sanity_ok {
|
sub _sanity_ok {
|
||||||
return 0 unless var('user')->admin;
|
return 0 unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
return 0 unless length param('dns')
|
return 0 unless length param('dns')
|
||||||
and param('dns') =~ m/^[[:print:]]+$/
|
and param('dns') =~ m/^[[:print:]]+$/
|
||||||
@@ -86,7 +86,7 @@ ajax '/ajax/content/admin/pseudodevice/update' => sub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ajax '/ajax/content/admin/pseudodevice' => sub {
|
ajax '/ajax/content/admin/pseudodevice' => sub {
|
||||||
return unless var('user')->admin;
|
return unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
my $set = schema('netdisco')->resultset('Device')
|
my $set = schema('netdisco')->resultset('Device')
|
||||||
->search(
|
->search(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ register_admin_task({
|
|||||||
});
|
});
|
||||||
|
|
||||||
sub _sanity_ok {
|
sub _sanity_ok {
|
||||||
return 0 unless var('user')->admin;
|
return 0 unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
my $dev1 = NetAddr::IP::Lite->new(param('dev1'));
|
my $dev1 = NetAddr::IP::Lite->new(param('dev1'));
|
||||||
return 0 unless ($dev1 and $dev1->addr ne '0.0.0.0');
|
return 0 unless ($dev1 and $dev1->addr ne '0.0.0.0');
|
||||||
@@ -54,7 +54,7 @@ ajax '/ajax/content/admin/topology/del' => sub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ajax '/ajax/content/admin/topology' => sub {
|
ajax '/ajax/content/admin/topology' => sub {
|
||||||
return unless var('user')->admin;
|
return unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
my $set = schema('netdisco')->resultset('Topology')
|
my $set = schema('netdisco')->resultset('Topology')
|
||||||
->search({},{order_by => [qw/dev1 dev2 port1/]});
|
->search({},{order_by => [qw/dev1 dev2 port1/]});
|
||||||
|
|||||||
@@ -28,6 +28,9 @@
|
|||||||
<ul id="search_results" class="nav nav-tabs">
|
<ul id="search_results" class="nav nav-tabs">
|
||||||
<li class="active"><a id="[% task.tag %]_link" class="nd_single_tab"
|
<li class="active"><a id="[% task.tag %]_link" class="nd_single_tab"
|
||||||
href="#[% task.tag %]_pane">[% task.label %]</a></li>
|
href="#[% task.tag %]_pane">[% task.label %]</a></li>
|
||||||
|
[% IF task.tag == 'jobqueue' %]
|
||||||
|
<span id="nd_device_name"></span>
|
||||||
|
[% END %]
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="[% task.tag %]_pane"></div>
|
<div class="tab-pane active" id="[% task.tag %]_pane"></div>
|
||||||
|
|||||||
47
Netdisco/share/views/ajax/admintask/jobqueue.tt
Normal file
47
Netdisco/share/views/ajax/admintask/jobqueue.tt
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<table class="table table-bordered table-condensed table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="center_cell">Entered</th>
|
||||||
|
<th class="center_cell">Status</th>
|
||||||
|
<th class="center_cell">Action</th>
|
||||||
|
<th class="center_cell">Device</th>
|
||||||
|
<th class="center_cell">Port</th>
|
||||||
|
<th class="center_cell">Param</th>
|
||||||
|
<th class="center_cell">User</th>
|
||||||
|
<th class="center_cell">Started</th>
|
||||||
|
<th class="center_cell">Finished</th>
|
||||||
|
<th class="center_cell">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</tbody>
|
||||||
|
[% WHILE (row = results.next) %]
|
||||||
|
<tr
|
||||||
|
[% ' class="success"' IF row.status == 'done' %]
|
||||||
|
[% ' class="error"' IF row.status == 'error' %]
|
||||||
|
[% ' class="info"' IF row.status.search('^queued-') %]
|
||||||
|
>
|
||||||
|
<td class="center_cell">[% row.entered_stamp | html_entity %]</td>
|
||||||
|
<td class="center_cell">[% row.action.ucfirst | html_entity %]</td>
|
||||||
|
[% IF row.status.search('^queued-') %]
|
||||||
|
<td class="center_cell">Running on "[% row.status.remove('^queued-') | html_entity %]"</td>
|
||||||
|
[% ELSE %]
|
||||||
|
<td class="center_cell">[% row.status.ucfirst | html_entity %]</td>
|
||||||
|
[% END %]
|
||||||
|
<td class="center_cell"><a class="nd_linkcell"
|
||||||
|
href="[% device_ports %]&q=[% row.device | uri %]">[% row.device | html_entity %]</a></td>
|
||||||
|
<td class="center_cell">[% row.port | html_entity %]</td>
|
||||||
|
<td class="center_cell">[% row.subaction | html_entity %]</td>
|
||||||
|
<td class="center_cell">[% row.username | html_entity %]</td>
|
||||||
|
<td class="center_cell">[% row.started_stamp | html_entity %]</td>
|
||||||
|
<td class="center_cell">[% row.finished_stamp | html_entity %]</td>
|
||||||
|
<td class="center_cell">
|
||||||
|
<form name="del" class="nd_inline_form">
|
||||||
|
<input name="job" type="hidden" value="[% row.job | html_entity %]">
|
||||||
|
<button class="btn" name="del" type="submit"><i class="icon-trash text-error"></i></button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
@@ -44,6 +44,19 @@
|
|||||||
<script type="text/javascript"> $('#nq').focus(); // set focus to navbar search </script>
|
<script type="text/javascript"> $('#nq').focus(); // set focus to navbar search </script>
|
||||||
[% END %]
|
[% END %]
|
||||||
</div>
|
</div>
|
||||||
|
[% IF vars.nodevices %]
|
||||||
|
<div class="hero-unit">
|
||||||
|
<h3>Initial Discovery</h3>
|
||||||
|
<p>You haven't discovered any devices yet.<p>
|
||||||
|
<p>Enter a network device name or IP to queue the first discovery:</p>
|
||||||
|
<form method="post" action="[% uri_for('/admin/discover') %]">
|
||||||
|
<div class="form-horizontal">
|
||||||
|
<input placeholder="Device hostname or IP" class="span4" name="device" type="text"/>
|
||||||
|
<button type="submit" class="btn btn-info">Discover</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
[% END %]
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- /container -->
|
</div> <!-- /container -->
|
||||||
|
|||||||
@@ -7,6 +7,23 @@
|
|||||||
// but which cannot use jQuery delegation via .on()
|
// but which cannot use jQuery delegation via .on()
|
||||||
function inner_view_processing(tab) {
|
function inner_view_processing(tab) {
|
||||||
|
|
||||||
|
// reload this table every 10 seconds
|
||||||
|
if (tab == 'jobqueue') {
|
||||||
|
$('#nd_device_name').text('10');
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('9') }, 1000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('8') }, 2000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('7') }, 3000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('6') }, 4000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('5') }, 5000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('4') }, 6000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('3') }, 7000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('2') }, 8000 );
|
||||||
|
setTimeout(function() { $('#nd_device_name').text('1') }, 9000 );
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#' + tab + '_form').trigger('submit');
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
// activate typeahead on the topo boxes
|
// activate typeahead on the topo boxes
|
||||||
$('.nd_topo_dev').autocomplete({
|
$('.nd_topo_dev').autocomplete({
|
||||||
source: '/ajax/data/deviceip/typeahead'
|
source: '/ajax/data/deviceip/typeahead'
|
||||||
|
|||||||
7
TODO
7
TODO
@@ -1,14 +1,17 @@
|
|||||||
FRONTEND
|
FRONTEND
|
||||||
========
|
========
|
||||||
|
|
||||||
* UI for topo DB table editing
|
|
||||||
* No devices - trigger first discover splash page
|
|
||||||
* view job queue
|
* view job queue
|
||||||
* view logs with colour
|
* view logs with colour
|
||||||
* moar reports
|
* moar reports
|
||||||
|
|
||||||
* (jeneric) device module tab
|
* (jeneric) device module tab
|
||||||
|
|
||||||
|
BACKEND
|
||||||
|
=======
|
||||||
|
|
||||||
|
* Job Delete handler
|
||||||
|
|
||||||
DAEMON
|
DAEMON
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user