Merge of og-work branch, many new features.

Squashed commit of the following:

commit a43c98962a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 20:37:39 2013 +0100

    Missing mibdirs causes all MIBs to be loaded (with a warning)

commit 09829a25b8
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 20:07:31 2013 +0100

    local plugins site_plugins dir

commit b0e804e558
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 19:59:04 2013 +0100

    use send_error and redirect from Dancer

commit 3d1185261a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 19:13:40 2013 +0100

    support path config option

commit 31ca119f84
Merge: 9a79855 4d2b3a5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 00:06:17 2013 +0100

    Merge remote-tracking branch 'origin/og-work' into og-work
    g-work"

    This reverts commit 9a79855361, reversing
    changes made to 6fd6118354.

    Conflicts:
    	Netdisco/share/views/plugin/device_port_column/c_observiumsparklines.tt

commit 9a79855361
Merge: 6fd6118 c8c3b82
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Jun 3 00:03:32 2013 +0100

    Merge remote-tracking branch 'origin/master' into og-work

commit 6fd6118354
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Jun 2 15:47:45 2013 +0100

    extra note about behind proxy

commit 798086ca29
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Jun 2 15:30:26 2013 +0100

    complete the observium plugin

commit 66b3ced179
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun Jun 2 12:48:06 2013 +0100

    Plugins can have CSS and Javascript loaded within <head>

commit 4d2b3a5307
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 08:50:16 2013 +0100

    get device dns to port template

commit ed1bfa1ae7
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 08:17:02 2013 +0100

    observium sparklines plugin; support X:: namespace

commit 76b7636c74
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 06:30:06 2013 +0100

    rename private settings keys

commit fdac8f6c33
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 05:59:53 2013 +0100

    add macwalk and arpnip buttons to device details

commit 3d688c7d83
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 05:57:20 2013 +0100

    Revert "reduce refresh to 5sec"

    This reverts commit 8ea9ec7dd9.

commit dc62382112
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 05:50:34 2013 +0100

    support for arpwalk and macwalk and all jobs via web

commit 8bc7d83c98
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 30 05:35:41 2013 +0100

    simplify discover options to only discoverall and discover

commit 8ea9ec7dd9
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 20:23:08 2013 +0100

    reduce refresh to 5sec

commit 8c54e6c58b
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 20:11:06 2013 +0100

    show undiscovered neighbor properly

commit e0ee25628f
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:54:09 2013 +0100

    avoid unecessary log for queueing

commit d5565423f2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:51:37 2013 +0100

    avoid warning on undefined remote type

commit 5d9b58a6b2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:48:22 2013 +0100

    avoid explosion when not admin

commit 377bb942e0
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:46:52 2013 +0100

    avoid undefined warning

commit 08806dcfa2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:46:42 2013 +0100

    get_db_version will be 0 at first deploy

commit 9511c17056
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 29 19:15:55 2013 +0100

    fix name of Template module

commit eb0288de35
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 28 07:17:07 2013 +0100

    initial config settings documentation

commit 7f2ea7f8dc
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 27 15:18:15 2013 +0100

    remove check_mac to own module, use in macsuck too

commit b995cf6398
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 27 15:01:29 2013 +0100

    show probable but undiscovered neighbor is ports display

commit dd8d461188
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 27 14:52:41 2013 +0100

    new schema version for is_uplink and is_uplink_admin

commit 3f6a7b5aa2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 27 14:47:59 2013 +0100

    make sure device_port is updated when manual_topo is set

commit 33bf9a6599
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 26 19:51:49 2013 +0100

    export store_arp and store_node

commit 0ed356d560
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 25 17:12:31 2013 +0100

    use row lock not table lock

commit f830bc3a3b
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 25 16:38:33 2013 +0100

    move macsuck/arpnip/discover to ::Core namespace

commit be40788987
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri May 24 21:10:34 2013 +0100

    add maybe_uplink to device_port; more macsuck implementation

commit 88371026d5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri May 24 14:34:58 2013 +0100

    start on macsuck; tweak update locking

commit 6f7c87ac07
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri May 24 13:10:58 2013 +0100

    ORDER BY ... FOR UPDATE will allow us to avoid table lock

commit 7c438e01fc
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri May 24 12:12:46 2013 +0100

    yet more efficient arpnip

commit c74c56dc02
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri May 24 11:34:23 2013 +0100

    guard against race with *_or_* DBIC methods

commit d50c54972e
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 20 23:42:41 2013 +0100

    more efficient arpnip

commit 73c8979130
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 19 22:52:15 2013 +0100

    fix confusing name

commit bf78e82411
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 19 22:37:22 2013 +0100

    fix mistake in DBIx::Class schema

commit 6a5af95836
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 19 22:06:27 2013 +0100

    arpnip implementation

commit 594abd3f82
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 16 00:00:50 2013 +0100

    PostgreSQL explicit locking support.

    Squashed commit of the following:

    commit 76e1539102
    Author: Oliver Gorwits <oliver@cpan.org>
    Date:   Wed May 15 23:54:25 2013 +0100

        finished explicit locking module

    commit 369387258b
    Author: Oliver Gorwits <oliver@cpan.org>
    Date:   Tue May 14 23:50:42 2013 +0100

        initial implementation of locking from schema object

commit 55c6d4fe63
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 14 21:05:01 2013 +0100

    add discover button to device details page

commit 11fd8bf964
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 14 20:43:43 2013 +0100

    fix typo and clear port box on autocomplete dropdown

commit a00f9b5c2e
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 14 20:38:54 2013 +0100

    move admin tasks and remove JobControl package

commit 74bc0023df
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 11 18:25:04 2013 +0100

    complete job queue delete and kill running timers properly when reloading page

commit dd6947f38d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 11 16:51:28 2013 +0100

    fix improper use of bootstrap table class

commit cd5b83f71e
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 11 15:55:45 2013 +0100

    fix update view icon in sidebar

commit e9349f325d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat May 11 11:57:19 2013 +0100

    css audit

commit 201470275d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 23:48:05 2013 +0100

    add job queue to standard plugins list

commit a18a3c72a3
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 23:37:43 2013 +0100

    fix table headings and improve Action display in Job Queue

commit 70f5da8bb6
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 23:30:32 2013 +0100

    implement "no devices" prompt for admin users to do first discover

commit 2e8ac83173
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 21:53:39 2013 +0100

    more js refactoring for report and search

commit 479ac0e55d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 21:50:29 2013 +0100

    refactor js for device tabs

commit 6a17fe5d6c
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Thu May 9 21:05:42 2013 +0100

    fix crazy races with javasacript by using global delegations

commit e94e3cef3b
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Wed May 8 23:06:41 2013 +0100

    remove Try::Tiny from web runtime

commit c746e68b9b
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 21:54:11 2013 +0100

    make topo autocomplete more responsive

commit 24c511786f
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 21:52:17 2013 +0100

    display name and IP for device typeahead

commit 52ab7d1266
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 21:47:05 2013 +0100

    add drop-down control for the topo form fields

commit 5744b6845f
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 21:25:30 2013 +0100

    complete the topology editor (add/delete)

commit b510fbe8c5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 00:59:11 2013 +0100

    add new admin tasks to default plugins list

commit 11d55e0129
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Tue May 7 00:56:19 2013 +0100

    Manual Device Topology

    Needed to add the 'autocomplete' jQuery UI component because
    it can do minLength=0 properly. Used the smoothness UI theme.

    Added typeahead AJAX calls to support the topology searching.

    Added new plugin and template for the topology editing page.

commit bf7a419d08
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 6 22:16:24 2013 +0100

    add a little colour to lone tab titles

commit 9690a31f19
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 6 22:01:13 2013 +0100

    complete Manage Pseudo Devices

commit 024f4d9a83
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 6 00:49:47 2013 +0100

    use bootstrap font colour instead of css

commit f75f1e5cbf
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon May 6 00:45:18 2013 +0100

    add frontend update/del forms, and display port count

commit f0899e16b3
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 23:53:20 2013 +0100

    add frontend pseudo device add form

commit 3271c01931
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 21:45:17 2013 +0100

    complete the code for admin tasks page loading

commit 38f70624f3
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 17:04:30 2013 +0100

    set up file paths consistently in all scripts

commit c761ca839b
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 17:00:30 2013 +0100

    Helper script to import the Netdisco 1.x Topology file to the database

commit f468b48049
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 16:20:39 2013 +0100

    Handle whitespace ahead of OUI data

commit 5c8a5754f6
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 16:16:20 2013 +0100

    also set neighbor info when discovering device interfaces

commit acb988b6af
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 15:34:20 2013 +0100

    try to avoid duplicate execution of scheduled jobs

commit c6bcaf66c5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 14:16:25 2013 +0100

    do not clobber manual topo when discovering neighbors

commit d9a6a1882a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sun May 5 13:02:45 2013 +0100

    User icon color indicates port_control/admin ability

commit 2cdcb9db7e
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Apr 29 23:34:27 2013 +0100

    add support for admin tasks as plugins

commit 075a770c9a
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Apr 29 22:23:20 2013 +0100

    skip pseudo devices (vendor netdisco)

commit 045c022d42
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Mon Apr 29 21:58:33 2013 +0100

    incorporate manual topo info from the topology db table

commit 09285d42b4
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 18:39:12 2013 +0100

    add unique constraints to topology table

commit 2780b72e49
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 15:38:05 2013 +0100

    muted help text in sidebar

commit 733d4f83fb
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:39:54 2013 +0100

    sorry, testing hook changes

commit 71e366e352
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:34:36 2013 +0100

    sorry, testing hook changes

commit 7f9eaa99f5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:33:44 2013 +0100

    sorry, testing hook changes

commit 5215fd632d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:30:07 2013 +0100

    sorry, testing hook changes

commit be817d60c2
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:21:45 2013 +0100

    sorry, testing hook changes

commit 1fd3695358
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:18:57 2013 +0100

    sorry, testing hook changes

commit ac448c4a91
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:13:03 2013 +0100

    sorry, testing hook changes

commit c563b8d9af
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:08:54 2013 +0100

    sorry, testing hook changes

commit 3abcfb01d5
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:06:25 2013 +0100

    sorry, testing hook changes

commit 877a81facf
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Sat Apr 27 14:05:25 2013 +0100

    sorry, testing hook changes
This commit is contained in:
Oliver Gorwits
2013-06-03 20:38:33 +01:00
parent c8c3b82238
commit 4d0e2461f5
119 changed files with 3524 additions and 778 deletions

View File

@@ -0,0 +1,80 @@
package App::Netdisco::Web::AdminTask;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use Try::Tiny;
sub add_job {
my ($jobtype, $device) = @_;
if ($device) {
$device = NetAddr::IP::Lite->new($device);
return send_error('Bad device', 400)
if ! $device or $device->addr eq '0.0.0.0';
}
try {
# job might already be in the queue, so this could die
schema('netdisco')->resultset('Admin')->create({
($device ? (device => $device->addr) : ()),
action => $jobtype,
status => 'queued',
username => session('user'),
userip => request->remote_address,
});
};
}
# we have a separate list for jobs needing a device to avoid queueing
# such a job when there's no device param (it could still be duff, tho).
my %jobs = map { $_ => 1} qw/
discover
macsuck
arpnip
/;
my %jobs_all = map {$_ => 1} qw/
discoverall
macwalk
arpwalk
/;
foreach my $jobtype (keys %jobs_all, keys %jobs) {
ajax "/ajax/control/admin/$jobtype" => sub {
send_error('Forbidden', 403)
unless var('user')->admin;
send_error('Missing device', 400)
if exists $jobs{$jobtype} and not param('device');
add_job($jobtype, param('device'));
};
post "/admin/$jobtype" => sub {
send_error('Forbidden', 403)
unless var('user')->admin;
send_error('Missing device', 400)
if exists $jobs{$jobtype} and not param('device');
add_job($jobtype, param('device'));
redirect uri_for('/admin/jobqueue')->path_query;
};
}
get '/admin/*' => sub {
my ($tag) = splat;
if (! eval { var('user')->admin }) {
return redirect uri_for('/')->path_query;
}
# trick the ajax into working as if this were a tabbed page
params->{tab} = $tag;
var(nav => 'admin');
template 'admintask', {
task => setting('_admin_tasks')->{ $tag },
};
};
true;

View File

@@ -18,30 +18,31 @@ hook 'before' => sub {
if (session('user') && session->id) {
var(user => schema('netdisco')->resultset('User')
->find(session('user')));
# really just for dev work, to quieten the logs
var('user')->port_control(0) if setting('no_port_control');
}
};
post '/login' => sub {
status(302);
if (param('username') and param('password')) {
my $user = schema('netdisco')->resultset('User')->find(param('username'));
if ($user) {
my $sum = Digest::MD5::md5_hex(param('password'));
if (($sum and $user->password) and ($sum eq $user->password)) {
session(user => $user->username);
header(Location => uri_for('/inventory')->path_query());
return;
return redirect uri_for('/inventory')->path_query;
}
}
}
header(Location => uri_for('/', {failed => 1})->path_query());
redirect uri_for('/', {failed => 1})->path_query;
};
get '/logout' => sub {
session->destroy;
status(302);
header(Location => uri_for('/', {logout => 1})->path_query());
redirect uri_for('/', {logout => 1})->path_query;
};
true;

View File

@@ -5,10 +5,12 @@ use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
hook 'before' => sub {
# list of port detail columns
var('port_columns' => [
{ name => 'c_admin', label => 'Admin Controls', default => '' },
my @default_port_columns_left = (
{ name => 'c_admin', label => 'Port Controls', default => '' },
{ name => 'c_port', label => 'Port', default => 'on' },
);
my @default_port_columns_right = (
{ name => 'c_descr', label => 'Description', default => '' },
{ name => 'c_type', label => 'Type', default => '' },
{ name => 'c_duplex', label => 'Duplex', default => '' },
@@ -24,7 +26,21 @@ hook 'before' => sub {
{ name => 'c_neighbors', label => 'Connected Devices', default => 'on' },
{ name => 'c_stp', label => 'Spanning Tree', default => '' },
{ name => 'c_up', label => 'Status', default => '' },
]);
);
# build list of port detail columns
my @port_columns = ();
push @port_columns,
grep {$_->{position} eq 'left'} @{ setting('_extra_device_port_cols') };
push @port_columns, @default_port_columns_left;
push @port_columns,
grep {$_->{position} eq 'mid'} @{ setting('_extra_device_port_cols') };
push @port_columns, @default_port_columns_right;
push @port_columns,
grep {$_->{position} eq 'right'} @{ setting('_extra_device_port_cols') };
var('port_columns' => \@port_columns);
# view settings for port connected devices
var('connected_properties' => [
@@ -100,9 +116,7 @@ get '/device' => sub {
});
if (!defined $dev) {
status(302);
header(Location => uri_for('/', {nosuchdevice => 1})->path_query());
return;
return redirect uri_for('/', {nosuchdevice => 1})->path_query;
}
params->{'tab'} ||= 'details';

View File

@@ -4,12 +4,16 @@ use Dancer ':syntax';
use Dancer::Plugin;
set(
'navbar_items' => [],
'search_tabs' => [],
'device_tabs' => [],
'reports_menu' => {},
'reports' => {},
'report_order' => [qw/Device Port Node VLAN Network Wireless/],
'_additional_css' => [],
'_additional_javascript' => [],
'_extra_device_port_cols' => [],
'_navbar_items' => [],
'_search_tabs' => [],
'_device_tabs' => [],
'_admin_tasks' => {},
'_reports_menu' => {},
'_reports' => {},
'_report_order' => [qw/Device Port Node VLAN Network Wireless/],
);
# this is what Dancer::Template::TemplateToolkit does by default
@@ -19,8 +23,7 @@ register 'register_template_path' => sub {
my ($self, $path) = plugin_args(@_);
if (!length $path) {
error "bad template path to register_template_paths";
return;
return error "bad template path to register_template_paths";
}
unshift
@@ -28,6 +31,49 @@ register 'register_template_path' => sub {
$path;
};
sub _register_include {
my ($type, $plugin) = @_;
if (!length $type) {
return error "bad type to _register_include";
}
if (!length $plugin) {
return error "bad plugin name to register_$type";
}
push @{ setting("_additional_$type") }, $plugin;
}
register 'register_css' => sub {
my ($self, $plugin) = plugin_args(@_);
_register_include('css', $plugin);
};
register 'register_javascript' => sub {
my ($self, $plugin) = plugin_args(@_);
_register_include('javascript', $plugin);
};
register 'register_device_port_column' => sub {
my ($self, $config) = plugin_args(@_);
$config->{default} ||= '';
$config->{position} ||= 'right';
if (!length $config->{name} or !length $config->{label}) {
return error "bad config to register_device_port_column";
}
foreach my $item (@{ setting('_extra_device_port_cols') }) {
if ($item->{name} eq $config->{name}) {
$item = $config;
return;
}
}
push @{ setting('_extra_device_port_cols') }, $config;
};
register 'register_navbar_item' => sub {
my ($self, $config) = plugin_args(@_);
@@ -35,29 +81,39 @@ register 'register_navbar_item' => sub {
or !length $config->{path}
or !length $config->{label}) {
error "bad config to register_navbar_item";
return;
return error "bad config to register_navbar_item";
}
foreach my $item (@{ setting('navbar_items') }) {
foreach my $item (@{ setting('_navbar_items') }) {
if ($item->{tag} eq $config->{tag}) {
$item = $config;
return;
}
}
push @{ setting('navbar_items') }, $config;
push @{ setting('_navbar_items') }, $config;
};
sub _register_tab {
my ($nav, $config) = @_;
my $stash = setting("${nav}_tabs");
register 'register_admin_task' => sub {
my ($self, $config) = plugin_args(@_);
if (!length $config->{tag}
or !length $config->{label}) {
error "bad config to register_${nav}_item";
return;
return error "bad config to register_admin_task";
}
setting('_admin_tasks')->{ $config->{tag} } = $config;
};
sub _register_tab {
my ($nav, $config) = @_;
my $stash = setting("_${nav}_tabs");
if (!length $config->{tag}
or !length $config->{label}) {
return error "bad config to register_${nav}_item";
}
foreach my $item (@{ $stash }) {
@@ -82,26 +138,25 @@ register 'register_device_tab' => sub {
register 'register_report' => sub {
my ($self, $config) = plugin_args(@_);
my @categories = @{ setting('report_order') };
my @categories = @{ setting('_report_order') };
if (!length $config->{category}
or !length $config->{tag}
or !length $config->{label}
or 0 == scalar grep {$config->{category} eq $_} @categories) {
error "bad config to register_report";
return;
return error "bad config to register_report";
}
foreach my $item (@{setting('reports_menu')->{ $config->{category} }}) {
foreach my $item (@{setting('_reports_menu')->{ $config->{category} }}) {
if ($item eq $config->{tag}) {
setting('reports')->{$config->{tag}} = $config;
setting('_reports')->{$config->{tag}} = $config;
return;
}
}
push @{setting('reports_menu')->{ $config->{category} }}, $config->{tag};
setting('reports')->{$config->{tag}} = $config;
push @{setting('_reports_menu')->{ $config->{category} }}, $config->{tag};
setting('_reports')->{$config->{tag}} = $config;
};
register_plugin;

View File

@@ -0,0 +1,37 @@
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/control/admin/jobqueue/del' => sub {
send_error('Forbidden', 403) unless var('user')->admin;
send_error('Missing job', 400) unless length param('job');
schema('netdisco')->txn_do(sub {
my $device = schema('netdisco')->resultset('Admin')
->search({job => param('job')})->delete;
});
};
ajax '/ajax/content/admin/jobqueue' => sub {
send_error('Forbidden', 403) unless 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;

View File

@@ -0,0 +1,103 @@
package App::Netdisco::Web::Plugin::AdminTask::PseudoDevice;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use App::Netdisco::Web::Plugin;
use NetAddr::IP::Lite ':lower';
register_admin_task({
tag => 'pseudodevice',
label => 'Pseudo Devices',
});
sub _sanity_ok {
return 0 unless var('user') and var('user')->admin;
return 0 unless length param('dns')
and param('dns') =~ m/^[[:print:]]+$/
and param('dns') !~ m/[[:space:]]/;
my $ip = NetAddr::IP::Lite->new(param('ip'));
return 0 unless ($ip and$ip->addr ne '0.0.0.0');
return 0 unless length param('ports')
and param('ports') =~ m/^[[:digit:]]+$/;
return 1;
}
ajax '/ajax/control/admin/pseudodevice/add' => sub {
send_error('Bad Request', 400) unless _sanity_ok();
schema('netdisco')->txn_do(sub {
my $device = schema('netdisco')->resultset('Device')
->create({
ip => param('ip'),
dns => param('dns'),
vendor => 'netdisco',
last_discover => \'now()',
});
return unless $device;
$device->ports->populate([
['port'],
map {["Port$_"]} @{[1 .. param('ports')]},
]);
});
};
ajax '/ajax/control/admin/pseudodevice/del' => sub {
send_error('Bad Request', 400) unless _sanity_ok();
schema('netdisco')->txn_do(sub {
my $device = schema('netdisco')->resultset('Device')
->find({ip => param('ip')});
$device->ports->delete;
$device->delete;
});
};
ajax '/ajax/control/admin/pseudodevice/update' => sub {
send_error('Bad Request', 400) unless _sanity_ok();
schema('netdisco')->txn_do(sub {
my $device = schema('netdisco')->resultset('Device')
->with_port_count->find({ip => param('ip')});
return unless $device;
my $count = $device->port_count;
if (param('ports') > $count) {
my $start = $count + 1;
$device->ports->populate([
['port'],
map {["Port$_"]} @{[$start .. param('ports')]},
]);
}
elsif (param('ports') < $count) {
my $start = param('ports') + 1;
$device->ports
->single({port => "Port$_"})->delete
for ($start .. $count);
}
});
};
ajax '/ajax/content/admin/pseudodevice' => sub {
send_error('Forbidden', 403) unless var('user')->admin;
my $set = schema('netdisco')->resultset('Device')
->search(
{vendor => 'netdisco'},
{order_by => { -desc => 'last_discover' }},
)->with_port_count;
content_type('text/html');
template 'ajax/admintask/pseudodevice.tt', {
results => $set,
}, { layout => undef };
};
true;

View File

@@ -0,0 +1,103 @@
package App::Netdisco::Web::Plugin::AdminTask::Topology;
use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use App::Netdisco::Web::Plugin;
use NetAddr::IP::Lite ':lower';
register_admin_task({
tag => 'topology',
label => 'Manual Device Topology',
});
sub _sanity_ok {
return 0 unless var('user') and var('user')->admin;
my $dev1 = NetAddr::IP::Lite->new(param('dev1'));
return 0 unless ($dev1 and $dev1->addr ne '0.0.0.0');
my $dev2 = NetAddr::IP::Lite->new(param('dev2'));
return 0 unless ($dev2 and $dev2->addr ne '0.0.0.0');
return 0 unless length param('port1');
return 0 unless length param('port2');
return 1;
}
ajax '/ajax/control/admin/topology/add' => sub {
send_error('Bad Request', 400) unless _sanity_ok();
my $device = schema('netdisco')->resultset('Topology')
->create({
dev1 => param('dev1'),
port1 => param('port1'),
dev2 => param('dev2'),
port2 => param('port2'),
});
# re-set remote device details in affected ports
# could fail for bad device or port names
try {
schema('netdisco')->txn_do(sub {
# only work on root_ips
my $left = get_device(param('dev1'));
my $right = get_device(param('dev2'));
# skip bad entries
return unless ($left->in_storage and $right->in_storage);
$left->ports
->single({port => param('port1')}, {for => 'update'})
->update({
remote_ip => param('dev2'),
remote_port => param('port2'),
remote_type => undef,
remote_id => undef,
is_uplink => \"true",
manual_topo => \"true",
});
$right->ports
->single({port => param('port2')}, {for => 'update'})
->update({
remote_ip => param('dev1'),
remote_port => param('port1'),
remote_type => undef,
remote_id => undef,
is_uplink => \"true",
manual_topo => \"true",
});
});
};
};
ajax '/ajax/control/admin/topology/del' => sub {
send_error('Bad Request', 400) unless _sanity_ok();
schema('netdisco')->txn_do(sub {
my $device = schema('netdisco')->resultset('Topology')
->search({
dev1 => param('dev1'),
port1 => param('port1'),
dev2 => param('dev2'),
port2 => param('port2'),
})->delete;
});
};
ajax '/ajax/content/admin/topology' => sub {
send_error('Forbidden', 403) unless var('user')->admin;
my $set = schema('netdisco')->resultset('Topology')
->search({},{order_by => [qw/dev1 dev2 port1/]});
content_type('text/html');
template 'ajax/admintask/topology.tt', {
results => $set,
}, { layout => undef };
};
true;

View File

@@ -13,7 +13,7 @@ ajax '/ajax/content/device/addresses' => sub {
my $q = param('q');
my $device = schema('netdisco')->resultset('Device')
->search_for_device($q) or return;
->search_for_device($q) or send_error('Bad device', 400);
my $set = $device->device_ips->search({}, {order_by => 'alias'});
return unless $set->count;

View File

@@ -12,7 +12,7 @@ register_device_tab({ tag => 'details', label => 'Details' });
ajax '/ajax/content/device/details' => sub {
my $q = param('q');
my $device = schema('netdisco')->resultset('Device')
->with_times()->search_for_device($q) or return;
->with_times()->search_for_device($q) or send_error('Bad device', 400);
content_type('text/html');
template 'ajax/device/details.tt', {

View File

@@ -43,7 +43,7 @@ get '/ajax/data/device/netmap' => sub {
my $q = param('q');
my $device = schema('netdisco')->resultset('Device')
->search_for_device($q) or return;
->search_for_device($q) or send_error('Bad device', 400);
my $start = $device->ip;
my @devices = schema('netdisco')->resultset('Device')->search({}, {
@@ -72,7 +72,7 @@ get '/ajax/data/device/netmap' => sub {
_add_children($tree{children}, var('links')->{$start});
content_type('application/json');
return to_json(\%tree);
to_json(\%tree);
};
ajax '/ajax/data/device/alldevicelinks' => sub {
@@ -93,7 +93,7 @@ ajax '/ajax/data/device/alldevicelinks' => sub {
}
content_type('application/json');
return to_json(\%tree);
to_json(\%tree);
};
true;

View File

@@ -14,7 +14,7 @@ ajax '/ajax/content/device/ports' => sub {
my $q = param('q');
my $device = schema('netdisco')->resultset('Device')
->search_for_device($q) or return;
->search_for_device($q) or send_error('Bad device', 400);
my $set = $device->ports;
# refine by ports if requested
@@ -77,7 +77,7 @@ ajax '/ajax/content/device/ports' => sub {
template 'ajax/device/ports.tt', {
results => $results,
nodes => $nodes_name,
device => $device->ip,
device => $device,
}, { layout => undef };
};

View File

@@ -21,7 +21,7 @@ ajax '/ajax/content/search/device' => sub {
}
else {
my $q = param('q');
return unless $q;
send_error('Missing query', 400) unless $q;
$set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
}

View File

@@ -14,7 +14,7 @@ register_search_tab({ tag => 'node', label => 'Node' });
# nodes matching the param as an IP or DNS hostname or MAC
ajax '/ajax/content/search/node' => sub {
my $node = param('q');
return unless $node;
send_error('Missing node', 400) unless $node;
content_type('text/html');
my $mac = Net::MAC->new(mac => $node, 'die' => 0, verbose => 0);

View File

@@ -11,7 +11,7 @@ register_search_tab({ tag => 'port', label => 'Port' });
# device ports with a description (er, name) matching
ajax '/ajax/content/search/port' => sub {
my $q = param('q');
return unless $q;
send_error('Missing query', 400) unless $q;
my $set;
if ($q =~ m/^\d+$/) {

View File

@@ -11,7 +11,7 @@ register_search_tab({ tag => 'vlan', label => 'VLAN' });
# devices carrying vlan xxx
ajax '/ajax/content/search/vlan' => sub {
my $q = param('q');
return unless $q;
send_error('Missing query', 400) unless $q;
my $set;
if ($q =~ m/^\d+$/) {

View File

@@ -4,42 +4,43 @@ use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
use Try::Tiny;
ajax '/ajax/portcontrol' => sub {
try {
my $log = sprintf 'd:[%s] p:[%s] f:[%s]. a:[%s] v[%s]',
param('device'), (param('port') || ''), param('field'),
(param('action') || ''), (param('value') || '');
send_error('Forbidden', 403)
unless var('user')->port_control;
send_error('No device/port/field', 400)
unless param('device') and param('port') and param('field');
my %action_map = (
'location' => 'location',
'contact' => 'contact',
'c_port' => 'portcontrol',
'c_name' => 'portname',
'c_vlan' => 'vlan',
'c_power' => 'power',
);
my $log = sprintf 'd:[%s] p:[%s] f:[%s]. a:[%s] v[%s]',
param('device'), (param('port') || ''), param('field'),
(param('action') || ''), (param('value') || '');
my $action = $action_map{ param('field') };
my $subaction = ($action =~ m/^(?:power|portcontrol)/
? (param('action') ."-other")
: param('value'));
my %action_map = (
'location' => 'location',
'contact' => 'contact',
'c_port' => 'portcontrol',
'c_name' => 'portname',
'c_vlan' => 'vlan',
'c_power' => 'power',
);
schema('netdisco')->resultset('Admin')->create({
device => param('device'),
port => param('port'),
action => $action,
subaction => $subaction,
status => 'queued',
username => session('user'),
userip => request->remote_address,
log => $log,
});
}
catch {
send_error('Failed to parse params or add DB record');
};
send_error('No action/value', 400)
unless (param('action') or param('value'));
my $action = $action_map{ param('field') };
my $subaction = ($action =~ m/^(?:power|portcontrol)/
? (param('action') ."-other")
: param('value'));
schema('netdisco')->resultset('Admin')->create({
device => param('device'),
port => param('port'),
action => $action,
subaction => $subaction,
status => 'queued',
username => session('user'),
userip => request->remote_address,
log => $log,
});
content_type('application/json');
to_json({});
@@ -47,7 +48,7 @@ ajax '/ajax/portcontrol' => sub {
ajax '/ajax/userlog' => sub {
my $user = session('user');
send_error('No username') unless $user;
send_error('No username', 400) unless $user;
my $rs = schema('netdisco')->resultset('Admin')->search({
username => $user,

View File

@@ -10,7 +10,7 @@ get '/report/*' => sub {
var(nav => 'reports');
template 'report', {
report => setting('reports')->{ $tag },
report => setting('_reports')->{ $tag },
};
};

View File

@@ -65,9 +65,7 @@ get '/search' => sub {
if (not param('tab')) {
if (not $q) {
status(302);
header(Location => uri_for('/')->path_query());
return;
return redirect uri_for('/')->path_query;
}
# pick most likely tab for initial results
@@ -80,13 +78,11 @@ get '/search' => sub {
if ($nd and $nd->count) {
if ($nd->count == 1) {
# redirect to device details for the one device
status(302);
header(Location => uri_for('/device', {
return redirect uri_for('/device', {
tab => 'details',
q => ($nd->first->dns || $nd->first->ip),
f => '',
})->path_query());
return;
})->path_query;
}
# multiple devices

View File

@@ -0,0 +1,30 @@
package App::Netdisco::Web::Static;
use Dancer ':syntax';
use Path::Class;
get '/plugin/*/*.js' => sub {
my ($plugin) = splat;
my $content = template
"plugin/$plugin/$plugin.js", {},
{ layout => undef };
send_file \$content,
content_type => 'application/javascript',
filename => "$plugin.js";
};
get '/plugin/*/*.css' => sub {
my ($plugin) = splat;
my $content = template
"plugin/$plugin/$plugin.css", {},
{ layout => undef };
send_file \$content,
content_type => 'text/css',
filename => "$plugin.css";
};
true;

View File

@@ -4,13 +4,51 @@ use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC;
# support typeahead with simple AJAX query for device names
ajax '/ajax/data/device/typeahead' => sub {
my $q = param('query');
use App::Netdisco::Util::Web (); # for sort_port
ajax '/ajax/data/devicename/typeahead' => sub {
my $q = param('query') || param('term');
my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
content_type 'application/json';
return to_json [map {$_->dns || $_->name || $_->ip} $set->all];
to_json [map {$_->dns || $_->name || $_->ip} $set->all];
};
ajax '/ajax/data/deviceip/typeahead' => sub {
my $q = param('query') || param('term');
my $set = schema('netdisco')->resultset('Device')->search_fuzzy($q);
my @data = ();
while (my $d = $set->next) {
my $label = $d->ip;
if ($d->dns or $d->name) {
$label = sprintf '%s (%s)',
($d->dns || $d->name), $d->ip;
}
push @data, {label => $label, value => $d->ip};
}
content_type 'application/json';
to_json \@data;
};
ajax '/ajax/data/port/typeahead' => sub {
my $dev = param('dev1') || param('dev2');
my $port = param('port1') || param('port2');
send_error('Missing device', 400) unless length $dev;
my $device = schema('netdisco')->resultset('Device')
->find({ip => $dev});
send_error('Bad device', 400) unless $device;
my $set = $device->ports({},{order_by => 'port'});
$set = $set->search({port => { -ilike => "\%$port\%" }})
if length $port;
my $results = [ sort { &App::Netdisco::Util::Web::sort_port($a->port, $b->port) } $set->all ];
content_type 'application/json';
to_json [map {$_->port} @$results];
};
true;