X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FAPI2%2FTasks.pm;h=73e398eb5117bb44486b70b83ae9d9b3626d49ca;hb=refs%2Fheads%2Fstable-5;hp=1b83f2cef76c7ed2574b1084c032cf28b8b426cc;hpb=c5693297d3749c2dcfc386e50f7bb3f2311b59a4;p=pve-manager.git diff --git a/PVE/API2/Tasks.pm b/PVE/API2/Tasks.pm index 1b83f2ce..73e398eb 100644 --- a/PVE/API2/Tasks.pm +++ b/PVE/API2/Tasks.pm @@ -11,15 +11,19 @@ use PVE::RESTHandler; use PVE::ProcFSTools; use PVE::RPCEnvironment; use PVE::JSONSchema qw(get_standard_option); +use PVE::Exception qw(raise_param_exc); use PVE::AccessControl; use base qw(PVE::RESTHandler); __PACKAGE__->register_method({ - name => 'node_tasks', - path => '', + name => 'node_tasks', + path => '', method => 'GET', - permissions => { user => 'all' }, + permissions => { + description => "List task associated with the current user, or all task the user has 'Sys.Audit' permissions on /nodes/ (the the task runs on).", + user => 'all' + }, description => "Read task list for one node (finished tasks).", proxyto => 'node', parameters => { @@ -29,21 +33,43 @@ __PACKAGE__->register_method({ start => { type => 'integer', minimum => 0, + default => 0, optional => 1, + description => "List tasks beginning from this offset.", }, limit => { type => 'integer', minimum => 0, + default => 50, optional => 1, + description => "Only list this amount of tasks.", }, userfilter => { type => 'string', optional => 1, + description => "Only list tasks from this user.", }, + typefilter => { + type => 'string', + optional => 1, + description => 'Only list tasks of this type (e.g., vzstart, vzdump).', + }, + vmid => get_standard_option('pve-vmid', { + description => "Only list tasks for this VM.", + optional => 1, + }), errors => { type => 'boolean', + default => 0, optional => 1, }, + source => { + type => 'string', + enum => ['archive', 'active', 'all'], + default => 'archive', + optional => 1, + description => 'List archived, active or all tasks.', + }, }, }, returns => { @@ -51,7 +77,16 @@ __PACKAGE__->register_method({ items => { type => "object", properties => { - upid => { type => 'string' }, + upid => { type => 'string', title => 'UPID', }, + node => { type => 'string', title => 'Node', }, + pid => { type => 'integer', title => 'PID', }, + pstart => { type => 'integer', }, + starttime => { type => 'integer', title => 'Starttime', }, + type => { type => 'string', title => 'Type', }, + id => { type => 'string', title => 'ID', }, + user => { type => 'string', title => 'User', }, + endtime => { type => 'integer', optional => 1, title => 'Endtime', }, + status => { type => 'string', optional => 1, title => 'Status', }, }, }, links => [ { rel => 'child', href => "{upid}" } ], @@ -67,15 +102,34 @@ __PACKAGE__->register_method({ my $filename = "/var/log/pve/tasks/index"; my $node = $param->{node}; - my $start = $param->{start} || 0; - my $limit = $param->{limit} || 50; + my $start = $param->{start} // 0; + my $limit = $param->{limit} // 50; my $userfilter = $param->{userfilter}; - my $errors = $param->{errors}; + my $typefilter = $param->{typefilter}; + my $errors = $param->{errors} // 0; + my $source = $param->{source} // 'archive'; my $count = 0; my $line; - my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]); + my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ], 1); + + my $filter_task = sub { + my $task = shift; + + return 1 if $userfilter && $task->{user} !~ m/\Q$userfilter\E/i; + return 1 if !($auditor || $user eq $task->{user}); + + return 1 if $typefilter && $task->{type} ne $typefilter; + + return 1 if $errors && $task->{status} && $task->{status} eq 'OK'; + return 1 if $param->{vmid} && (!$task->{id} || $task->{id} ne $param->{vmid}); + + return 1 if $count++ < $start; + return 1 if $limit <= 0; + + return 0; + }; my $parse_line = sub { if ($line =~ m/^(\S+)(\s([0-9A-Za-z]{8})(\s(\S.*))?)?$/) { @@ -83,44 +137,54 @@ __PACKAGE__->register_method({ my $endtime = $3; my $status = $5; if ((my $task = PVE::Tools::upid_decode($upid, 1))) { - return if $userfilter && $task->{user} !~ m/\Q$userfilter\E/i; - next if !($auditor || $user eq $task->{user}); - - return if $errors && $status && $status eq 'OK'; - - return if $count++ < $start; - return if $limit <= 0; $task->{upid} = $upid; $task->{endtime} = hex($endtime) if $endtime; $task->{status} = $status if $status; - push @$res, $task; - $limit--; + + if (!$filter_task->($task)) { + push @$res, $task; + $limit--; + } } } }; - if (my $bw = File::ReadBackwards->new($filename)) { - while (defined ($line = $bw->readline)) { - &$parse_line(); + if ($source eq 'active' || $source eq 'all') { + my $recent_tasks = PVE::INotify::read_file('active'); + for my $task (@$recent_tasks) { + next if $task->{saved}; # archived task, already in index(.1) + if (!$filter_task->($task)) { + $task->{status} = 'RUNNING' if !$task->{status}; # otherwise it would be archived + push @$res, $task; + $limit--; + } } - $bw->close(); } - if (my $bw = File::ReadBackwards->new("$filename.1")) { - while (defined ($line = $bw->readline)) { - &$parse_line(); + + if ($source ne 'active') { + if (my $bw = File::ReadBackwards->new($filename)) { + while (defined ($line = $bw->readline)) { + &$parse_line(); + } + $bw->close(); + } + if (my $bw = File::ReadBackwards->new("$filename.1")) { + while (defined ($line = $bw->readline)) { + &$parse_line(); + } + $bw->close(); } - $bw->close(); } - $rpcenv->set_result_count($count); + $rpcenv->set_result_attrib('total', $count); return $res; }}); __PACKAGE__->register_method({ - name => 'upid_index', - path => '{upid}', + name => 'upid_index', + path => '{upid}', method => 'GET', description => '', # index helper permissions => { user => 'all' }, @@ -149,11 +213,14 @@ __PACKAGE__->register_method({ }}); __PACKAGE__->register_method({ - name => 'stop_task', - path => '{upid}', + name => 'stop_task', + path => '{upid}', method => 'DELETE', description => 'Stop a task.', - permissions => { user => 'all' }, + permissions => { + description => "The user needs 'Sys.Modify' permissions on '/nodes/' if the task does not belong to him.", + user => 'all', + }, protected => 1, proxyto => 'node', parameters => { @@ -175,20 +242,23 @@ __PACKAGE__->register_method({ my $user = $rpcenv->get_user(); my $node = $param->{node}; - my $sysadmin = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Console' ]); - die "Permission check failed\n" - if !($sysadmin || $user eq $task->{user}); + if ($user ne $task->{user}) { + $rpcenv->check($user, "/nodes/$node", [ 'Sys.Modify' ]); + } - PVE::RPCEnvironment::check_worker($param->{upid}, 1); + PVE::RPCEnvironment->check_worker($param->{upid}, 1); return undef; }}); __PACKAGE__->register_method({ - name => 'read_task_log', - path => '{upid}/log', + name => 'read_task_log', + path => '{upid}/log', method => 'GET', - permissions => { user => 'all' }, + permissions => { + description => "The user needs 'Sys.Audit' permissions on '/nodes/' if the task does not belong to him.", + user => 'all', + }, protected => 1, description => "Read task log.", proxyto => 'node', @@ -200,18 +270,20 @@ __PACKAGE__->register_method({ start => { type => 'integer', minimum => 0, + default => 0, optional => 1, }, limit => { type => 'integer', minimum => 0, + default => 50, optional => 1, }, }, }, returns => { type => 'array', - items => { + items => { type => "object", properties => { n => { @@ -231,50 +303,34 @@ __PACKAGE__->register_method({ my ($task, $filename) = PVE::Tools::upid_decode($param->{upid}, 1); raise_param_exc({ upid => "unable to parse worker upid" }) if !$task; - my $lines = []; - my $rpcenv = PVE::RPCEnvironment::get(); my $user = $rpcenv->get_user(); my $node = $param->{node}; + my $start = $param->{start} // 0; + my $limit = $param->{limit} // 50; - my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]); - die "Permission check failed\n" - if !($auditor || $user eq $task->{user}); - - my $fh = IO::File->new($filename, "r"); - raise_param_exc({ upid => "no such task - unable to open file - $!" }) if !$fh; - - my $start = $param->{start} || 0; - my $limit = $param->{limit} || 50; - my $count = 0; - my $line; - while (defined ($line = <$fh>)) { - next if $count++ < $start; - next if $limit <= 0; - chomp $line; - push @$lines, { n => $count, t => $line}; - $limit--; + if ($user ne $task->{user}) { + $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]); } - close($fh); + my ($count, $lines) = PVE::Tools::dump_logfile($filename, $start, $limit); - # HACK: ExtJS store.guaranteeRange() does not like empty array - # so we add a line - if (!$count) { - $count++; - push @$lines, { n => $count, t => "no content"}; - } + $rpcenv->set_result_attrib('total', $count); - $rpcenv->set_result_count($count); - return $lines; }}); + +my $exit_status_cache = {}; + __PACKAGE__->register_method({ - name => 'read_task_status', - path => '{upid}/status', + name => 'read_task_status', + path => '{upid}/status', method => 'GET', - permissions => { user => 'all' }, + permissions => { + description => "The user needs 'Sys.Audit' permissions on '/nodes/' if the task does not belong to him.", + user => 'all', + }, protected => 1, description => "Read task status.", proxyto => 'node', @@ -288,11 +344,11 @@ __PACKAGE__->register_method({ returns => { type => "object", properties => { - pid => { + pid => { type => 'integer' }, - status => { - type => 'string', enum => ['running', 'stopped'], + status => { + type => 'string', enum => ['running', 'stopped'], }, }, }, @@ -309,9 +365,9 @@ __PACKAGE__->register_method({ my $user = $rpcenv->get_user(); my $node = $param->{node}; - my $auditor = $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]); - die "Permission check failed\n" - if !($auditor || $user eq $task->{user}); + if ($user ne $task->{user}) { + $rpcenv->check($user, "/nodes/$node", [ 'Sys.Audit' ]); + } my $pstart = PVE::ProcFSTools::read_proc_starttime($task->{pid}); $task->{status} = ($pstart && ($pstart == $task->{pstart})) ? @@ -319,5 +375,15 @@ __PACKAGE__->register_method({ $task->{upid} = $param->{upid}; # include upid + if ($task->{status} eq 'stopped') { + if (!defined($exit_status_cache->{$task->{upid}})) { + $exit_status_cache->{$task->{upid}} = + PVE::Tools::upid_read_status($task->{upid}); + } + $task->{exitstatus} = $exit_status_cache->{$task->{upid}}; + } + return $task; }}); + +1;