foreman-tasks 5.2.0 → 5.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/foreman_tasks/tasks_controller.rb +18 -18
- data/app/lib/actions/trigger_proxy_batch.rb +2 -1
- data/app/models/foreman_tasks/recurring_logic.rb +1 -1
- data/app/models/foreman_tasks/remote_task.rb +5 -4
- data/extra/foreman-tasks-cleanup.sh +19 -2
- data/lib/foreman_tasks/tasks/export_tasks.rake +27 -19
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/unit/remote_task_test.rb +26 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f1c154c83d919ac42b7d12edd03cff55e67f11a1367a67250a9a090759cd099
|
4
|
+
data.tar.gz: 519d85e8e975aab38b2e2314eb5761cb2bb3fe2d07b291822f3c667e6c0feab4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28a96b1b6c28bb5697607d69950167100815c667ecbebbc3f9f446f7e44899e7459e79a4f8efe8f53d798ee7ece6ebaad6d0becd610b11ab499cd32a4e2b5b53
|
7
|
+
data.tar.gz: 16c30b57fc2b2f82be9baf9d2dcf4c1b9f7a47df3013393d274fb6268ea04b6a066d3464bdeb1d911b998d7d5e68faa19ea9551193a25a07fb1cc12ddb45ee66
|
@@ -4,6 +4,8 @@ module ForemanTasks
|
|
4
4
|
include Foreman::Controller::CsvResponder
|
5
5
|
include ForemanTasks::FindTasksCommon
|
6
6
|
|
7
|
+
before_action :find_dynflow_task, only: [:unlock, :force_unlock, :cancel, :cancel_step, :resume]
|
8
|
+
|
7
9
|
def show
|
8
10
|
@task = resource_base.find(params[:id])
|
9
11
|
render :layout => !request.xhr?
|
@@ -31,8 +33,7 @@ module ForemanTasks
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def cancel_step
|
34
|
-
|
35
|
-
result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
|
36
|
+
result = ForemanTasks.dynflow.world.event(@dynflow_task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
|
36
37
|
if result.rejected?
|
37
38
|
render json: { error: result.reason }, status: :bad_request
|
38
39
|
else
|
@@ -41,8 +42,7 @@ module ForemanTasks
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def cancel
|
44
|
-
|
45
|
-
if task.cancel
|
45
|
+
if @dynflow_task.cancel
|
46
46
|
render json: { statusText: 'OK' }
|
47
47
|
else
|
48
48
|
render json: {}, status: :bad_request
|
@@ -50,19 +50,17 @@ module ForemanTasks
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def abort
|
53
|
-
|
54
|
-
if task.abort
|
53
|
+
if @dynflow_task.abort
|
55
54
|
flash[:info] = _('Trying to abort the task')
|
56
55
|
else
|
57
56
|
flash[:warning] = _('The task cannot be aborted at the moment.')
|
58
57
|
end
|
59
|
-
redirect_back(:fallback_location => foreman_tasks_task_path(
|
58
|
+
redirect_back(:fallback_location => foreman_tasks_task_path(@dynflow_task))
|
60
59
|
end
|
61
60
|
|
62
61
|
def resume
|
63
|
-
|
64
|
-
|
65
|
-
ForemanTasks.dynflow.world.execute(task.execution_plan.id)
|
62
|
+
if @dynflow_task.resumable?
|
63
|
+
ForemanTasks.dynflow.world.execute(@dynflow_task.execution_plan.id)
|
66
64
|
render json: { statusText: 'OK' }
|
67
65
|
else
|
68
66
|
render json: {}, status: :bad_request
|
@@ -70,10 +68,8 @@ module ForemanTasks
|
|
70
68
|
end
|
71
69
|
|
72
70
|
def unlock
|
73
|
-
|
74
|
-
|
75
|
-
task.state = :stopped
|
76
|
-
task.save!
|
71
|
+
if @dynflow_task.paused?
|
72
|
+
unlock_task(@dynflow_task)
|
77
73
|
render json: { statusText: 'OK' }
|
78
74
|
else
|
79
75
|
render json: {}, status: :bad_request
|
@@ -81,9 +77,7 @@ module ForemanTasks
|
|
81
77
|
end
|
82
78
|
|
83
79
|
def force_unlock
|
84
|
-
|
85
|
-
task.state = :stopped
|
86
|
-
task.save!
|
80
|
+
unlock_task(@dynflow_task)
|
87
81
|
render json: { statusText: 'OK' }
|
88
82
|
end
|
89
83
|
|
@@ -98,6 +92,12 @@ module ForemanTasks
|
|
98
92
|
|
99
93
|
private
|
100
94
|
|
95
|
+
def unlock_task(task)
|
96
|
+
task.state = :stopped
|
97
|
+
task.locks.destroy_all
|
98
|
+
task.save!
|
99
|
+
end
|
100
|
+
|
101
101
|
def respond_with_tasks(scope)
|
102
102
|
@tasks = filter(scope, paginate: false).with_duration
|
103
103
|
csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :duration, :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'Duration', 'User'])
|
@@ -123,7 +123,7 @@ module ForemanTasks
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def find_dynflow_task
|
126
|
-
resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
|
126
|
+
@dynflow_task = resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
|
127
127
|
end
|
128
128
|
|
129
129
|
def filter(scope, paginate: true)
|
@@ -40,7 +40,8 @@ module Actions
|
|
40
40
|
end
|
41
41
|
output[:planned_count] += batch.size
|
42
42
|
rescue => e
|
43
|
-
action_logger.warn "Could not trigger task on the smart proxy
|
43
|
+
action_logger.warn "Could not trigger task on the smart proxy"
|
44
|
+
action_logger.warn e
|
44
45
|
batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
|
45
46
|
output[:failed_count] += batch.size
|
46
47
|
end
|
@@ -90,7 +90,7 @@ module ForemanTasks
|
|
90
90
|
def next_occurrence_time(time = Time.zone.now)
|
91
91
|
@parser ||= CronParser.new(cron_line, Time.zone)
|
92
92
|
# @parser.next(start_time) is not inclusive of the start_time hence stepping back one run to include checking start_time for the first run.
|
93
|
-
before_next = @parser.next(@parser.last(time))
|
93
|
+
before_next = @parser.next(@parser.last(time.in_time_zone))
|
94
94
|
return before_next if before_next >= time && tasks.count == 0
|
95
95
|
@parser.next(time)
|
96
96
|
end
|
@@ -18,7 +18,8 @@ module ForemanTasks
|
|
18
18
|
response = begin
|
19
19
|
proxy.launch_tasks('single', :action_class => proxy_action_name, :action_input => input)
|
20
20
|
rescue RestClient::Exception => e
|
21
|
-
logger.warn "Could not trigger task on the smart proxy
|
21
|
+
logger.warn "Could not trigger task on the smart proxy"
|
22
|
+
logger.warn e
|
22
23
|
{}
|
23
24
|
end
|
24
25
|
update_from_batch_trigger(response)
|
@@ -26,13 +27,13 @@ module ForemanTasks
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def self.batch_trigger(operation, remote_tasks)
|
29
|
-
remote_tasks.group_by(&:proxy_url).
|
30
|
+
remote_tasks.group_by(&:proxy_url).each_value do |group|
|
30
31
|
input_hash = group.reduce({}) do |acc, remote_task|
|
31
32
|
acc.merge(remote_task.execution_plan_id => { :action_input => remote_task.proxy_input,
|
32
33
|
:action_class => remote_task.proxy_action_name })
|
33
34
|
end
|
34
|
-
results =
|
35
|
-
|
35
|
+
results = group.first.proxy.launch_tasks(operation, input_hash)
|
36
|
+
group.each { |remote_task| remote_task.update_from_batch_trigger results[remote_task.execution_plan_id] }
|
36
37
|
end
|
37
38
|
remote_tasks
|
38
39
|
end
|
@@ -21,6 +21,21 @@ function build_rake() {
|
|
21
21
|
echo
|
22
22
|
}
|
23
23
|
|
24
|
+
function incorrect_usage() {
|
25
|
+
echo "$1" >&2
|
26
|
+
echo
|
27
|
+
usage
|
28
|
+
|
29
|
+
exit 1
|
30
|
+
}
|
31
|
+
|
32
|
+
function validate_options!() {
|
33
|
+
if [ -z "$TASK_SEARCH" ]; then
|
34
|
+
[ -n "$AFTER" ] && incorrect_usage "Error: -a|--after cannot be used without -s|--search"
|
35
|
+
[ -n "$STATES" ] && incorrect_usage "Error: -S|--states cannot be used without -s|--search"
|
36
|
+
fi
|
37
|
+
}
|
38
|
+
|
24
39
|
function usage() {
|
25
40
|
cat << EOF
|
26
41
|
Usage: $PROGNAME [script_options...] [options...]
|
@@ -43,8 +58,8 @@ EOF
|
|
43
58
|
echo Cleanup options:
|
44
59
|
cat <<EOF | column -s\& -t
|
45
60
|
-B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
|
46
|
-
-S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state
|
47
|
-
-a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days
|
61
|
+
-S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state. Has to be used together with -s|--search
|
62
|
+
-a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days. Has to be used together with -s|--search
|
48
63
|
-b|--backup & backup deleted tasks
|
49
64
|
-n|--noop & do a dry run, print what would be done
|
50
65
|
-s|--search QUERY & use QUERY in scoped search format to match tasks to delete
|
@@ -119,6 +134,8 @@ while true; do
|
|
119
134
|
shift
|
120
135
|
done
|
121
136
|
|
137
|
+
validate_options!
|
138
|
+
|
122
139
|
if [ "$EXECUTE" -eq 1 ]; then
|
123
140
|
build_rake | sh
|
124
141
|
else
|
@@ -241,35 +241,42 @@ namespace :foreman_tasks do
|
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
244
|
-
def csv_export(export_filename,
|
244
|
+
def csv_export(export_filename, id_scope, task_scope)
|
245
245
|
CSV.open(export_filename, 'wb') do |csv|
|
246
246
|
csv << %w[id state type label result parent_task_id started_at ended_at duration]
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
247
|
+
id_scope.pluck(:id).each_slice(1000).each do |ids|
|
248
|
+
task_scope.where(id: ids).each do |task|
|
249
|
+
with_error_handling(task) do
|
250
|
+
csv << [task.id, task.state, task.type, task.label, task.result,
|
251
|
+
task.parent_task_id, task.started_at, task.ended_at, task.duration]
|
252
|
+
end
|
251
253
|
end
|
252
254
|
end
|
253
255
|
end
|
254
256
|
end
|
255
257
|
|
256
|
-
def html_export(workdir,
|
258
|
+
def html_export(workdir, id_scope, task_scope)
|
257
259
|
PageHelper.copy_assets(workdir)
|
258
260
|
|
261
|
+
ids = id_scope.pluck(:id)
|
259
262
|
renderer = TaskRender.new
|
260
|
-
|
263
|
+
count = 0
|
264
|
+
total = ids.count
|
261
265
|
index = File.open(File.join(workdir, 'index.html'), 'w')
|
262
266
|
|
263
267
|
File.open(File.join(workdir, 'index.html'), 'w') do |index|
|
264
268
|
PageHelper.pagify(index) do |io|
|
265
269
|
PageHelper.generate_with_index(io) do |index|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
270
|
+
ids.each_slice(1000).each do |ids|
|
271
|
+
task_scope.where(id: ids).each do |task|
|
272
|
+
content = with_error_handling(task) { renderer.render_task(task) }
|
273
|
+
if content
|
274
|
+
File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
|
275
|
+
with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
|
276
|
+
end
|
277
|
+
count += 1
|
278
|
+
puts "#{count}/#{total}"
|
271
279
|
end
|
272
|
-
puts "#{count + 1}/#{total}"
|
273
280
|
end
|
274
281
|
end
|
275
282
|
end
|
@@ -300,7 +307,7 @@ namespace :foreman_tasks do
|
|
300
307
|
end
|
301
308
|
end
|
302
309
|
|
303
|
-
SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? ENV['SKIP_FAILED'].downcase
|
310
|
+
SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? (ENV['SKIP_FAILED'] || '').downcase
|
304
311
|
|
305
312
|
filter = if ENV['TASK_SEARCH'].nil? && ENV['TASK_DAYS'].nil?
|
306
313
|
"started_at > \"#{7.days.ago.to_s(:db)}\" || " \
|
@@ -317,21 +324,22 @@ namespace :foreman_tasks do
|
|
317
324
|
format = ENV['TASK_FORMAT'] || 'html'
|
318
325
|
export_filename = ENV['TASK_FILE'] || generate_filename(format)
|
319
326
|
|
320
|
-
|
327
|
+
task_scope = ForemanTasks::Task.search_for(filter).with_duration.order(:started_at => :desc)
|
328
|
+
id_scope = task_scope.group(:id, :started_at)
|
321
329
|
|
322
330
|
puts _("Exporting all tasks matching filter #{filter}")
|
323
|
-
puts _("Gathering #{
|
331
|
+
puts _("Gathering #{id_scope.count(:all).count} tasks.")
|
324
332
|
case format
|
325
333
|
when 'html'
|
326
334
|
Dir.mktmpdir('task-export') do |tmp_dir|
|
327
|
-
html_export(tmp_dir,
|
335
|
+
html_export(tmp_dir, id_scope, task_scope)
|
328
336
|
system("tar", "czf", export_filename, tmp_dir)
|
329
337
|
end
|
330
338
|
when 'html-dir'
|
331
339
|
FileUtils.mkdir_p(export_filename)
|
332
|
-
html_export(export_filename,
|
340
|
+
html_export(export_filename, id_scope, task_scope)
|
333
341
|
when 'csv'
|
334
|
-
csv_export(export_filename,
|
342
|
+
csv_export(export_filename, id_scope, task_scope)
|
335
343
|
else
|
336
344
|
raise "Unkonwn export format '#{format}'"
|
337
345
|
end
|
@@ -28,6 +28,32 @@ module ForemanTasks
|
|
28
28
|
_(remote_task.remote_task_id).must_equal((remote_task.id + 5).to_s)
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
it 'honors the batches with multiple proxies' do
|
33
|
+
remote_task = remote_tasks.last
|
34
|
+
remote_task.proxy_url = 'something else'
|
35
|
+
|
36
|
+
results = remote_tasks.reduce({}) do |acc, cur|
|
37
|
+
acc.merge(cur.execution_plan_id.to_s => { 'task_id' => cur.id + 5, 'result' => 'success' })
|
38
|
+
end
|
39
|
+
other_results = { remote_task.execution_plan_id => results.delete(remote_task.execution_plan_id) }
|
40
|
+
|
41
|
+
fake_proxy = mock
|
42
|
+
fake_proxy.expects(:launch_tasks).returns(results)
|
43
|
+
|
44
|
+
another_fake_proxy = mock
|
45
|
+
another_fake_proxy.expects(:launch_tasks).returns(other_results)
|
46
|
+
|
47
|
+
remote_tasks.first.expects(:proxy).returns(fake_proxy)
|
48
|
+
remote_tasks.last.expects(:proxy).returns(another_fake_proxy)
|
49
|
+
|
50
|
+
RemoteTask.batch_trigger('a_operation', remote_tasks)
|
51
|
+
remote_tasks.each do |remote_task|
|
52
|
+
remote_task.reload
|
53
|
+
_(remote_task.state).must_equal 'triggered'
|
54
|
+
_(remote_task.remote_task_id).must_equal((remote_task.id + 5).to_s)
|
55
|
+
end
|
56
|
+
end
|
31
57
|
end
|
32
58
|
end
|
33
59
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman-tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2.
|
4
|
+
version: 5.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dynflow
|
@@ -607,7 +607,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
607
607
|
- !ruby/object:Gem::Version
|
608
608
|
version: '0'
|
609
609
|
requirements: []
|
610
|
-
rubygems_version: 3.1.
|
610
|
+
rubygems_version: 3.1.4
|
611
611
|
signing_key:
|
612
612
|
specification_version: 4
|
613
613
|
summary: Foreman plugin for showing tasks information for resources and users
|