foreman-tasks 5.2.3 → 5.3.0
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/base.rb +1 -0
- data/app/lib/actions/foreman/host/import_facts.rb +1 -1
- data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +18 -11
- data/app/lib/actions/proxy_action.rb +1 -1
- data/app/lib/actions/task_synchronization.rb +65 -0
- data/app/lib/actions/trigger_proxy_batch.rb +2 -3
- data/app/models/foreman_tasks/recurring_logic.rb +1 -1
- data/app/models/foreman_tasks/remote_task.rb +13 -7
- data/app/models/foreman_tasks/task/dynflow_task.rb +20 -21
- data/app/views/foreman_tasks/api/locks/show.json.rabl +4 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20210708123832_add_parent_task_id_to_remote_tasks.foreman_tasks.rb +5 -0
- data/db/migrate/20211123170430_tasks_settings_to_dsl_category.rb +5 -0
- data/extra/foreman-tasks-cleanup.sh +2 -19
- data/foreman-tasks.gemspec +1 -1
- data/lib/foreman_tasks/dynflow/configuration.rb +0 -5
- data/lib/foreman_tasks/engine.rb +56 -9
- data/lib/foreman_tasks/tasks/export_tasks.rake +19 -27
- data/lib/foreman_tasks/test_helpers.rb +1 -1
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/factories/task_factory.rb +1 -1
- data/test/lib/actions/middleware/keep_current_taxonomies_test.rb +9 -1
- data/test/support/dummy_dynflow_action.rb +1 -1
- data/test/unit/actions/trigger_proxy_batch_test.rb +0 -1
- data/test/unit/remote_task_test.rb +0 -26
- data/test/unit/troubleshooting_help_generator_test.rb +0 -1
- data/test/unit/ui_notifications_test.rb +0 -1
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +1 -1
- data/webpack/ForemanTasks/Components/TaskActions/index.js +1 -1
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +28 -14
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +50 -42
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +1 -1
- data/webpack/__mocks__/foremanReact/{redux/actions/toasts.js → components/ToastsList/index.js} +0 -0
- metadata +9 -8
- data/app/models/setting/foreman_tasks.rb +0 -29
- data/lib/foreman_tasks/dynflow/persistence.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f5ae3bb36274977790149d11f722477d6db0254b251ffa1dc79ec850a206183
|
4
|
+
data.tar.gz: bfd76d0fcc43a78e34c1831a392042e5770bd09276d95d126043f3a14db174b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2bde1cb649304ba13019b544c8db8f7fa72b2e0539d47b2db07d6e945364d08b4e5fa0b100b701aa7f080e8be308ad7a6af404eaee2865e7b13fd423f9a9300
|
7
|
+
data.tar.gz: 00a77aaf192bbfc12a70dc034aa75bfd728c18956b1b9377bbef3d7fce7491c68cfa520d9fd04de4789c721fcc2d2b2b605b63a9165601092841e44184219dfc
|
@@ -4,8 +4,6 @@ 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
|
-
|
9
7
|
def show
|
10
8
|
@task = resource_base.find(params[:id])
|
11
9
|
render :layout => !request.xhr?
|
@@ -33,7 +31,8 @@ module ForemanTasks
|
|
33
31
|
end
|
34
32
|
|
35
33
|
def cancel_step
|
36
|
-
|
34
|
+
task = find_dynflow_task
|
35
|
+
result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
|
37
36
|
if result.rejected?
|
38
37
|
render json: { error: result.reason }, status: :bad_request
|
39
38
|
else
|
@@ -42,7 +41,8 @@ module ForemanTasks
|
|
42
41
|
end
|
43
42
|
|
44
43
|
def cancel
|
45
|
-
|
44
|
+
task = find_dynflow_task
|
45
|
+
if task.cancel
|
46
46
|
render json: { statusText: 'OK' }
|
47
47
|
else
|
48
48
|
render json: {}, status: :bad_request
|
@@ -50,17 +50,19 @@ module ForemanTasks
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def abort
|
53
|
-
|
53
|
+
task = find_dynflow_task
|
54
|
+
if task.abort
|
54
55
|
flash[:info] = _('Trying to abort the task')
|
55
56
|
else
|
56
57
|
flash[:warning] = _('The task cannot be aborted at the moment.')
|
57
58
|
end
|
58
|
-
redirect_back(:fallback_location => foreman_tasks_task_path(
|
59
|
+
redirect_back(:fallback_location => foreman_tasks_task_path(task))
|
59
60
|
end
|
60
61
|
|
61
62
|
def resume
|
62
|
-
|
63
|
-
|
63
|
+
task = find_dynflow_task
|
64
|
+
if task.resumable?
|
65
|
+
ForemanTasks.dynflow.world.execute(task.execution_plan.id)
|
64
66
|
render json: { statusText: 'OK' }
|
65
67
|
else
|
66
68
|
render json: {}, status: :bad_request
|
@@ -68,8 +70,10 @@ module ForemanTasks
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def unlock
|
71
|
-
|
72
|
-
|
73
|
+
task = find_dynflow_task
|
74
|
+
if task.paused?
|
75
|
+
task.state = :stopped
|
76
|
+
task.save!
|
73
77
|
render json: { statusText: 'OK' }
|
74
78
|
else
|
75
79
|
render json: {}, status: :bad_request
|
@@ -77,7 +81,9 @@ module ForemanTasks
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def force_unlock
|
80
|
-
|
84
|
+
task = find_dynflow_task
|
85
|
+
task.state = :stopped
|
86
|
+
task.save!
|
81
87
|
render json: { statusText: 'OK' }
|
82
88
|
end
|
83
89
|
|
@@ -92,12 +98,6 @@ module ForemanTasks
|
|
92
98
|
|
93
99
|
private
|
94
100
|
|
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
|
-
|
126
|
+
resource_scope.where(:type => 'ForemanTasks::Task::DynflowTask').find(params[:id])
|
127
127
|
end
|
128
128
|
|
129
129
|
def filter(scope, paginate: true)
|
data/app/lib/actions/base.rb
CHANGED
@@ -26,7 +26,7 @@ module Actions
|
|
26
26
|
def run
|
27
27
|
::User.as :admin do
|
28
28
|
host = ::Host.find(input[:host][:id])
|
29
|
-
state = host.import_facts(input[:facts], proxy)
|
29
|
+
state = ::HostFactImporter.new(host).import_facts(input[:facts], proxy)
|
30
30
|
output[:state] = state
|
31
31
|
end
|
32
32
|
rescue ::Foreman::Exception => e
|
@@ -26,17 +26,26 @@ module Actions
|
|
26
26
|
def check_triggered
|
27
27
|
in_remote_task_batches(remote_tasks.triggered) do |batch|
|
28
28
|
batch.group_by(&:proxy_url).each do |(url, tasks)|
|
29
|
-
|
30
|
-
process_task_results tasks
|
29
|
+
results = poll_proxy_tasks(url, tasks)
|
30
|
+
process_task_results tasks, results
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def process_task_results(tasks)
|
36
|
-
|
35
|
+
def process_task_results(tasks, results)
|
36
|
+
possibly_missing, present = tasks.partition { |task| !results.key?(task.remote_task_id) }
|
37
|
+
missing = possibly_missing.select do |task|
|
38
|
+
# Really missing are tasks which are missing and:
|
39
|
+
# don't have a remote parent
|
40
|
+
# had a remote parent but the proxy doesn't have the remote parent anymore
|
41
|
+
# has a remote parent, proxy has the remote parent but it is stopped or paused
|
42
|
+
task.parent_task_id.nil? ||
|
43
|
+
!results.key?(task.parent_task_id) ||
|
44
|
+
%(stopped paused).include?(results[task.parent_task_id]['state'])
|
45
|
+
end
|
37
46
|
notify ::Actions::ProxyAction::ProxyActionMissing.new, missing if missing.any?
|
38
47
|
|
39
|
-
stopped = present.select { |task| %w[stopped paused].include? task.
|
48
|
+
stopped = present.select { |task| %w[stopped paused].include? results.dig(task.remote_task_id, 'state') }
|
40
49
|
notify ::Actions::ProxyAction::ProxyActionStopped.new, stopped if stopped.any?
|
41
50
|
end
|
42
51
|
|
@@ -52,15 +61,13 @@ module Actions
|
|
52
61
|
|
53
62
|
def poll_proxy_tasks(url, tasks)
|
54
63
|
proxy = ProxyAPI::ForemanDynflow::DynflowProxy.new(:url => url)
|
55
|
-
|
56
|
-
tasks.map
|
57
|
-
|
58
|
-
task
|
59
|
-
end
|
64
|
+
# Get statuses of tasks and their optional parent tasks
|
65
|
+
ids = (tasks.map(&:remote_task_id) + tasks.map(&:parent_task_id)).uniq
|
66
|
+
proxy.task_states(ids)
|
60
67
|
rescue => e
|
61
68
|
# We could not reach the remote task, we'll try again next time
|
62
69
|
action.action_logger.warn(_('Failed to check on tasks on proxy at %{url}: %{exception}') % { :url => url, :exception => e.message })
|
63
|
-
|
70
|
+
{}
|
64
71
|
end
|
65
72
|
|
66
73
|
def in_remote_task_batches(scope)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Actions
|
2
|
+
# Examples:
|
3
|
+
|
4
|
+
# # Action A which emits an event when it successfully finishes.
|
5
|
+
# class A
|
6
|
+
# include ::Actions::ObservableAction
|
7
|
+
# # ... rest ...
|
8
|
+
# end
|
9
|
+
|
10
|
+
# # Action B which emits an event when it successfully finishes or fails.
|
11
|
+
# class B
|
12
|
+
# include ::Actions::ObservableAction
|
13
|
+
#
|
14
|
+
# execution_plan_hooks.use :emit_event_failure, :on => [:failure]
|
15
|
+
#
|
16
|
+
# def self.event_names
|
17
|
+
# super + [event_name_base + '_' + event_name_suffix(:failure)]
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def emit_event_failure(plan)
|
21
|
+
# emit_event(plan, :failure)
|
22
|
+
# end
|
23
|
+
# # ... rest ...
|
24
|
+
# end
|
25
|
+
module TaskSynchronization
|
26
|
+
def self.included(base)
|
27
|
+
base.execution_plan_hooks.use :sync_execution_plan_to_task, on: ::Dynflow::ExecutionPlan.states
|
28
|
+
end
|
29
|
+
|
30
|
+
def sync_execution_plan_to_task(plan)
|
31
|
+
return unless root_action?
|
32
|
+
on_execution_plan_save(plan)
|
33
|
+
rescue => e
|
34
|
+
::Foreman::Logging.exception('Error on on_execution_plan_save event', e,
|
35
|
+
:logger => 'dynflow')
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def on_execution_plan_save(execution_plan)
|
41
|
+
# We can load the data unless the execution plan was properly planned and saved
|
42
|
+
# including its steps
|
43
|
+
case execution_plan.state
|
44
|
+
when :pending
|
45
|
+
task = ForemanTasks::Task::DynflowTask.new_for_execution_plan(execution_plan)
|
46
|
+
task.start_at ||= Time.zone.now
|
47
|
+
task.save!
|
48
|
+
when :scheduled
|
49
|
+
delayed_plan = world.persistence.load_delayed_plan(execution_plan.id)
|
50
|
+
raise ::Foreman::Exception, 'Plan is delayed but the delay record is missing' if delayed_plan.nil?
|
51
|
+
task = ::ForemanTasks::Task::DynflowTask.find_by!(:external_id => execution_plan.id)
|
52
|
+
task.update_from_dynflow(execution_plan, delayed_plan)
|
53
|
+
when :planning
|
54
|
+
task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan.id).first
|
55
|
+
task.update_from_dynflow(execution_plan)
|
56
|
+
else
|
57
|
+
if (task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan.id).first)
|
58
|
+
unless task.state.to_s == execution_plan.state.to_s
|
59
|
+
task.update_from_dynflow(execution_plan)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -37,11 +37,10 @@ module Actions
|
|
37
37
|
# Group the tasks by operation, in theory there should be only one operation
|
38
38
|
batch.group_by(&:operation).each do |operation, group|
|
39
39
|
ForemanTasks::RemoteTask.batch_trigger(operation, group)
|
40
|
+
output[:planned_count] += group.size
|
40
41
|
end
|
41
|
-
output[:planned_count] += batch.size
|
42
42
|
rescue => e
|
43
|
-
action_logger.warn "Could not trigger task on the smart proxy"
|
44
|
-
action_logger.warn e
|
43
|
+
action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
|
45
44
|
batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
|
46
45
|
output[:failed_count] += batch.size
|
47
46
|
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))
|
94
94
|
return before_next if before_next >= time && tasks.count == 0
|
95
95
|
@parser.next(time)
|
96
96
|
end
|
@@ -18,8 +18,7 @@ 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"
|
22
|
-
logger.warn e
|
21
|
+
logger.warn "Could not trigger task on the smart proxy: #{e.message}"
|
23
22
|
{}
|
24
23
|
end
|
25
24
|
update_from_batch_trigger(response)
|
@@ -27,26 +26,33 @@ module ForemanTasks
|
|
27
26
|
end
|
28
27
|
|
29
28
|
def self.batch_trigger(operation, remote_tasks)
|
30
|
-
remote_tasks.group_by(&:proxy_url).
|
29
|
+
remote_tasks.group_by(&:proxy_url).values.map do |group|
|
31
30
|
input_hash = group.reduce({}) do |acc, remote_task|
|
32
31
|
acc.merge(remote_task.execution_plan_id => { :action_input => remote_task.proxy_input,
|
33
32
|
:action_class => remote_task.proxy_action_name })
|
34
33
|
end
|
35
|
-
results =
|
36
|
-
|
34
|
+
results = remote_tasks.first.proxy.launch_tasks(operation, input_hash)
|
35
|
+
remote_tasks.each do |remote_task|
|
36
|
+
remote_task.update_from_batch_trigger results.fetch(remote_task.execution_plan_id, {}),
|
37
|
+
results.fetch('parent', {})
|
38
|
+
end
|
37
39
|
end
|
38
40
|
remote_tasks
|
39
41
|
end
|
40
42
|
|
41
|
-
def update_from_batch_trigger(data)
|
43
|
+
def update_from_batch_trigger(data, parent = {})
|
42
44
|
if data['result'] == 'success'
|
43
45
|
self.remote_task_id = data['task_id']
|
44
46
|
self.state = 'triggered'
|
47
|
+
elsif !parent.empty?
|
48
|
+
self.parent_task_id = parent['task_id']
|
49
|
+
self.state = 'parent-triggered'
|
45
50
|
else
|
46
51
|
# Tell the action the task on the smart proxy stopped
|
47
52
|
ForemanTasks.dynflow.world.event execution_plan_id,
|
48
53
|
step_id,
|
49
|
-
::Actions::ProxyAction::ProxyActionStopped.new
|
54
|
+
::Actions::ProxyAction::ProxyActionStopped.new,
|
55
|
+
optional: true
|
50
56
|
end
|
51
57
|
save!
|
52
58
|
end
|
@@ -5,15 +5,14 @@ module ForemanTasks
|
|
5
5
|
scope :for_action, ->(action_class) { where(label: action_class.name) }
|
6
6
|
after_validation :set_action_field
|
7
7
|
|
8
|
-
def update_from_dynflow(
|
9
|
-
|
10
|
-
self.
|
11
|
-
self.
|
12
|
-
self.
|
13
|
-
self.
|
14
|
-
self.
|
15
|
-
self.
|
16
|
-
self.start_before = string_to_time(utc_zone, data[:start_before]) if data[:start_before]
|
8
|
+
def update_from_dynflow(plan, delayed_plan = nil)
|
9
|
+
self.external_id = plan.id
|
10
|
+
self.result = map_result(plan).to_s
|
11
|
+
self.state = plan.state.to_s
|
12
|
+
self.started_at = plan.started_at unless plan.started_at.nil?
|
13
|
+
self.ended_at = plan.ended_at unless plan.ended_at.nil?
|
14
|
+
self.start_at = delayed_plan.start_at if delayed_plan
|
15
|
+
self.start_before = delayed_plan.start_before if delayed_plan
|
17
16
|
self.parent_task_id ||= begin
|
18
17
|
if main_action.try(:caller_execution_plan_id)
|
19
18
|
DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
|
@@ -221,7 +220,7 @@ module ForemanTasks
|
|
221
220
|
fixed_count = 0
|
222
221
|
logger = Foreman::Logging.logger('foreman-tasks')
|
223
222
|
running.each do |task|
|
224
|
-
changes = task.update_from_dynflow(task.execution_plan
|
223
|
+
changes = task.update_from_dynflow(task.execution_plan)
|
225
224
|
unless changes.empty?
|
226
225
|
fixed_count += 1
|
227
226
|
logger.warn('Task %s updated at consistency check: %s' % [task.id, changes.inspect])
|
@@ -236,10 +235,10 @@ module ForemanTasks
|
|
236
235
|
fixed_count
|
237
236
|
end
|
238
237
|
|
239
|
-
def self.new_for_execution_plan(
|
240
|
-
new(:external_id =>
|
241
|
-
:state =>
|
242
|
-
:result =>
|
238
|
+
def self.new_for_execution_plan(execution_plan)
|
239
|
+
new(:external_id => execution_plan.id,
|
240
|
+
:state => execution_plan.state.to_s,
|
241
|
+
:result => execution_plan.result.to_s,
|
243
242
|
:user_id => User.current.try(:id))
|
244
243
|
end
|
245
244
|
|
@@ -258,20 +257,20 @@ module ForemanTasks
|
|
258
257
|
self.action = to_label
|
259
258
|
end
|
260
259
|
|
261
|
-
def map_result(
|
262
|
-
if state_result_transitioned?(%w[planned pending], %w[stopped error],
|
263
|
-
(
|
260
|
+
def map_result(plan)
|
261
|
+
if state_result_transitioned?(%w[planned pending], %w[stopped error], plan) ||
|
262
|
+
(plan.result == :error && cancelled?)
|
264
263
|
:cancelled
|
265
264
|
else
|
266
|
-
|
265
|
+
plan.result
|
267
266
|
end
|
268
267
|
end
|
269
268
|
|
270
|
-
def state_result_transitioned?(from, to,
|
269
|
+
def state_result_transitioned?(from, to, plan)
|
271
270
|
oldstate, oldresult = from
|
272
271
|
newstate, newresult = to
|
273
|
-
state == oldstate &&
|
274
|
-
result == oldresult &&
|
272
|
+
state == oldstate && plan.state.to_s == newstate &&
|
273
|
+
result == oldresult && plan.result.to_s == newresult
|
275
274
|
end
|
276
275
|
|
277
276
|
def cancelled?
|
data/config/routes.rb
CHANGED
@@ -70,7 +70,7 @@ Foreman::Application.routes.draw do
|
|
70
70
|
mount ForemanTasks.dynflow.web_console => '/dynflow'
|
71
71
|
if defined? ::Sidekiq
|
72
72
|
require 'sidekiq/web'
|
73
|
-
redis_url = SETTINGS.dig(:dynflow, :redis_url)
|
73
|
+
redis_url = ENV['DYNFLOW_REDIS_URL'] || SETTINGS.dig(:dynflow, :redis_url)
|
74
74
|
Sidekiq.redis = { url: redis_url }
|
75
75
|
Sidekiq::Web.set :sessions, false
|
76
76
|
mount Sidekiq::Web => '/sidekiq', :constraints => ForemanTasks::Dynflow::SidekiqConsoleConstraint.new
|
@@ -21,21 +21,6 @@ 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
|
-
|
39
24
|
function usage() {
|
40
25
|
cat << EOF
|
41
26
|
Usage: $PROGNAME [script_options...] [options...]
|
@@ -58,8 +43,8 @@ EOF
|
|
58
43
|
echo Cleanup options:
|
59
44
|
cat <<EOF | column -s\& -t
|
60
45
|
-B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
|
61
|
-
-S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state
|
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
|
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
|
63
48
|
-b|--backup & backup deleted tasks
|
64
49
|
-n|--noop & do a dry run, print what would be done
|
65
50
|
-s|--search QUERY & use QUERY in scoped search format to match tasks to delete
|
@@ -134,8 +119,6 @@ while true; do
|
|
134
119
|
shift
|
135
120
|
done
|
136
121
|
|
137
|
-
validate_options!
|
138
|
-
|
139
122
|
if [ "$EXECUTE" -eq 1 ]; then
|
140
123
|
build_rake | sh
|
141
124
|
else
|
data/foreman-tasks.gemspec
CHANGED
@@ -26,7 +26,7 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
|
|
26
26
|
s.test_files = `git ls-files test`.split("\n")
|
27
27
|
s.extra_rdoc_files = Dir['README*', 'LICENSE']
|
28
28
|
|
29
|
-
s.add_dependency "dynflow", '>= 1.
|
29
|
+
s.add_dependency "dynflow", '>= 1.6.0'
|
30
30
|
s.add_dependency "get_process_mem" # for memory polling
|
31
31
|
s.add_dependency "parse-cron", '~> 0.1.4'
|
32
32
|
s.add_dependency "sinatra" # for Dynflow web console
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.expand_path('lib/foreman/dynflow/configuration', Rails.root)
|
2
|
-
require 'foreman_tasks/dynflow/persistence'
|
3
2
|
|
4
3
|
module ForemanTasks
|
5
4
|
# Import all Dynflow configuration from Foreman, and add our own for Tasks
|
@@ -34,9 +33,5 @@ module ForemanTasks
|
|
34
33
|
end
|
35
34
|
options
|
36
35
|
end
|
37
|
-
|
38
|
-
def persistence_class
|
39
|
-
ForemanTasks::Dynflow::Persistence
|
40
|
-
end
|
41
36
|
end
|
42
37
|
end
|
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -5,14 +5,6 @@ module ForemanTasks
|
|
5
5
|
class Engine < ::Rails::Engine
|
6
6
|
engine_name 'foreman_tasks'
|
7
7
|
|
8
|
-
initializer 'foreman_tasks.load_default_settings', :before => :load_config_initializers do
|
9
|
-
require_dependency File.expand_path('../../app/models/setting/foreman_tasks.rb', __dir__) if begin
|
10
|
-
Setting.table_exists?
|
11
|
-
rescue
|
12
|
-
false
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
8
|
assets_to_precompile = %w[foreman_tasks/foreman_tasks.css
|
17
9
|
foreman_tasks/foreman_tasks.js]
|
18
10
|
|
@@ -33,7 +25,7 @@ module ForemanTasks
|
|
33
25
|
|
34
26
|
initializer 'foreman_tasks.register_plugin', :before => :finisher_hook do |_app|
|
35
27
|
Foreman::Plugin.register :"foreman-tasks" do
|
36
|
-
requires_foreman '>=
|
28
|
+
requires_foreman '>= 3.0.0'
|
37
29
|
divider :top_menu, :parent => :monitor_menu, :last => true, :caption => N_('Foreman Tasks')
|
38
30
|
menu :top_menu, :tasks,
|
39
31
|
:url_hash => { :controller => 'foreman_tasks/tasks', :action => :index },
|
@@ -65,6 +57,60 @@ module ForemanTasks
|
|
65
57
|
|
66
58
|
add_all_permissions_to_default_roles
|
67
59
|
|
60
|
+
settings do
|
61
|
+
category(:tasks, N_('Tasks')) do
|
62
|
+
setting('foreman_tasks_sync_task_timeout',
|
63
|
+
type: :integer,
|
64
|
+
description: N_('Number of seconds to wait for synchronous task to finish.'),
|
65
|
+
default: 120,
|
66
|
+
full_name: N_('Sync task timeout'))
|
67
|
+
setting('dynflow_enable_console',
|
68
|
+
type: :boolean,
|
69
|
+
description: N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'),
|
70
|
+
default: true,
|
71
|
+
full_name: N_('Enable dynflow console'))
|
72
|
+
setting('dynflow_console_require_auth',
|
73
|
+
type: :boolean,
|
74
|
+
description: N_('Require user to be authenticated as user with admin rights when accessing dynflow console'),
|
75
|
+
default: true,
|
76
|
+
full_name: N_('Require auth for dynflow console'))
|
77
|
+
setting('foreman_tasks_proxy_action_retry_count',
|
78
|
+
type: :integer,
|
79
|
+
description: N_('Number of attempts to start a task on the smart proxy before failing'),
|
80
|
+
default: 4,
|
81
|
+
full_name: N_('Proxy action retry count'))
|
82
|
+
setting('foreman_tasks_proxy_action_retry_interval',
|
83
|
+
type: :integer,
|
84
|
+
description: N_('Time in seconds between retries'),
|
85
|
+
default: 15,
|
86
|
+
full_name: N_('Proxy action retry interval'))
|
87
|
+
setting('foreman_tasks_proxy_batch_trigger',
|
88
|
+
type: :boolean,
|
89
|
+
description: N_('Allow triggering tasks on the smart proxy in batches'),
|
90
|
+
default: true,
|
91
|
+
full_name: N_('Allow proxy batch tasks'))
|
92
|
+
setting('foreman_tasks_proxy_batch_size',
|
93
|
+
type: :integer,
|
94
|
+
description: N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'),
|
95
|
+
default: 100,
|
96
|
+
full_name: N_('Proxy tasks batch size'))
|
97
|
+
setting('foreman_tasks_troubleshooting_url',
|
98
|
+
type: :string,
|
99
|
+
description: N_('Url pointing to the task troubleshooting documentation. '\
|
100
|
+
'It should contain %{label} placeholder, that will be replaced with normalized task label '\
|
101
|
+
'(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
|
102
|
+
default: nil,
|
103
|
+
full_name: N_('Tasks troubleshooting URL'))
|
104
|
+
setting('foreman_tasks_polling_multiplier',
|
105
|
+
type: :integer,
|
106
|
+
description: N_('Polling multiplier which is used to multiply the default polling intervals. '\
|
107
|
+
'This can be used to prevent polling too frequently for long running tasks.'),
|
108
|
+
default: 1,
|
109
|
+
full_name: N_("Polling intervals multiplier"),
|
110
|
+
validate: { numericality: { greater_than: 0 } })
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
68
114
|
register_graphql_query_field :task, '::Types::Task', :record_field
|
69
115
|
register_graphql_query_field :tasks, '::Types::Task', :collection_field
|
70
116
|
register_graphql_query_field :recurring_logic, '::Types::RecurringLogic', :record_field
|
@@ -138,6 +184,7 @@ module ForemanTasks
|
|
138
184
|
Authorizer.prepend AuthorizerExt
|
139
185
|
User.include ForemanTasks::Concerns::UserExtensions
|
140
186
|
::Dynflow::Action::Polling.prepend ForemanTasks::Concerns::PollingActionExtensions
|
187
|
+
::Dynflow::ActiveJob::QueueAdapters::JobWrapper.include Actions::TaskSynchronization
|
141
188
|
end
|
142
189
|
|
143
190
|
rake_tasks do
|
@@ -241,42 +241,35 @@ namespace :foreman_tasks do
|
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
244
|
-
def csv_export(export_filename,
|
244
|
+
def csv_export(export_filename, tasks)
|
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
|
-
|
251
|
-
task.parent_task_id, task.started_at, task.ended_at, task.duration]
|
252
|
-
end
|
247
|
+
tasks.find_each do |task|
|
248
|
+
with_error_handling(task) do
|
249
|
+
csv << [task.id, task.state, task.type, task.label, task.result,
|
250
|
+
task.parent_task_id, task.started_at, task.ended_at, task.duration]
|
253
251
|
end
|
254
252
|
end
|
255
253
|
end
|
256
254
|
end
|
257
255
|
|
258
|
-
def html_export(workdir,
|
256
|
+
def html_export(workdir, tasks)
|
259
257
|
PageHelper.copy_assets(workdir)
|
260
258
|
|
261
|
-
ids = id_scope.pluck(:id)
|
262
259
|
renderer = TaskRender.new
|
263
|
-
|
264
|
-
total = ids.count
|
260
|
+
total = tasks.count(:all)
|
265
261
|
index = File.open(File.join(workdir, 'index.html'), 'w')
|
266
262
|
|
267
263
|
File.open(File.join(workdir, 'index.html'), 'w') do |index|
|
268
264
|
PageHelper.pagify(index) do |io|
|
269
265
|
PageHelper.generate_with_index(io) do |index|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
|
276
|
-
end
|
277
|
-
count += 1
|
278
|
-
puts "#{count}/#{total}"
|
266
|
+
tasks.find_each.each_with_index do |task, count|
|
267
|
+
content = with_error_handling(task) { renderer.render_task(task) }
|
268
|
+
if content
|
269
|
+
File.open(File.join(workdir, "#{task.id}.html"), 'w') { |file| PageHelper.pagify(file, content) }
|
270
|
+
with_error_handling(task, _('task index entry')) { PageHelper.generate_index_entry(index, task) }
|
279
271
|
end
|
272
|
+
puts "#{count + 1}/#{total}"
|
280
273
|
end
|
281
274
|
end
|
282
275
|
end
|
@@ -307,7 +300,7 @@ namespace :foreman_tasks do
|
|
307
300
|
end
|
308
301
|
end
|
309
302
|
|
310
|
-
SKIP_ERRORS = ['true', '1', 'y', 'yes'].include?
|
303
|
+
SKIP_ERRORS = ['true', '1', 'y', 'yes'].include? ENV['SKIP_FAILED'].downcase
|
311
304
|
|
312
305
|
filter = if ENV['TASK_SEARCH'].nil? && ENV['TASK_DAYS'].nil?
|
313
306
|
"started_at > \"#{7.days.ago.to_s(:db)}\" || " \
|
@@ -324,22 +317,21 @@ namespace :foreman_tasks do
|
|
324
317
|
format = ENV['TASK_FORMAT'] || 'html'
|
325
318
|
export_filename = ENV['TASK_FILE'] || generate_filename(format)
|
326
319
|
|
327
|
-
|
328
|
-
id_scope = task_scope.group(:id, :started_at)
|
320
|
+
tasks = ForemanTasks::Task.search_for(filter).order(:started_at => :desc).with_duration.distinct
|
329
321
|
|
330
322
|
puts _("Exporting all tasks matching filter #{filter}")
|
331
|
-
puts _("Gathering #{
|
323
|
+
puts _("Gathering #{tasks.count(:all)} tasks.")
|
332
324
|
case format
|
333
325
|
when 'html'
|
334
326
|
Dir.mktmpdir('task-export') do |tmp_dir|
|
335
|
-
html_export(tmp_dir,
|
327
|
+
html_export(tmp_dir, tasks)
|
336
328
|
system("tar", "czf", export_filename, tmp_dir)
|
337
329
|
end
|
338
330
|
when 'html-dir'
|
339
331
|
FileUtils.mkdir_p(export_filename)
|
340
|
-
html_export(export_filename,
|
332
|
+
html_export(export_filename, tasks)
|
341
333
|
when 'csv'
|
342
|
-
csv_export(export_filename,
|
334
|
+
csv_export(export_filename, tasks)
|
343
335
|
else
|
344
336
|
raise "Unkonwn export format '#{format}'"
|
345
337
|
end
|
@@ -11,7 +11,7 @@ module ForemanTasks
|
|
11
11
|
world_config = ForemanTasks.dynflow.config.world_config
|
12
12
|
if @use_in_memory_sqlite
|
13
13
|
world_config.persistence_adapter = lambda do |*_args|
|
14
|
-
::
|
14
|
+
::Dynflow::PersistenceAdapters::Sequel.new('adapter' => 'sqlite', 'database' => ':memory:')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
@test_in_thread_world = ::Dynflow::Testing::InThreadWorld.new(world_config)
|
@@ -24,7 +24,7 @@ FactoryBot.define do
|
|
24
24
|
ForemanTasks::Task.where(:external_id => execution_plan.id).delete_all
|
25
25
|
task.update!(:external_id => execution_plan.id)
|
26
26
|
if evaluator.sync_with_dynflow
|
27
|
-
task.update_from_dynflow(execution_plan
|
27
|
+
task.update_from_dynflow(execution_plan)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -15,7 +15,7 @@ module Actions
|
|
15
15
|
middleware.use KeepCurrentTaxonomies
|
16
16
|
execution_plan_hooks.use :null_hook, :on => :planning
|
17
17
|
|
18
|
-
def null_hook; end
|
18
|
+
def null_hook(plan); end
|
19
19
|
end
|
20
20
|
|
21
21
|
before do
|
@@ -78,6 +78,14 @@ module Actions
|
|
78
78
|
Organization.stubs(:current=)
|
79
79
|
Location.stubs(:current=)
|
80
80
|
|
81
|
+
org_finder = mock('organization finder')
|
82
|
+
org_finder.stubs(:find).with(@org.id).returns(@org)
|
83
|
+
Organization.stubs(:unscoped).returns(org_finder)
|
84
|
+
|
85
|
+
loc_finder = mock('location finder')
|
86
|
+
loc_finder.stubs(:find).with(@loc.id).returns(@loc)
|
87
|
+
Location.stubs(:unscoped).returns(loc_finder)
|
88
|
+
|
81
89
|
triggered = ForemanTasks.trigger(TestHookAction)
|
82
90
|
task = ForemanTasks::Task.where(:external_id => triggered.id).first
|
83
91
|
wait_for { task.reload.state == 'stopped' }
|
@@ -46,7 +46,6 @@ module ForemanTasks
|
|
46
46
|
|
47
47
|
it 'fetches batch_size of tasks and triggers them' do
|
48
48
|
remote_tasks.expects(:first).with(batch_size).returns(remote_tasks)
|
49
|
-
remote_tasks.expects(:size).returns(batch_size)
|
50
49
|
triggered.expects(:remote_tasks).returns(remote_tasks)
|
51
50
|
ForemanTasks::RemoteTask.expects(:batch_trigger).with(proxy_operation_name, grouped_remote_batch)
|
52
51
|
|
@@ -28,32 +28,6 @@ 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
|
57
31
|
end
|
58
32
|
end
|
59
33
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
2
|
-
import { addToast } from 'foremanReact/
|
2
|
+
import { addToast } from 'foremanReact/components/ToastsList';
|
3
3
|
import { TASKS_DASHBOARD_JS_QUERY_MODES } from '../TasksDashboard/TasksDashboardConstants';
|
4
4
|
import { timeToHoursNumber } from '../TasksDashboard/TasksDashboardHelper';
|
5
5
|
import {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { sprintf } from 'foremanReact/common/I18n';
|
2
2
|
import { API } from 'foremanReact/redux/API';
|
3
|
-
import { addToast } from 'foremanReact/
|
3
|
+
import { addToast } from 'foremanReact/components/ToastsList';
|
4
4
|
import {
|
5
5
|
TASKS_RESUME_REQUEST,
|
6
6
|
TASKS_RESUME_SUCCESS,
|
@@ -3,6 +3,18 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { Alert, Card, Row, Col } from 'patternfly-react';
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
|
6
|
+
const ConditionalLink = ({ children, link }) =>
|
7
|
+
link ? <a href={link}>{children}</a> : children;
|
8
|
+
|
9
|
+
ConditionalLink.propTypes = {
|
10
|
+
children: PropTypes.node.isRequired,
|
11
|
+
link: PropTypes.string,
|
12
|
+
};
|
13
|
+
|
14
|
+
ConditionalLink.defaultProps = {
|
15
|
+
link: null,
|
16
|
+
};
|
17
|
+
|
6
18
|
const Locks = ({ locks }) => (
|
7
19
|
<div>
|
8
20
|
<Alert type="info">
|
@@ -14,20 +26,22 @@ const Locks = ({ locks }) => (
|
|
14
26
|
<Row>
|
15
27
|
{locks.map((lock, key) => (
|
16
28
|
<Col xs={6} sm={4} md={4} key={key}>
|
17
|
-
<
|
18
|
-
<Card
|
19
|
-
<
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
<ConditionalLink link={lock.link}>
|
30
|
+
<Card className="card-pf-aggregate-status" accented>
|
31
|
+
<Card.Title>
|
32
|
+
<span
|
33
|
+
className={`fa ${
|
34
|
+
lock.exclusive ? 'fa-lock' : 'fa-unlock-alt'
|
35
|
+
}`}
|
36
|
+
/>
|
37
|
+
{lock.resource_type}
|
38
|
+
</Card.Title>
|
39
|
+
<Card.Body>
|
40
|
+
{`id:${lock.resource_id}`}
|
41
|
+
<br />
|
42
|
+
</Card.Body>
|
43
|
+
</Card>
|
44
|
+
</ConditionalLink>
|
31
45
|
</Col>
|
32
46
|
))}
|
33
47
|
</Row>
|
@@ -25,29 +25,33 @@ exports[`Locks rendering render with Props 1`] = `
|
|
25
25
|
sm={4}
|
26
26
|
xs={6}
|
27
27
|
>
|
28
|
-
<
|
29
|
-
|
30
|
-
aggregated={false}
|
31
|
-
aggregatedMini={false}
|
32
|
-
cardRef={null}
|
33
|
-
className="card-pf-aggregate-status"
|
34
|
-
matchHeight={false}
|
28
|
+
<ConditionalLink
|
29
|
+
link={null}
|
35
30
|
>
|
36
|
-
<
|
37
|
-
|
31
|
+
<Card
|
32
|
+
accented={true}
|
33
|
+
aggregated={false}
|
34
|
+
aggregatedMini={false}
|
35
|
+
cardRef={null}
|
36
|
+
className="card-pf-aggregate-status"
|
37
|
+
matchHeight={false}
|
38
38
|
>
|
39
|
-
<
|
40
|
-
className="
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
<CardTitle
|
40
|
+
className=""
|
41
|
+
>
|
42
|
+
<span
|
43
|
+
className="fa fa-unlock-alt"
|
44
|
+
/>
|
45
|
+
User
|
46
|
+
</CardTitle>
|
47
|
+
<CardBody
|
48
|
+
className=""
|
49
|
+
>
|
50
|
+
id:4
|
51
|
+
<br />
|
52
|
+
</CardBody>
|
53
|
+
</Card>
|
54
|
+
</ConditionalLink>
|
51
55
|
</Col>
|
52
56
|
<Col
|
53
57
|
bsClass="col"
|
@@ -57,29 +61,33 @@ exports[`Locks rendering render with Props 1`] = `
|
|
57
61
|
sm={4}
|
58
62
|
xs={6}
|
59
63
|
>
|
60
|
-
<
|
61
|
-
|
62
|
-
aggregated={false}
|
63
|
-
aggregatedMini={false}
|
64
|
-
cardRef={null}
|
65
|
-
className="card-pf-aggregate-status"
|
66
|
-
matchHeight={false}
|
64
|
+
<ConditionalLink
|
65
|
+
link={null}
|
67
66
|
>
|
68
|
-
<
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
</CardTitle>
|
76
|
-
<CardBody
|
77
|
-
className=""
|
67
|
+
<Card
|
68
|
+
accented={true}
|
69
|
+
aggregated={false}
|
70
|
+
aggregatedMini={false}
|
71
|
+
cardRef={null}
|
72
|
+
className="card-pf-aggregate-status"
|
73
|
+
matchHeight={false}
|
78
74
|
>
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
<CardTitle
|
76
|
+
className=""
|
77
|
+
>
|
78
|
+
<span
|
79
|
+
className="fa fa-unlock-alt"
|
80
|
+
/>
|
81
|
+
User
|
82
|
+
</CardTitle>
|
83
|
+
<CardBody
|
84
|
+
className=""
|
85
|
+
>
|
86
|
+
id:2
|
87
|
+
<br />
|
88
|
+
</CardBody>
|
89
|
+
</Card>
|
90
|
+
</ConditionalLink>
|
83
91
|
</Col>
|
84
92
|
</Row>
|
85
93
|
</CardGrid>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { API } from 'foremanReact/redux/API';
|
2
|
-
import { addToast } from 'foremanReact/
|
2
|
+
import { addToast } from 'foremanReact/components/ToastsList';
|
3
3
|
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
4
4
|
import {
|
5
5
|
BULK_CANCEL_PATH,
|
data/webpack/__mocks__/foremanReact/{redux/actions/toasts.js → components/ToastsList/index.js}
RENAMED
File without changes
|
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.
|
4
|
+
version: 5.3.0
|
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: 2021-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dynflow
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.6.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.6.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: get_process_mem
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- app/lib/actions/proxy_action.rb
|
167
167
|
- app/lib/actions/recurring_action.rb
|
168
168
|
- app/lib/actions/serializers/active_record_serializer.rb
|
169
|
+
- app/lib/actions/task_synchronization.rb
|
169
170
|
- app/lib/actions/trigger_proxy_batch.rb
|
170
171
|
- app/lib/foreman_tasks/concerns/polling_action_extensions.rb
|
171
172
|
- app/lib/proxy_api/foreman_dynflow/dynflow_proxy.rb
|
@@ -188,7 +189,6 @@ files:
|
|
188
189
|
- app/models/foreman_tasks/task_group_member.rb
|
189
190
|
- app/models/foreman_tasks/task_groups/recurring_logic_task_group.rb
|
190
191
|
- app/models/foreman_tasks/triggering.rb
|
191
|
-
- app/models/setting/foreman_tasks.rb
|
192
192
|
- app/services/foreman_tasks/proxy_selector.rb
|
193
193
|
- app/services/foreman_tasks/troubleshooting_help_generator.rb
|
194
194
|
- app/services/ui_notifications/tasks.rb
|
@@ -250,7 +250,9 @@ files:
|
|
250
250
|
- db/migrate/20200517215015_rename_bookmarks_controller.rb
|
251
251
|
- db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb
|
252
252
|
- db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb
|
253
|
+
- db/migrate/20210708123832_add_parent_task_id_to_remote_tasks.foreman_tasks.rb
|
253
254
|
- db/migrate/20210720115251_add_purpose_to_recurring_logic.rb
|
255
|
+
- db/migrate/20211123170430_tasks_settings_to_dsl_category.rb
|
254
256
|
- db/seeds.d/20-foreman_tasks_permissions.rb
|
255
257
|
- db/seeds.d/30-notification_blueprints.rb
|
256
258
|
- db/seeds.d/60-dynflow_proxy_feature.rb
|
@@ -271,7 +273,6 @@ files:
|
|
271
273
|
- lib/foreman_tasks/dynflow.rb
|
272
274
|
- lib/foreman_tasks/dynflow/configuration.rb
|
273
275
|
- lib/foreman_tasks/dynflow/console_authorizer.rb
|
274
|
-
- lib/foreman_tasks/dynflow/persistence.rb
|
275
276
|
- lib/foreman_tasks/engine.rb
|
276
277
|
- lib/foreman_tasks/task_error.rb
|
277
278
|
- lib/foreman_tasks/tasks/cleanup.rake
|
@@ -572,6 +573,7 @@ files:
|
|
572
573
|
- webpack/__mocks__/foremanReact/components/ForemanModal/index.js
|
573
574
|
- webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js
|
574
575
|
- webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js
|
576
|
+
- webpack/__mocks__/foremanReact/components/ToastsList/index.js
|
575
577
|
- webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js
|
576
578
|
- webpack/__mocks__/foremanReact/components/common/MessageBox.js
|
577
579
|
- webpack/__mocks__/foremanReact/components/common/dates/LongDateTime.js
|
@@ -581,7 +583,6 @@ files:
|
|
581
583
|
- webpack/__mocks__/foremanReact/constants.js
|
582
584
|
- webpack/__mocks__/foremanReact/redux/API/APISelectors.js
|
583
585
|
- webpack/__mocks__/foremanReact/redux/API/index.js
|
584
|
-
- webpack/__mocks__/foremanReact/redux/actions/toasts.js
|
585
586
|
- webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware.js
|
586
587
|
- webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js
|
587
588
|
- webpack/__mocks__/foremanReact/routes/common/PageLayout/components/ExportButton/ExportButton.js
|
@@ -607,7 +608,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
607
608
|
- !ruby/object:Gem::Version
|
608
609
|
version: '0'
|
609
610
|
requirements: []
|
610
|
-
rubygems_version: 3.1.
|
611
|
+
rubygems_version: 3.1.2
|
611
612
|
signing_key:
|
612
613
|
specification_version: 4
|
613
614
|
summary: Foreman plugin for showing tasks information for resources and users
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class Setting::ForemanTasks < Setting
|
2
|
-
def self.default_settings
|
3
|
-
[
|
4
|
-
set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120, N_('Sync task timeout')),
|
5
|
-
set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true, N_('Enable dynflow console')),
|
6
|
-
set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true, N_('Require auth for dynflow console')),
|
7
|
-
set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4, N_('Proxy action retry count')),
|
8
|
-
set('foreman_tasks_proxy_action_retry_interval', N_('Time in seconds between retries'), 15, N_('Proxy action retry interval')),
|
9
|
-
set('foreman_tasks_proxy_batch_trigger', N_('Allow triggering tasks on the smart proxy in batches'), true, N_('Allow proxy batch tasks')),
|
10
|
-
set('foreman_tasks_proxy_batch_size', N_('Number of tasks which should be sent to the smart proxy in one request, if foreman_tasks_proxy_batch_trigger is enabled'), 100, N_('Proxy tasks batch size')),
|
11
|
-
set('foreman_tasks_troubleshooting_url',
|
12
|
-
N_('Url pointing to the task troubleshooting documentation. '\
|
13
|
-
'It should contain %{label} placeholder, that will be replaced with normalized task label '\
|
14
|
-
'(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
|
15
|
-
nil, N_('Tasks troubleshooting URL')),
|
16
|
-
set('foreman_tasks_polling_multiplier',
|
17
|
-
N_('Polling multiplier which is used to multiply the default polling intervals. '\
|
18
|
-
'This can be used to prevent polling too frequently for long running tasks.'),
|
19
|
-
1,
|
20
|
-
N_("Polling intervals multiplier")),
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.load_defaults
|
25
|
-
Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
|
26
|
-
Setting::NONZERO_ATTRS.push('foreman_tasks_polling_multiplier')
|
27
|
-
super
|
28
|
-
end
|
29
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module ForemanTasks
|
2
|
-
# Wrap the Dynflow persistence to reflect the changes to execution plan
|
3
|
-
# in the Task model. This is probably a temporary solution and
|
4
|
-
# Dynflow will probably get more events-based API but it should be enought
|
5
|
-
# for start, until the requiements on the API are clear enough.
|
6
|
-
class Dynflow::Persistence < ::Dynflow::PersistenceAdapters::Sequel
|
7
|
-
def save_execution_plan(execution_plan_id, value)
|
8
|
-
# clear connection only if not running in some active record transaction already
|
9
|
-
clear_connections = ActiveRecord::Base.connection.open_transactions.zero?
|
10
|
-
super.tap do
|
11
|
-
on_execution_plan_save(execution_plan_id, value)
|
12
|
-
rescue => e
|
13
|
-
Foreman::Logging.exception('Error on on_execution_plan_save event', e,
|
14
|
-
:logger => 'dynflow')
|
15
|
-
end
|
16
|
-
ensure
|
17
|
-
::ActiveRecord::Base.clear_active_connections! if clear_connections
|
18
|
-
end
|
19
|
-
|
20
|
-
def on_execution_plan_save(execution_plan_id, data)
|
21
|
-
# We can load the data unless the execution plan was properly planned and saved
|
22
|
-
# including its steps
|
23
|
-
case data[:state]
|
24
|
-
when :pending
|
25
|
-
task = ForemanTasks::Task::DynflowTask.new_for_execution_plan(execution_plan_id, data)
|
26
|
-
task.start_at ||= Time.zone.now
|
27
|
-
task.save!
|
28
|
-
when :scheduled
|
29
|
-
delayed_plan = load_delayed_plan(execution_plan_id)
|
30
|
-
raise Foreman::Exception, 'Plan is delayed but the delay record is missing' if delayed_plan.nil?
|
31
|
-
task = ::ForemanTasks::Task::DynflowTask.find_by!(:external_id => execution_plan_id)
|
32
|
-
task.update_from_dynflow(data.merge(:start_at => delayed_plan[:start_at],
|
33
|
-
:start_before => delayed_plan[:start_before]))
|
34
|
-
when :planning
|
35
|
-
task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan_id).first
|
36
|
-
task.update_from_dynflow(data)
|
37
|
-
else
|
38
|
-
if (task = ::ForemanTasks::Task::DynflowTask.where(:external_id => execution_plan_id).first)
|
39
|
-
unless task.state.to_s == data[:state].to_s
|
40
|
-
task.update_from_dynflow(data)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|