foreman-tasks 6.0.0 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_tests.yml +7 -18
- data/.rubocop.yml +1 -0
- 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/remote_task.rb +5 -4
- data/lib/foreman_tasks/tasks/export_tasks.rake +26 -18
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/action_names.rb +2 -3
- data/locale/en/foreman_tasks.po +27 -3
- data/locale/foreman_tasks.pot +230 -176
- data/locale/fr/foreman_tasks.po +27 -3
- data/locale/ja/foreman_tasks.po +27 -3
- data/locale/zh_CN/foreman_tasks.po +27 -3
- 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: 7fe4afd0fbe9eaa5cb5587310ddddd1538453e733b48b6f927e6bc14fa4ad42e
|
4
|
+
data.tar.gz: 27591131ac0329e66625a15c4b512fa7a24b23b3623abdc1eaef40fee7bdea9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 042c221e1f3c01b2d38b2011165479282165846d9b46923b5ef7cc4fc8f91c3ab6ff5921bd229a4a26dbf661dccff3a9d0f0d474e11eb18b074880fc8fc25870
|
7
|
+
data.tar.gz: 4fdf7dfc1836bd8b4ea0490f8c7a894569c7ccf9eca811945b517f7aedface6b04c0c6bbe9c7f34a7b7bbea77328edaadd27ebb624bc29c4acae2eb86bc25624
|
@@ -4,6 +4,7 @@ env:
|
|
4
4
|
RAILS_ENV: test
|
5
5
|
DATABASE_URL: postgresql://postgres:@localhost/test
|
6
6
|
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true
|
7
|
+
BUNDLE_WITHOUT: "journald:development:console:mysql2:sqlite:libvirt"
|
7
8
|
jobs:
|
8
9
|
rubocop:
|
9
10
|
runs-on: ubuntu-latest
|
@@ -13,10 +14,7 @@ jobs:
|
|
13
14
|
uses: ruby/setup-ruby@v1
|
14
15
|
with:
|
15
16
|
ruby-version: 2.7
|
16
|
-
|
17
|
-
run: |
|
18
|
-
gem install bundler
|
19
|
-
bundle install --jobs=3 --retry=3
|
17
|
+
bundler-cache: true
|
20
18
|
- name: Run rubocop
|
21
19
|
run: bundle exec rubocop
|
22
20
|
test_ruby:
|
@@ -43,30 +41,21 @@ jobs:
|
|
43
41
|
- uses: actions/checkout@v2
|
44
42
|
with:
|
45
43
|
path: foreman-tasks
|
44
|
+
- name: Setup Plugin in Foreman
|
45
|
+
run: |
|
46
|
+
echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
|
47
|
+
echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
|
46
48
|
- name: Setup Ruby
|
47
49
|
uses: ruby/setup-ruby@v1
|
48
50
|
with:
|
49
51
|
ruby-version: ${{ matrix.ruby-version }}
|
52
|
+
bundler-cache: true
|
50
53
|
- name: Setup Node
|
51
54
|
uses: actions/setup-node@v1
|
52
55
|
with:
|
53
56
|
node-version: ${{ matrix.node-version }}
|
54
|
-
- uses: actions/cache@v2
|
55
|
-
with:
|
56
|
-
path: vendor/bundle
|
57
|
-
key: ${{ runner.os }}-fgems-${{ matrix.ruby-version }}-${{ hashFiles('Gemfile.lock') }}
|
58
|
-
restore-keys: |
|
59
|
-
${{ runner.os }}-fgems-${{ matrix.ruby-version }}-
|
60
|
-
- name: Setup Bundler
|
61
|
-
run: |
|
62
|
-
echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
|
63
|
-
echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
|
64
|
-
gem install bundler
|
65
|
-
bundle config set without journald development console libvirt
|
66
|
-
bundle config set path vendor/bundle
|
67
57
|
- name: Prepare test env
|
68
58
|
run: |
|
69
|
-
bundle install --jobs=3 --retry=3
|
70
59
|
bundle exec rake db:create
|
71
60
|
bundle exec rake db:migrate
|
72
61
|
- name: Run plugin tests
|
data/.rubocop.yml
CHANGED
@@ -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
|
output[:planned_count] += group.size
|
41
41
|
end
|
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
|
@@ -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 do |remote_task|
|
36
37
|
remote_task.update_from_batch_trigger results.fetch(remote_task.execution_plan_id, {}),
|
37
38
|
results.fetch('parent', {})
|
38
39
|
end
|
@@ -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
|
@@ -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
|
data/locale/action_names.rb
CHANGED
data/locale/en/foreman_tasks.po
CHANGED
@@ -56,6 +56,9 @@ msgstr ""
|
|
56
56
|
msgid "A paused task represents a process that has not finished properly. Any task in paused state can lead to potential inconsistency and needs to be resolved."
|
57
57
|
msgstr ""
|
58
58
|
|
59
|
+
msgid "A special label for tracking a recurring job. There can be only one active job with a given purpose at a time."
|
60
|
+
msgstr ""
|
61
|
+
|
59
62
|
msgid "Action"
|
60
63
|
msgstr ""
|
61
64
|
|
@@ -68,6 +71,9 @@ msgstr ""
|
|
68
71
|
msgid "Active Filters:"
|
69
72
|
msgstr ""
|
70
73
|
|
74
|
+
msgid "Active or disabled recurring logic with purpose %s already exists"
|
75
|
+
msgstr ""
|
76
|
+
|
71
77
|
msgid "All %s tasks are selected. "
|
72
78
|
msgstr ""
|
73
79
|
|
@@ -276,6 +282,9 @@ msgstr ""
|
|
276
282
|
msgid "Fri"
|
277
283
|
msgstr ""
|
278
284
|
|
285
|
+
msgid "I understand that this may cause harm and have working database backups of all backend services."
|
286
|
+
msgstr ""
|
287
|
+
|
279
288
|
msgid "ID"
|
280
289
|
msgstr ""
|
281
290
|
|
@@ -427,9 +436,6 @@ msgstr ""
|
|
427
436
|
msgid "Polling multiplier which is used to multiply the default polling intervals. This can be used to prevent polling too frequently for long running tasks."
|
428
437
|
msgstr ""
|
429
438
|
|
430
|
-
msgid "Preupgrade job"
|
431
|
-
msgstr ""
|
432
|
-
|
433
439
|
msgid "Proxy action retry count"
|
434
440
|
msgstr ""
|
435
441
|
|
@@ -442,6 +448,9 @@ msgstr ""
|
|
442
448
|
msgid "Proxy tasks batch size"
|
443
449
|
msgstr ""
|
444
450
|
|
451
|
+
msgid "Purpose"
|
452
|
+
msgstr ""
|
453
|
+
|
445
454
|
msgid "Raw"
|
446
455
|
msgstr ""
|
447
456
|
|
@@ -487,6 +496,9 @@ msgstr ""
|
|
487
496
|
msgid "Resource search_params requires resource_type and resource_id to be specified"
|
488
497
|
msgstr ""
|
489
498
|
|
499
|
+
msgid "Resources for %s task(s) will be unlocked and will not prevent other tasks from being run. As the task(s) might be still running, it should be avoided to use this unless you are really sure the task(s) got stuck."
|
500
|
+
msgstr ""
|
501
|
+
|
490
502
|
msgid "Result"
|
491
503
|
msgstr ""
|
492
504
|
|
@@ -663,6 +675,12 @@ msgstr[1] ""
|
|
663
675
|
msgid "This action will delete all cancelled recurring logics. Please note that this action can't be reversed."
|
664
676
|
msgstr ""
|
665
677
|
|
678
|
+
msgid "This will %(action)s %(number)s task(s), putting them in the %(state)s state. Are you sure?"
|
679
|
+
msgstr ""
|
680
|
+
|
681
|
+
msgid "This will unlock the resources that the task is running against. Please note that this might lead to inconsistent state and should be used with caution, after making sure that the task can't be resumed."
|
682
|
+
msgstr ""
|
683
|
+
|
666
684
|
msgid "Thu"
|
667
685
|
msgstr ""
|
668
686
|
|
@@ -717,6 +735,9 @@ msgstr ""
|
|
717
735
|
msgid "Yes"
|
718
736
|
msgstr ""
|
719
737
|
|
738
|
+
msgid "You can find resource locks on this page. Exclusive lock marked with locked icon means that no other task can use locked resource while this task is running. Non-exclusive lock marked with unlocked icon means other tasks can access the resource freely, it is only used to indicate the relation of this task with the resource"
|
739
|
+
msgstr ""
|
740
|
+
|
720
741
|
msgid "You do not have permission"
|
721
742
|
msgstr ""
|
722
743
|
|
@@ -758,6 +779,9 @@ msgstr ""
|
|
758
779
|
msgid "is month (range: 1-12)"
|
759
780
|
msgstr ""
|
760
781
|
|
782
|
+
msgid "is not a valid format"
|
783
|
+
msgstr ""
|
784
|
+
|
761
785
|
msgid "last"
|
762
786
|
msgstr ""
|
763
787
|
|