From 949aeb9eeaa3e88c119e6997e6a92364b864322f Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 30 Apr 2023 22:58:42 +0100 Subject: [PATCH] delete hook (#1032) * make log_message optional in delete_device * add hooks support to delete job * make delete job high prio * web delete now queues job instead of inline delete * move web logging into web package and remove userlog from device delete helper * submit delete job for expire device instead of inline delete * fixes to get web submit form for delete device to work * enable delete hook functionality --- lib/App/Netdisco/Util/Device.pm | 9 +--- lib/App/Netdisco/Web/AdminTask.pm | 44 ++++++++++--------- lib/App/Netdisco/Worker/Plugin/Delete.pm | 16 +++++-- .../Netdisco/Worker/Plugin/Delete/Hooks.pm | 31 +++++++++++++ lib/App/Netdisco/Worker/Plugin/Expire.pm | 12 +++-- share/config.yml | 2 + .../views/ajax/admintask/duplicatedevices.tt | 4 +- share/views/ajax/device/details.tt | 4 +- share/views/js/device.js | 2 +- 9 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 lib/App/Netdisco/Worker/Plugin/Delete/Hooks.pm diff --git a/lib/App/Netdisco/Util/Device.pm b/lib/App/Netdisco/Util/Device.pm index 472fc543..3cb3c08b 100644 --- a/lib/App/Netdisco/Util/Device.pm +++ b/lib/App/Netdisco/Util/Device.pm @@ -77,7 +77,7 @@ Returns true if the transaction completes, else returns false. =cut sub delete_device { - my ($ip, $archive, $log) = @_; + my ($ip, $archive) = @_; my $device = get_device($ip) or return 0; return 0 if not $device->in_storage; @@ -87,13 +87,6 @@ sub delete_device { schema(vars->{'tenant'})->resultset('Device') ->search({ ip => $device->ip })->delete({archive_nodes => $archive}); - schema(vars->{'tenant'})->resultset('UserLog')->create({ - username => session('logged_in_user'), - userip => scalar eval {request->remote_address}, - event => (sprintf "Delete device %s", $device->ip), - details => $log, - }); - $happy = 1; }); diff --git a/lib/App/Netdisco/Web/AdminTask.pm b/lib/App/Netdisco/Web/AdminTask.pm index 9eb9322c..7e1f8859 100644 --- a/lib/App/Netdisco/Web/AdminTask.pm +++ b/lib/App/Netdisco/Web/AdminTask.pm @@ -7,10 +7,10 @@ use Dancer::Plugin::Auth::Extensible; use NetAddr::IP qw/:rfc3021 :lower/; use App::Netdisco::JobQueue 'jq_insert'; -use App::Netdisco::Util::Device qw/delete_device renumber_device/; +use App::Netdisco::Util::Device qw/renumber_device/; sub add_job { - my ($action, $device, $subaction) = @_; + my ($action, $device, $extra, $port) = @_; my $net = NetAddr::IP->new($device); return if @@ -18,15 +18,29 @@ sub add_job { my @hostlist = $device ? ($net->hostenum) : (undef); - jq_insert([map {{ - ($_ ? (device => $_->addr) : ()), + my $happy = jq_insert([map {{ action => $action, - ($subaction ? (subaction => $subaction) : ()), + ($_ ? (device => $_->addr) : ()), + ($port ? (port => $port) : ()), + ($extra ? (extra => $extra) : ()), username => session('logged_in_user'), - userip => request->remote_address, + userip => scalar eval {request->remote_address}, }} @hostlist]); - true; + foreach my $h (@hostlist) { + next unless defined $h; + my $msg = ($happy ? "Queued job to $action device \%s" + : "Failed to queue job to $action device \%s"); + + schema(vars->{'tenant'})->resultset('UserLog')->create({ + username => session('logged_in_user'), + userip => scalar eval {request->remote_address}, + event => (sprintf $msg, $h->addr), + details => ($extra || 'no user log supplied'), + }); + } + + return $happy; } foreach my $action (@{ setting('job_prio')->{high} }, @@ -35,12 +49,12 @@ foreach my $action (@{ setting('job_prio')->{high} }, next if $action and $action =~ m/^hook::/; # skip hooks ajax "/ajax/control/admin/$action" => require_role admin => sub { - add_job($action, param('device'), param('extra')) + add_job($action, param('device'), param('extra'), param('port')) or send_error('Bad device', 400); }; post "/admin/$action" => require_role admin => sub { - add_job($action, param('device'), param('extra')) + add_job($action, param('device'), param('extra'), param('port')) ? redirect uri_for('/admin/jobqueue')->path : redirect uri_for('/')->path; }; @@ -61,18 +75,6 @@ ajax qr{/ajax/control/admin/(?:\w+/)?renumber} => require_role setting('defanged return renumber_device( $device->addr, $newip->addr ); }; -ajax qr{/ajax/control/admin/(?:\w+/)?delete} => require_role setting('defanged_admin') => sub { - send_error('Missing device', 400) unless param('device'); - - my $device = NetAddr::IP->new(param('device')); - send_error('Bad device', 400) - if ! $device or $device->addr eq '0.0.0.0'; - - return delete_device( - $device->addr, param('archive'), param('log'), - ); -}; - ajax "/ajax/control/admin/snapshot_req" => require_role admin => sub { my $device = NetAddr::IP->new(param('device')); send_error('Bad device', 400) diff --git a/lib/App/Netdisco/Worker/Plugin/Delete.pm b/lib/App/Netdisco/Worker/Plugin/Delete.pm index 5597b7e2..a1e4e8b6 100644 --- a/lib/App/Netdisco/Worker/Plugin/Delete.pm +++ b/lib/App/Netdisco/Worker/Plugin/Delete.pm @@ -14,11 +14,21 @@ register_worker({ phase => 'check' }, sub { register_worker({ phase => 'main' }, sub { my ($job, $workerconf) = @_; - my ($device, $port, $extra) = map {$job->$_} qw/device port extra/; + my ($device, $port) = map {$job->$_} qw/device port/; + + # support for Hooks + vars->{'hook_data'} = { $device->get_columns }; + delete vars->{'hook_data'}->{'snmp_comm'}; # for privacy $port = ($port ? 1 : 0); - delete_device($device, $port, $extra); - return Status->done("Deleted device: $device"); + my $happy = delete_device($device, $port); + + if ($happy) { + return Status->done("Deleted device: $device") + } + else { + return Status->error("Failed to delete device: $device") + } }); true; diff --git a/lib/App/Netdisco/Worker/Plugin/Delete/Hooks.pm b/lib/App/Netdisco/Worker/Plugin/Delete/Hooks.pm new file mode 100644 index 00000000..062f913f --- /dev/null +++ b/lib/App/Netdisco/Worker/Plugin/Delete/Hooks.pm @@ -0,0 +1,31 @@ +package App::Netdisco::Worker::Plugin::Delete::Hooks; + +use Dancer ':syntax'; +use App::Netdisco::Worker::Plugin; +use aliased 'App::Netdisco::Worker::Status'; + +use App::Netdisco::Util::Worker; +use App::Netdisco::Util::Permission qw/check_acl_no check_acl_only/; + +register_worker({ phase => 'late' }, sub { + my ($job, $workerconf) = @_; + my $count = 0; + + foreach my $conf (@{ setting('hooks') }) { + my $no = ($conf->{'filter'}->{'no'} || []); + my $only = ($conf->{'filter'}->{'only'} || []); + + next if check_acl_no( $job->device, $no ); + next unless check_acl_only( $job->device, $only); + + if ($conf->{'event'} eq 'delete') { + $count += queue_hook('delete', $conf); + debug sprintf ' [%s] hooks - %s queued', 'delete', $job->device; + } + } + + return Status + ->info(sprintf ' [%s] hooks - %d queued', $job->device, $count); +}); + +true; diff --git a/lib/App/Netdisco/Worker/Plugin/Expire.pm b/lib/App/Netdisco/Worker/Plugin/Expire.pm index 03093f48..934ed136 100644 --- a/lib/App/Netdisco/Worker/Plugin/Expire.pm +++ b/lib/App/Netdisco/Worker/Plugin/Expire.pm @@ -5,6 +5,7 @@ use App::Netdisco::Worker::Plugin; use aliased 'App::Netdisco::Worker::Status'; use Dancer::Plugin::DBIC 'schema'; +use App::Netdisco::JobQueue 'jq_insert'; use App::Netdisco::Util::Statistics 'update_stats'; use App::Netdisco::DB::ExplicitLocking ':modes'; @@ -13,11 +14,16 @@ register_worker({ phase => 'main' }, sub { if (setting('expire_devices') and setting('expire_devices') > 0) { schema('netdisco')->txn_do(sub { - schema('netdisco')->resultset('Device')->search({ - -or => [ 'vendor' => undef, 'vendor' => { '!=' => 'netdisco' }], + my @hostlist = schema('netdisco')->resultset('Device')->search({ + -not_bool => 'is_pseudo', last_discover => \[q/< (LOCALTIMESTAMP - ?::interval)/, (setting('expire_devices') * 86400)], - })->delete(); + })->get_column('ip')->all; + + my $happy = jq_insert([map {{ + device => $_, + action => 'delete', + }} @hostlist]); }); } diff --git a/share/config.yml b/share/config.yml index 3a9f8f0a..c5ae8026 100644 --- a/share/config.yml +++ b/share/config.yml @@ -472,6 +472,7 @@ job_prio: - power - snapshot - vlan + - delete normal: - arpnip - arpwalk @@ -493,6 +494,7 @@ worker_plugins: - 'Arpwalk' - 'Contact' - 'Delete' + - 'Delete::Hooks' - 'Discover' - 'Discover::CanonicalIP' - 'Discover::Entities' diff --git a/share/views/ajax/admintask/duplicatedevices.tt b/share/views/ajax/admintask/duplicatedevices.tt index 44315131..9eeececa 100644 --- a/share/views/ajax/admintask/duplicatedevices.tt +++ b/share/views/ajax/admintask/duplicatedevices.tt @@ -51,9 +51,9 @@ + placeholder="Enter a log message" name="extra"> diff --git a/share/views/ajax/device/details.tt b/share/views/ajax/device/details.tt index 77fe3505..d380e4fb 100644 --- a/share/views/ajax/device/details.tt +++ b/share/views/ajax/device/details.tt @@ -243,9 +243,9 @@ + placeholder="Enter a log message" name="extra"> diff --git a/share/views/js/device.js b/share/views/js/device.js index 56b7948d..5031ef93 100644 --- a/share/views/js/device.js +++ b/share/views/js/device.js @@ -176,7 +176,7 @@ } } else { - toastr.success('Deleted device '+ tr.data('for-device')); + toastr.success('Queued job to delete '+ tr.data('for-device')); } } // skip any error reporting for now