foreman-tasks 5.2.3 → 5.3.0
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/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
|