foreman-tasks 1.0.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -12
  3. data/.rubocop_todo.yml +34 -116
  4. data/README.md +2 -0
  5. data/app/controllers/foreman_tasks/api/recurring_logics_controller.rb +20 -1
  6. data/app/controllers/foreman_tasks/api/tasks_controller.rb +65 -10
  7. data/app/controllers/foreman_tasks/concerns/hosts_controller_extension.rb +1 -1
  8. data/app/controllers/foreman_tasks/recurring_logics_controller.rb +19 -0
  9. data/app/controllers/foreman_tasks/tasks_controller.rb +9 -14
  10. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +1 -3
  11. data/app/lib/actions/helpers/humanizer.rb +1 -3
  12. data/app/lib/actions/proxy_action.rb +33 -12
  13. data/app/lib/foreman_tasks/concerns/polling_action_extensions.rb +12 -0
  14. data/app/models/foreman_tasks/concerns/action_triggering.rb +1 -1
  15. data/app/models/foreman_tasks/recurring_logic.rb +1 -0
  16. data/app/models/foreman_tasks/remote_task.rb +1 -0
  17. data/app/models/foreman_tasks/task.rb +8 -0
  18. data/app/models/foreman_tasks/task/dynflow_task.rb +2 -1
  19. data/app/models/foreman_tasks/task/search.rb +11 -1
  20. data/app/models/setting/foreman_tasks.rb +7 -2
  21. data/app/services/foreman_tasks/troubleshooting_help_generator.rb +0 -4
  22. data/app/services/ui_notifications/tasks/task_bulk_cancel.rb +36 -0
  23. data/app/services/ui_notifications/tasks/task_bulk_resume.rb +38 -0
  24. data/app/services/ui_notifications/tasks/task_bulk_stop.rb +36 -0
  25. data/app/views/foreman_tasks/api/recurring_logics/base.json.rabl +2 -1
  26. data/app/views/foreman_tasks/api/tasks/details.json.rabl +1 -1
  27. data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
  28. data/app/views/foreman_tasks/recurring_logics/index.html.erb +30 -0
  29. data/app/views/foreman_tasks/tasks/show.html.erb +3 -0
  30. data/config/routes.rb +8 -0
  31. data/db/migrate/20200517215015_rename_bookmarks_controller.rb +35 -0
  32. data/db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb +5 -0
  33. data/db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb +9 -0
  34. data/db/seeds.d/30-notification_blueprints.rb +21 -0
  35. data/foreman-tasks.gemspec +5 -6
  36. data/gemfile.d/foreman-tasks.rb +1 -0
  37. data/lib/foreman_tasks/dynflow/console_authorizer.rb +2 -2
  38. data/lib/foreman_tasks/engine.rb +17 -19
  39. data/lib/foreman_tasks/tasks/cleanup.rake +1 -1
  40. data/lib/foreman_tasks/tasks/export_tasks.rake +2 -2
  41. data/lib/foreman_tasks/test_extensions.rb +1 -1
  42. data/lib/foreman_tasks/version.rb +1 -1
  43. data/locale/action_names.rb +2 -2
  44. data/locale/en/LC_MESSAGES/foreman_tasks.mo +0 -0
  45. data/locale/en/foreman_tasks.po +270 -54
  46. data/locale/foreman_tasks.pot +630 -292
  47. data/locale/fr/LC_MESSAGES/foreman_tasks.mo +0 -0
  48. data/locale/fr/foreman_tasks.po +817 -0
  49. data/locale/ja/LC_MESSAGES/foreman_tasks.mo +0 -0
  50. data/locale/ja/foreman_tasks.po +817 -0
  51. data/locale/zh_CN/LC_MESSAGES/foreman_tasks.mo +0 -0
  52. data/locale/zh_CN/foreman_tasks.po +816 -0
  53. data/package.json +1 -2
  54. data/script/rails +2 -2
  55. data/script/travis_run_js_tests.sh +2 -2
  56. data/test/factories/task_factory.rb +34 -2
  57. data/test/foreman_tasks_test_helper.rb +4 -0
  58. data/test/lib/concerns/polling_action_extensions_test.rb +34 -0
  59. data/test/unit/actions/action_with_sub_plans_test.rb +1 -1
  60. data/test/unit/task_test.rb +160 -74
  61. data/webpack/ForemanTasks/Components/TaskActions/TaskAction.test.js +60 -0
  62. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +67 -0
  63. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +46 -0
  64. data/webpack/ForemanTasks/Components/TaskActions/TaskActionsConstants.js +16 -0
  65. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.js +60 -0
  66. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.test.js +14 -0
  67. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/TaskAction.test.js.snap +233 -0
  68. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/UnlockModals.test.js.snap +25 -0
  69. data/webpack/ForemanTasks/Components/TaskActions/index.js +115 -0
  70. data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +17 -3
  71. data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +132 -165
  72. data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +3 -12
  73. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +8 -1
  74. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +68 -3
  75. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +0 -1
  76. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/RunningSteps.test.js.snap +1 -1
  77. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +108 -75
  78. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +3 -9
  79. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +35 -5
  80. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +34 -14
  81. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +3 -4
  82. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +0 -6
  83. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +4 -10
  84. data/webpack/ForemanTasks/Components/TaskDetails/TasksDetailsHelper.js +6 -1
  85. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.fixtures.js +8 -0
  86. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +7 -1
  87. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +18 -2
  88. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +30 -13
  89. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +91 -0
  90. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +13 -4
  91. data/webpack/ForemanTasks/Components/TaskDetails/index.js +6 -8
  92. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardActions.js +1 -1
  93. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardConstants.js +5 -0
  94. data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboardHelper.js +3 -2
  95. data/webpack/ForemanTasks/Components/TasksDashboard/__tests__/TasksDashboardActions.test.js +2 -2
  96. data/webpack/ForemanTasks/Components/TasksTable/Components/ActionSelectButton.js +14 -1
  97. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModal.js +83 -0
  98. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalActions.js +106 -0
  99. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalReducer.js +38 -0
  100. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalSelectors.js +45 -0
  101. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModal.test.js +36 -0
  102. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalActions.test.js +205 -0
  103. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalReducer.test.js +27 -0
  104. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalSelectors.test.js +54 -0
  105. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModal.test.js.snap +41 -0
  106. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalReducer.test.js.snap +19 -0
  107. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalSelectors.test.js.snap +30 -0
  108. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/index.js +29 -0
  109. data/webpack/ForemanTasks/Components/TasksTable/Components/SelectAllAlert.js +43 -0
  110. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ActionSelectButton.test.js +1 -0
  111. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/SelectAllAlert.test.js +29 -0
  112. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap +11 -0
  113. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/SelectAllAlert.test.js.snap +75 -0
  114. data/webpack/ForemanTasks/Components/TasksTable/SubTasksPage.js +4 -1
  115. data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +247 -0
  116. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +37 -19
  117. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +56 -92
  118. data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +19 -11
  119. data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +4 -3
  120. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +64 -73
  121. data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +21 -2
  122. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSchema.js +2 -2
  123. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +13 -4
  124. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +147 -0
  125. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +3 -10
  126. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +46 -74
  127. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +17 -1
  128. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTablePage.test.js +9 -1
  129. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +22 -1
  130. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +4 -12
  131. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +336 -0
  132. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +3 -12
  133. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +22 -158
  134. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +56 -132
  135. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +34 -0
  136. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
  137. data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +2 -2
  138. data/webpack/ForemanTasks/Components/TasksTable/index.js +10 -4
  139. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.js +47 -19
  140. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +61 -14
  141. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +80 -21
  142. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.scss +9 -0
  143. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.test.js +44 -0
  144. data/webpack/ForemanTasks/Components/common/ClickConfirmation/__snapshots__/ClickConfirmation.test.js.snap +52 -0
  145. data/webpack/ForemanTasks/Components/common/ClickConfirmation/index.js +59 -66
  146. data/webpack/ForemanTasks/Components/common/ToastsHelpers/ToastTypesConstants.js +11 -0
  147. data/webpack/ForemanTasks/Components/common/ToastsHelpers/index.js +15 -0
  148. data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
  149. data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.test.js +2 -1
  150. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +2 -2
  151. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +17 -3
  152. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
  153. data/webpack/__mocks__/foremanReact/{API.js → redux/API.js} +1 -1
  154. metadata +76 -27
  155. data/webpack/ForemanTasks/Components/TasksTable/Components/CancelConfirm.js +0 -53
  156. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmationModals.js +0 -56
  157. data/webpack/ForemanTasks/Components/TasksTable/Components/ResumeConfirm.js +0 -52
  158. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/CancelConfirm.test.js +0 -26
  159. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ConfirmationModals.test.js +0 -24
  160. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ResumeConfirm.test.js +0 -26
  161. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/CancelConfirm.test.js.snap +0 -65
  162. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ConfirmationModals.test.js.snap +0 -30
  163. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ResumeConfirm.test.js.snap +0 -63
  164. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.js +0 -23
  165. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.test.js +0 -26
  166. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.js +0 -23
  167. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.test.js +0 -27
  168. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/CancelButton.test.js.snap +0 -15
  169. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ResumeButton.test.js.snap +0 -15
@@ -3,8 +3,6 @@ module ForemanTasks
3
3
  include Foreman::Controller::AutoCompleteSearch
4
4
  include Foreman::Controller::CsvResponder
5
5
 
6
- before_action :restrict_dangerous_actions, :only => [:unlock, :force_unlock]
7
-
8
6
  def show
9
7
  @task = resource_base.find(params[:id])
10
8
  render :layout => !request.xhr?
@@ -33,9 +31,12 @@ module ForemanTasks
33
31
 
34
32
  def cancel_step
35
33
  task = find_dynflow_task
36
- flash[:info] = _('Trying to cancel step %s') % params[:step_id]
37
- ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
38
- redirect_to foreman_tasks_task_path(task)
34
+ result = ForemanTasks.dynflow.world.event(task.external_id, params[:step_id].to_i, ::Dynflow::Action::Cancellable::Cancel).wait
35
+ if result.rejected?
36
+ render json: { error: result.reason }, status: :bad_request
37
+ else
38
+ render json: { statusText: 'OK' }
39
+ end
39
40
  end
40
41
 
41
42
  def cancel
@@ -72,19 +73,17 @@ module ForemanTasks
72
73
  if task.paused?
73
74
  task.state = :stopped
74
75
  task.save!
75
- flash[:info] = _('The task resources were unlocked.')
76
+ render json: { statusText: 'OK' }
76
77
  else
77
- flash[:warning] = _('The execution has to be paused.')
78
+ render json: {}, status: :bad_request
78
79
  end
79
- redirect_back(:fallback_location => foreman_tasks_task_path(task))
80
80
  end
81
81
 
82
82
  def force_unlock
83
83
  task = find_dynflow_task
84
84
  task.state = :stopped
85
85
  task.save!
86
- flash[:info] = _('The task resources were unlocked with force.')
87
- redirect_back(:fallback_location => foreman_tasks_task_path(task))
86
+ render json: { statusText: 'OK' }
88
87
  end
89
88
 
90
89
  # we need do this to make the Foreman helpers working properly
@@ -103,10 +102,6 @@ module ForemanTasks
103
102
  csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
104
103
  end
105
104
 
106
- def restrict_dangerous_actions
107
- render_403 unless Setting['dynflow_allow_dangerous_actions']
108
- end
109
-
110
105
  def controller_permission
111
106
  'foreman_tasks'
112
107
  end
@@ -1,5 +1,4 @@
1
1
  module ForemanTasks
2
- # rubocop:disable Metrics/ModuleLength
3
2
  module ForemanTasksHelper
4
3
  def recurring_logic_state(recurring_logic)
5
4
  icon, status = case recurring_logic.state
@@ -28,7 +27,7 @@ module ForemanTasks
28
27
  def troubleshooting_info_text
29
28
  return if @task.state != 'paused' || @task.main_action.nil?
30
29
  helper = TroubleshootingHelpGenerator.new(@task.main_action)
31
- helper.generate_text
30
+ helper.generate_html
32
31
  end
33
32
 
34
33
  def username_link_task(owner, username)
@@ -209,5 +208,4 @@ module ForemanTasks
209
208
  tags.join.html_safe
210
209
  end
211
210
  end
212
- # rubocop:enable Metrics/ModuleLength
213
211
  end
@@ -78,9 +78,7 @@ module Actions
78
78
  else
79
79
  head, *tail = subkeys
80
80
  if data.is_a?(Hash) && data.key?(head)
81
- return fetch_data(data[head], *tail)
82
- else
83
- return nil
81
+ fetch_data(data[head], *tail)
84
82
  end
85
83
  end
86
84
  end
@@ -41,12 +41,7 @@ module Actions
41
41
  with_connection_error_handling(event) do |event|
42
42
  case event
43
43
  when nil
44
- if remote_task
45
- on_resume
46
- else
47
- trigger_proxy_task
48
- end
49
- suspend
44
+ start_or_resume
50
45
  when ::Dynflow::Action::Skip
51
46
  # do nothing
52
47
  when ::Dynflow::Action::Cancellable::Cancel
@@ -77,6 +72,12 @@ module Actions
77
72
  end
78
73
  end
79
74
 
75
+ def trigger_remote_task
76
+ suspend do |_suspended_action|
77
+ ForemanTasks::RemoteTask.batch_trigger(remote_task.operation, [remote_task])
78
+ end
79
+ end
80
+
80
81
  def proxy_input(task_id = task.id)
81
82
  input.merge(:callback => { :task_id => task_id,
82
83
  :step_id => run_step_id })
@@ -88,7 +89,7 @@ module Actions
88
89
  if response['result'] == 'error'
89
90
  raise ::Foreman::Exception, _('The smart proxy task %s failed.') % proxy_task_id
90
91
  else
91
- on_data(response['actions'].find { |block_action| block_action['class'] == proxy_action_name }['output'])
92
+ on_data(get_proxy_data(response))
92
93
  end
93
94
  else
94
95
  suspend
@@ -154,10 +155,8 @@ module Actions
154
155
  if output.key?(:proxy_output) || state == :error
155
156
  output.fetch(:proxy_output, {})
156
157
  elsif live && proxy_task_id
157
- proxy_data = proxy.status_of_task(proxy_task_id)['actions'].detect do |action|
158
- action['class'] == proxy_action_name || action.fetch('input', {})['proxy_operation_name'] == proxy_operation_name
159
- end
160
- proxy_data.fetch('output', {})
158
+ response = proxy.status_of_task(proxy_task_id)
159
+ get_proxy_data(response)
161
160
  else
162
161
  {}
163
162
  end
@@ -205,6 +204,26 @@ module Actions
205
204
 
206
205
  private
207
206
 
207
+ def start_or_resume
208
+ if remote_task
209
+ if remote_task.state == 'external'
210
+ trigger_remote_task
211
+ else
212
+ on_resume
213
+ end
214
+ else
215
+ trigger_proxy_task
216
+ end
217
+ suspend
218
+ end
219
+
220
+ def get_proxy_data(response)
221
+ proxy_data = response['actions'].detect do |action|
222
+ action['class'] == proxy_action_name || action.fetch('input', {})['proxy_operation_name'] == proxy_operation_name
223
+ end
224
+ proxy_data.fetch('output', {})
225
+ end
226
+
208
227
  def proxy_version(proxy)
209
228
  match = proxy.statuses[:version].version['version'].match(/(\d+)\.(\d+)\.(\d+)/)
210
229
  { :major => match[1].to_i, :minor => match[2].to_i, :patch => match[3].to_i }
@@ -247,10 +266,12 @@ module Actions
247
266
  end
248
267
 
249
268
  def prepare_remote_task
269
+ state = input[:use_concurrency_control] ? 'external' : 'new'
250
270
  ::ForemanTasks::RemoteTask.new(:execution_plan_id => execution_plan_id,
251
271
  :proxy_url => input[:proxy_url],
252
272
  :step_id => run_step_id,
253
- :operation => proxy_operation_name)
273
+ :operation => proxy_operation_name,
274
+ :state => state)
254
275
  end
255
276
 
256
277
  def proxy_task_id
@@ -0,0 +1,12 @@
1
+ module ForemanTasks
2
+ module Concerns
3
+ module PollingActionExtensions
4
+ def poll_intervals
5
+ multiplier = Setting[:foreman_tasks_polling_multiplier] || 1
6
+
7
+ # Prevent the intervals from going below 0.5 seconds
8
+ super.map { |interval| [interval * multiplier, 0.5].max }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -158,7 +158,7 @@ module ForemanTasks
158
158
  end
159
159
  end
160
160
  end
161
- return true
161
+ true
162
162
  ensure
163
163
  # to not execute the same execution plan twice in a row
164
164
  @execution_plan = nil
@@ -14,6 +14,7 @@ module ForemanTasks
14
14
  scoped_search :on => :max_iteration, :complete_value => false, :rename => :iteration_limit
15
15
  scoped_search :on => :iteration, :complete_value => false
16
16
  scoped_search :on => :cron_line, :complete_value => true
17
+ scoped_search :on => :state, :complete_value => true
17
18
 
18
19
  before_create do
19
20
  task_group.save
@@ -9,6 +9,7 @@ module ForemanTasks
9
9
 
10
10
  scope :triggered, -> { where(:state => 'triggered') }
11
11
  scope :pending, -> { where(:state => 'new') }
12
+ scope :external, -> { where(:state => 'external') }
12
13
 
13
14
  delegate :proxy_action_name, :to => :action
14
15
 
@@ -42,6 +42,10 @@ module ForemanTasks
42
42
  scoped_search :on => :start_at, :complete_value => false
43
43
  scoped_search :on => :ended_at, :complete_value => false
44
44
  scoped_search :on => :parent_task_id, :complete_value => true
45
+ scoped_search :on => :duration, :complete_value => false, :ext_method => :search_by_duration, :operators => %w[> >= = <= <], :only_explicit => true
46
+
47
+ # Note: the following searches may return duplicates, this is due to
48
+ # one task maybe having multiple locks (e.g. read/write) for the same resource_id
45
49
  scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'location_id', :ext_method => :search_by_taxonomy, :only_explicit => true
46
50
  scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'organization_id', :ext_method => :search_by_taxonomy, :only_explicit => true
47
51
  scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => 'resource_type', :ext_method => :search_by_generic_resource, :only_explicit => true
@@ -66,6 +70,10 @@ module ForemanTasks
66
70
  end)
67
71
  scope :for_action_types, (->(action_types) { where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
68
72
 
73
+ class Jail < Safemode::Jail
74
+ allow :started_at, :ended_at, :result, :state, :label, :main_action
75
+ end
76
+
69
77
  def input
70
78
  {}
71
79
  end
@@ -119,6 +119,7 @@ module ForemanTasks
119
119
  begin
120
120
  f_action = f.action(execution_plan)
121
121
  {
122
+ id: f_action.id,
122
123
  action_class: f.action_class.name,
123
124
  state: f.state,
124
125
  input: f_action.input.pretty_inspect,
@@ -228,7 +229,7 @@ module ForemanTasks
228
229
  # if we fail updating the data from dynflow, it usually means there is something
229
230
  # odd with the data consistency and at this point it is not possible to resume, switching
230
231
  # the task to stopped/error
231
- task.update_attributes(:state => 'stopped', :result => 'error')
232
+ task.update(:state => 'stopped', :result => 'error')
232
233
  Foreman::Logging.exception("Failed at consistency check for task #{task.id}", e, :logger => 'foreman-tasks')
233
234
  end
234
235
  end
@@ -4,7 +4,8 @@ module ForemanTasks
4
4
  def search_by_generic_resource(key, operator, value)
5
5
  key = 'resource_type' if key.blank?
6
6
  key_name = connection.quote_column_name(key.sub(/^.*\./, ''))
7
- condition = sanitize_sql_for_conditions(["foreman_tasks_locks.#{key_name} #{operator} ?", value])
7
+ value = value.split(',') if operator.index(/IN/i)
8
+ condition = sanitize_sql_for_conditions(["foreman_tasks_locks.#{key_name} #{operator} (?)", value])
8
9
 
9
10
  { :conditions => condition, :joins => :locks }
10
11
  end
@@ -22,6 +23,15 @@ module ForemanTasks
22
23
  sql = "foreman_tasks_locks_taxonomy#{uniq_suffix}.resource_id #{operator} ? OR foreman_tasks_locks_taxonomy#{uniq_suffix}.resource_id IS NULL"
23
24
  { :conditions => sanitize_sql_for_conditions([sql, value]), :joins => joins }
24
25
  end
26
+
27
+ # Expects the time in the format "\d+ (seconds|minutes|hours|days|months|years)"
28
+ SUPPORTED_DURATION_FORMAT = /\A\s*(\d+(\s+\b(seconds?|minutes?|hours?|days?|months?|years?)\b)?)\b\s*\z/i.freeze
29
+ def search_by_duration(_key, operator, value)
30
+ raise "Unsupported duration '#{value}' specified for searching" unless value =~ SUPPORTED_DURATION_FORMAT
31
+ value = value.strip
32
+ { :conditions => "coalesce(ended_at, current_timestamp) - coalesce(coalesce(started_at, ended_at), current_timestamp) #{operator} ?::interval",
33
+ :parameter => [value] }
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -2,7 +2,6 @@ class Setting::ForemanTasks < Setting
2
2
  def self.default_settings
3
3
  [
4
4
  set('foreman_tasks_sync_task_timeout', N_('Number of seconds to wait for synchronous task to finish.'), 120),
5
- set('dynflow_allow_dangerous_actions', N_('Allow unlocking actions which can have dangerous consequences.'), false),
6
5
  set('dynflow_enable_console', N_('Enable the dynflow console (/foreman_tasks/dynflow) for debugging'), true),
7
6
  set('dynflow_console_require_auth', N_('Require user to be authenticated as user with admin rights when accessing dynflow console'), true),
8
7
  set('foreman_tasks_proxy_action_retry_count', N_('Number of attempts to start a task on the smart proxy before failing'), 4),
@@ -13,12 +12,18 @@ class Setting::ForemanTasks < Setting
13
12
  N_('Url pointing to the task troubleshooting documentation. '\
14
13
  'It should contain %{label} placeholder, that will be replaced with normalized task label '\
15
14
  '(restricted to only alphanumeric characters)). %{version} placeholder is also available.'),
16
- nil)
15
+ nil),
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"))
17
21
  ]
18
22
  end
19
23
 
20
24
  def self.load_defaults
21
25
  Setting::BLANK_ATTRS.push('foreman_tasks_troubleshooting_url')
26
+ Setting::NONZERO_ATTRS.push('foreman_tasks_polling_multiplier')
22
27
  super
23
28
  end
24
29
  end
@@ -44,10 +44,6 @@ module ForemanTasks
44
44
  # rubocop:enable Rails/OutputSafety
45
45
  end
46
46
 
47
- def generate_text
48
- (description + link_descriptions_html).join("\n")
49
- end
50
-
51
47
  def link_descriptions_html
52
48
  links.map do |link|
53
49
  link.description % { link: %(<a href="%{href}">%{title}</a>) % link.to_h }
@@ -0,0 +1,36 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskBulkCancel < ::UINotifications::Base
4
+ def initialize(task, cancelled_length, skipped_length)
5
+ @subject = task
6
+ @cancelled_length = cancelled_length
7
+ @skipped_length = skipped_length
8
+ end
9
+
10
+ def create
11
+ Notification.create!(
12
+ initiator: initiator,
13
+ audience: audience,
14
+ subject: subject,
15
+ notification_blueprint: blueprint,
16
+ message: message,
17
+ notification_recipients: [NotificationRecipient.create({ :user => User.current })]
18
+ )
19
+ end
20
+
21
+ def audience
22
+ Notification::AUDIENCE_GLOBAL
23
+ end
24
+
25
+ def message
26
+ ('%{cancelled} Tasks were cancelled. %{skipped} Tasks were skipped. ' %
27
+ { cancelled: @cancelled_length,
28
+ skipped: @skipped_length })
29
+ end
30
+
31
+ def blueprint
32
+ @blueprint ||= NotificationBlueprint.find_by(name: 'tasks_bulk_cancel')
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskBulkResume < ::UINotifications::Base
4
+ def initialize(task, resumed_length, failed_length, skipped_length)
5
+ @subject = task
6
+ @resumed_length = resumed_length
7
+ @failed_length = failed_length
8
+ @skipped_length = skipped_length
9
+ end
10
+
11
+ def create
12
+ Notification.create!(
13
+ initiator: initiator,
14
+ audience: audience,
15
+ subject: subject,
16
+ notification_blueprint: blueprint,
17
+ message: message,
18
+ notification_recipients: [NotificationRecipient.create({ :user => User.current })]
19
+ )
20
+ end
21
+
22
+ def audience
23
+ Notification::AUDIENCE_USER
24
+ end
25
+
26
+ def message
27
+ ('%{resumed} Tasks were resumed. %{failed} Tasks failed to resume. %{skipped} Tasks were skipped. ' %
28
+ { resumed: @resumed_length,
29
+ failed: @failed_length,
30
+ skipped: @skipped_length })
31
+ end
32
+
33
+ def blueprint
34
+ @blueprint ||= NotificationBlueprint.find_by(name: 'tasks_bulk_resume')
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ module UINotifications
2
+ module Tasks
3
+ class TaskBulkStop < ::UINotifications::Base
4
+ def initialize(task, stopped_length, skipped_length)
5
+ @subject = task
6
+ @stopped_length = stopped_length
7
+ @skipped_length = skipped_length
8
+ end
9
+
10
+ def create
11
+ Notification.create!(
12
+ initiator: initiator,
13
+ audience: audience,
14
+ subject: subject,
15
+ notification_blueprint: blueprint,
16
+ message: message,
17
+ notification_recipients: [NotificationRecipient.create(:user => User.current)]
18
+ )
19
+ end
20
+
21
+ def audience
22
+ Notification::AUDIENCE_GLOBAL
23
+ end
24
+
25
+ def message
26
+ ('%{stopped} Tasks were stopped. %{skipped} Tasks were already stopped. ' %
27
+ { stopped: @stopped_length,
28
+ skipped: @skipped_length })
29
+ end
30
+
31
+ def blueprint
32
+ @blueprint ||= NotificationBlueprint.find_by(name: 'tasks_bulk_stop')
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,4 @@
1
1
  object @recurring_logic
2
2
 
3
- attributes :id, :cron_line, :end_time, :iteration, :task_group_id, :state
3
+ attributes :id, :cron_line, :end_time, :iteration, :task_group_id, :state,
4
+ :max_iteration
@@ -9,10 +9,10 @@ node(:failed_steps) { @task.input_output_failed_steps }
9
9
  node(:running_steps) { @task.input_output_running_steps }
10
10
  node(:help) { troubleshooting_info_text }
11
11
  node(:has_sub_tasks) { @task.sub_tasks.any? }
12
- node(:allowDangerousActions) { Setting['dynflow_allow_dangerous_actions'] }
13
12
  node(:locks) do
14
13
  @task.locks.map do |lock|
15
14
  { name: lock.name, exclusive: lock.exclusive, resource_type: lock.resource_type, resource_id: lock.resource_id }
16
15
  end
17
16
  end
18
17
  node(:username_path) { username_link_task(@task.owner, @task.username) }
18
+ node(:dynflow_enable_console) { Setting['dynflow_enable_console'] }