foreman-tasks 5.2.0 → 5.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|