foreman_remote_execution 1.2.2 → 1.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -0
  3. data/app/assets/javascripts/template_invocation.js +6 -11
  4. data/app/controllers/api/v2/job_templates_controller.rb +1 -0
  5. data/app/controllers/concerns/foreman/controller/parameters/remote_execution_feature.rb +1 -1
  6. data/app/controllers/job_invocations_controller.rb +6 -2
  7. data/app/controllers/job_templates_controller.rb +0 -5
  8. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +18 -2
  9. data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +1 -2
  10. data/app/helpers/remote_execution_helper.rb +42 -41
  11. data/app/lib/actions/remote_execution/run_host_job.rb +2 -15
  12. data/app/lib/actions/remote_execution/run_hosts_job.rb +17 -3
  13. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -6
  14. data/app/models/input_template_renderer.rb +1 -1
  15. data/app/models/job_invocation.rb +23 -5
  16. data/app/models/remote_execution_feature.rb +16 -3
  17. data/app/models/setting/remote_execution.rb +4 -1
  18. data/app/models/ssh_execution_provider.rb +33 -2
  19. data/app/models/targeting.rb +7 -3
  20. data/app/views/api/v2/remote_execution_features/base.json.rabl +1 -1
  21. data/app/views/job_invocations/_form.html.erb +1 -4
  22. data/app/views/job_invocations/_host_name_td.html.erb +2 -1
  23. data/app/views/job_invocations/index.html.erb +1 -1
  24. data/app/views/job_invocations/show.html.erb +1 -1
  25. data/app/views/job_templates/_custom_tabs.html.erb +1 -4
  26. data/app/views/overrides/nics/_execution_interface.html.erb +2 -3
  27. data/app/views/remote_execution_features/index.html.erb +1 -1
  28. data/db/migrate/20170110145641_add_host_action_button_to_remote_execution_feature.rb +5 -0
  29. data/foreman_remote_execution.gemspec +1 -1
  30. data/lib/foreman_remote_execution/engine.rb +3 -1
  31. data/lib/foreman_remote_execution/version.rb +1 -1
  32. data/locale/action_names.rb +2 -5
  33. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  34. data/locale/de/foreman_remote_execution.po +13 -23
  35. data/locale/en/foreman_remote_execution.po +12 -22
  36. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  37. data/locale/en_GB/foreman_remote_execution.po +12 -22
  38. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  39. data/locale/es/foreman_remote_execution.po +13 -23
  40. data/locale/foreman_remote_execution.pot +71 -80
  41. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  42. data/locale/fr/foreman_remote_execution.po +13 -23
  43. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  44. data/locale/ja/foreman_remote_execution.po +13 -23
  45. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  46. data/locale/ko/foreman_remote_execution.po +13 -23
  47. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  48. data/locale/pt_BR/foreman_remote_execution.po +13 -23
  49. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  50. data/locale/ru/foreman_remote_execution.po +13 -23
  51. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  52. data/locale/zh_CN/foreman_remote_execution.po +13 -23
  53. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  54. data/locale/zh_TW/foreman_remote_execution.po +13 -23
  55. data/test/benchmark/run_hosts_job_benchmark.rb +70 -0
  56. data/test/benchmark/targeting_benchmark.rb +31 -0
  57. data/test/factories/foreman_remote_execution_factories.rb +3 -0
  58. data/test/functional/api/v2/job_templates_controller_test.rb +1 -1
  59. data/test/unit/actions/run_hosts_job_test.rb +1 -0
  60. data/test/unit/concerns/host_extensions_test.rb +1 -4
  61. data/test/unit/job_invocation_test.rb +28 -1
  62. data/test/unit/remote_execution_feature_test.rb +40 -0
  63. data/test/unit/remote_execution_provider_test.rb +55 -1
  64. metadata +10 -7
  65. data/test/unit/actions/run_host_job_test.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a436dbf82dcf2ef9b040cdc5dfecf517dd25cc9e
4
- data.tar.gz: 8f8d044eba9abcbb997d4299ba828700d29e71f4
3
+ metadata.gz: e008f8d140c9b90f475730f271f2eef1722d9ea3
4
+ data.tar.gz: 0a60b9e6cf46ec5d233aec53f39c2c8b60ab77b7
5
5
  SHA512:
6
- metadata.gz: f27bc513effd681f1d186a264e1a1d289c3c164aabedf7fad48c90d3891eac9ce1a7f319e13196d532fe061f7b679e0d3f0da475688f72cf82d53f1c270826a1
7
- data.tar.gz: b3f489e09fbad3cc4f69c8197f939bc7e3ed6a013f93b46989cf4f9f863509d73a87213715bd7f038d82a13a3a14a755da8321f096f890acaf8a99085a38bf52
6
+ metadata.gz: ec1c4582b54752cffd9fe0c1f944dee4449787be282ca619511a934ee36d2a30b3d0af3bbb0ed050cfbbf19533bd1afd78b96776f6efa95e99fdef4bdbc6eef5
7
+ data.tar.gz: cea15e552318c30f00417d3af6d57ebbd38d2fc2fb5dd54c7c8bee435b08933d203cc476b450f236537e2dc2464dcafeafd3abe9e4a8d5737cc556a656af5499
data/README.md CHANGED
@@ -24,6 +24,17 @@ management functionality with remote management functionality.
24
24
 
25
25
  Check the Foreman manual [remote execution section](http://theforeman.org/plugins/foreman_remote_execution/)
26
26
 
27
+ ## Simulated runs
28
+ There is an option to use an alternative `ScriptRunner` implementation. Instead of doing ssh connections it discards any input its given and gives back fake output.
29
+
30
+ It is controlled by setting the following environment variables
31
+ `REX_SIMULATE` - setting to 1, yes or true enables the use of the fake runner
32
+ `REX_SIMULATE_PATH` - full path to a file which should be given back as output by the runner, currently one line per runner's refresh
33
+ `REX_SIMULATE_FAIL_CHANCE` - number from 0 to 100, each host has a `REX_SIMULATE_FAIL_CHANCE` of exiting with `REX_SIMULATE_EXIT`, useful for simulating random failures
34
+ `REX_SIMULATE_EXIT` - see the previous
35
+
36
+ Because it doesn't really open the outgoing connections, it doesn't need hosts with valid IP addresses as targets.
37
+
27
38
  ## Links
28
39
 
29
40
  * [Design document](http://theforeman.github.io/foreman_remote_execution/design/)
@@ -30,22 +30,17 @@ function refresh_search_query(value){
30
30
  }
31
31
 
32
32
  function show_preview_hosts_modal() {
33
- var modal_window = $('#previewHostsModal');
34
-
35
33
  var form = $('form#job_invocation_form');
36
34
  var data = form.serializeArray();
37
-
38
35
  request = $.ajax({
39
36
  data: data,
40
37
  type: 'GET',
41
- url: modal_window.attr('data-url'),
42
- success: function(request) {
43
- modal_window.find('.modal-body').html(request);
44
- },
45
- complete: function() {
46
- modal_window.modal({'show': true});
47
- modal_window.find('a[rel="popover-modal"]').popover();
48
- }
38
+ url: $('#previewHostsModal').attr('data-url')
39
+ }).then(function(result){
40
+ var modal_window = $('#previewHostsModal');
41
+ modal_window.find('.modal-body').html(result);
42
+ modal_window.modal({'show': true});
43
+ modal_window.find('a[rel="popover-modal"]').popover();
49
44
  });
50
45
  }
51
46
 
@@ -37,6 +37,7 @@ module Api
37
37
  api :GET, '/job_templates/:id/export', N_('Export a job template to ERB')
38
38
  param :id, :identifier, :required => true
39
39
  def export
40
+ Foreman::Deprecation.api_deprecation_warning('Exporting template is provided by Foreman core, please use that endpoint instead')
40
41
  send_data @job_template.to_erb, :type => 'text/plain', :disposition => 'attachment', :filename => @job_template.filename
41
42
  end
42
43
 
@@ -4,7 +4,7 @@ module Foreman::Controller::Parameters::RemoteExecutionFeature
4
4
  class_methods do
5
5
  def remote_execution_feature_params_filter
6
6
  ::Foreman::ParameterFilter.new(::RemoteExecutionFeature).tap do |filter|
7
- filter.permit :label, :name, :provided_input_names, :description, :job_template_id
7
+ filter.permit :label, :name, :provided_input_names, :description, :job_template_id, :host_action_button
8
8
  end
9
9
  end
10
10
  end
@@ -39,7 +39,7 @@ class JobInvocationsController < ApplicationController
39
39
  if @composer.trigger
40
40
  redirect_to job_invocation_path(@composer.job_invocation)
41
41
  else
42
- @composer.job_invocation.description_format = nil if params[:job_invocation].key?(:description_override)
42
+ @composer.job_invocation.description_format = nil if params.fetch(:job_invocation, {}).key?(:description_override)
43
43
  render :action => 'new'
44
44
  end
45
45
  end
@@ -86,6 +86,10 @@ class JobInvocationsController < ApplicationController
86
86
  end
87
87
 
88
88
  def prepare_composer
89
- JobInvocationComposer.from_ui_params(params.merge(:triggering => triggering_params))
89
+ if params[:feature].present?
90
+ JobInvocationComposer.for_feature(params[:feature], params[:host_ids], {})
91
+ else
92
+ JobInvocationComposer.from_ui_params(params.merge(:triggering => triggering_params))
93
+ end
90
94
  end
91
95
  end
@@ -41,11 +41,6 @@ class JobTemplatesController < ::TemplatesController
41
41
  end
42
42
  end
43
43
 
44
- def export
45
- find_resource unless @template.present?
46
- send_data @template.to_erb, :type => 'text/plain', :disposition => 'attachment', :filename => @template.filename
47
- end
48
-
49
44
  private
50
45
 
51
46
  def find_resource
@@ -8,11 +8,27 @@ module ForemanRemoteExecution
8
8
  end
9
9
 
10
10
  def multiple_actions_with_remote_execution
11
- multiple_actions_without_remote_execution + [[_('Run Job'), new_job_invocation_path, false]]
11
+ multiple_actions_without_remote_execution + [ [_('Schedule Remote Job'), new_job_invocation_path, false] ]
12
+ end
13
+
14
+ def schedule_job_multi_button(*args)
15
+ host_features = RemoteExecutionFeature.with_host_action_button.order(:label).map do |feature|
16
+ link_to(_('%s') % feature.name, job_invocations_path(:host_ids => [args.first.id], :feature => feature.label), :method => :post)
17
+ end
18
+
19
+ if host_features.present?
20
+ action_buttons(schedule_job_button(*args), *host_features)
21
+ else
22
+ schedule_job_button(*args)
23
+ end
24
+ end
25
+
26
+ def schedule_job_button(*args)
27
+ link_to(_('Schedule Remote Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')
12
28
  end
13
29
 
14
30
  def host_title_actions_with_run_button(*args)
15
- title_actions(button_group(link_to(_('Run Job'), new_job_invocation_path(:host_ids => [args.first.id]), :id => :run_button, :class => 'btn btn-default')))
31
+ title_actions(button_group(schedule_job_multi_button(*args)))
16
32
  host_title_actions_without_run_button(*args)
17
33
  end
18
34
  end
@@ -10,8 +10,7 @@ module ForemanRemoteExecution
10
10
  original = permitted_actions_without_run_button(template)
11
11
 
12
12
  if template.is_a?(JobTemplate)
13
- original.unshift(display_link_if_authorized(_('Export'), hash_for_export_job_template_path(:id => template.id), 'data-no-turbolink' => true))
14
- original.unshift(display_link_if_authorized(_('Run'), hash_for_new_job_invocation_path(:template_id => template.id))) unless template.snippet
13
+ original.unshift(display_link_if_authorized(_('Run'), hash_for_new_job_invocation_path(:template_id => template.id).merge(:authorizer => authorizer))) unless template.snippet
15
14
  end
16
15
 
17
16
  original
@@ -11,31 +11,31 @@ module RemoteExecutionHelper
11
11
  def job_invocation_chart(invocation)
12
12
  options = { :class => 'statistics-pie small', :expandable => true, :border => 0, :show_title => true }
13
13
 
14
- if (bulk_task = invocation.task)
15
- failed_tasks = bulk_task.sub_tasks.select { |sub_task| task_failed? sub_task }
16
- cancelled_tasks, failed_tasks = failed_tasks.partition { |task| task_cancelled? task }
17
- success = bulk_task.output['success_count'] || 0
18
- cancelled = cancelled_tasks.length
19
- failed = failed_tasks.length
20
- pending = (bulk_task.output['pending_count'] || bulk_task.sub_tasks.count)
21
-
22
- flot_pie_chart('status', job_invocation_status(invocation),
23
- [{:label => _('Success'), :data => success, :color => '#5CB85C'},
24
- {:label => _('Failed'), :data => failed, :color => '#D9534F'},
25
- {:label => _('Pending'), :data => pending, :color => '#DEDEDE'},
26
- {:label => _('Cancelled'), :data => cancelled, :color => '#B7312D'}],
14
+ if (invocation.task)
15
+ # Request precise subtask counts only if the task is stopped
16
+ report = invocation.progress_report
17
+ flot_pie_chart('status', job_invocation_status(invocation, report[:progress]),
18
+ [{:label => _('Success'), :data => report[:success], :color => '#5CB85C'},
19
+ {:label => _('Failed'), :data => report[:failed], :color => '#D9534F'},
20
+ {:label => _('Pending'), :data => report[:pending], :color => '#DEDEDE'},
21
+ {:label => _('Cancelled'), :data => report[:cancelled], :color => '#B7312D'}],
27
22
  options)
28
23
  else
29
24
  content_tag(:h4, job_invocation_status(invocation))
30
25
  end
31
26
  end
32
27
 
33
- def job_invocation_status(invocation)
28
+ def job_hosts_authorizer
29
+ @job_hosts_authorizer ||= Authorizer.new(User.current, :collection => @hosts)
30
+ end
31
+
32
+ def job_invocation_status(invocation, percent = nil)
34
33
  case invocation.status
35
34
  when HostStatus::ExecutionStatus::QUEUED
36
35
  _('queued')
37
36
  when HostStatus::ExecutionStatus::RUNNING
38
- _('running %{percent}%%') % {:percent => invocation.progress}
37
+ percent ||= invocation.progress_report[:progress]
38
+ _('running %{percent}%%') % {:percent => percent}
39
39
  when HostStatus::ExecutionStatus::OK
40
40
  _('succeeded')
41
41
  when HostStatus::ExecutionStatus::ERROR
@@ -45,14 +45,6 @@ module RemoteExecutionHelper
45
45
  end
46
46
  end
47
47
 
48
- def task_failed?(task)
49
- %w(warning error).include? task.result
50
- end
51
-
52
- def task_cancelled?(task)
53
- task.execution_plan.errors.map(&:exception).any? { |exception| exception.class == ::ForemanTasks::Task::TaskCancelledException }
54
- end
55
-
56
48
  def host_counter(label, count)
57
49
  content_tag(:div, :class => 'host_counter') do
58
50
  content_tag(:div, label, :class => 'header') + content_tag(:div, count.to_s, :class => 'count')
@@ -69,11 +61,9 @@ module RemoteExecutionHelper
69
61
  else
70
62
  case task.result
71
63
  when 'warning', 'error'
72
- if task_cancelled?(task)
73
- icon_text('warning-triangle-o', _('cancelled'), :kind => 'pficon')
74
- else
75
- icon_text('error-circle-o', _('failed'), :kind => 'pficon')
76
- end
64
+ icon_text('error-circle-o', _('failed'), :kind => 'pficon')
65
+ when 'cancelled'
66
+ icon_text('warning-triangle-o', _('cancelled'), :kind => 'pficon')
77
67
  when 'success'
78
68
  icon_text('ok', _('success'), :kind => 'pficon')
79
69
  else
@@ -84,8 +74,8 @@ module RemoteExecutionHelper
84
74
 
85
75
  def template_invocation_actions(task, host, job_invocation, template_invocation)
86
76
  [
87
- display_link_if_authorized(_('Host detail'), hash_for_host_path(host).merge(:auth_object => host, :permission => :view_hosts)),
88
- display_link_if_authorized(_('Rerun on %s') % host.name, hash_for_rerun_job_invocation_path(:id => job_invocation, :host_ids => [ host.id ])),
77
+ display_link_if_authorized(_('Host detail'), hash_for_host_path(host).merge(:auth_object => host, :permission => :view_hosts, :authorizer => job_hosts_authorizer)),
78
+ display_link_if_authorized(_('Rerun on %s') % host.name, hash_for_rerun_job_invocation_path(:id => job_invocation, :host_ids => [ host.id ], :authorizer => job_hosts_authorizer)),
89
79
  ]
90
80
  end
91
81
 
@@ -103,6 +93,7 @@ module RemoteExecutionHelper
103
93
  # rubocop:disable Metrics/AbcSize
104
94
  def job_invocation_task_buttons(task)
105
95
  job_invocation = task.task_groups.find { |group| group.class == JobInvocationTaskGroup }.job_invocation
96
+ task_authorizer = Authorizer.new(User.current, :collection => [task])
106
97
  buttons = []
107
98
  buttons << link_to(_('Refresh'), {}, :class => 'btn btn-default', :title => _('Refresh this page'))
108
99
  if authorized_for(hash_for_new_job_invocation_path)
@@ -113,15 +104,15 @@ module RemoteExecutionHelper
113
104
  if authorized_for(hash_for_new_job_invocation_path)
114
105
  buttons << link_to(_('Rerun failed'), rerun_job_invocation_path(:id => job_invocation.id, :failed_only => 1),
115
106
  :class => 'btn btn-default',
116
- :disabled => !task.sub_tasks.any? { |sub_task| task_failed?(sub_task) },
107
+ :disabled => task.sub_tasks.where(:result => %w(error warning)).count.zero?,
117
108
  :title => _('Rerun on failed hosts'))
118
109
  end
119
- if authorized_for(:permission => :view_foreman_tasks, :auth_object => task)
110
+ if authorized_for(:permission => :view_foreman_tasks, :auth_object => task, :authorizer => task_authorizer)
120
111
  buttons << link_to(_('Job Task'), foreman_tasks_task_path(task),
121
112
  :class => 'btn btn-default',
122
113
  :title => _('See the last task details'))
123
114
  end
124
- if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task)
115
+ if authorized_for(:permission => :edit_foreman_tasks, :auth_object => task, :authorizer => task_authorizer)
125
116
  buttons << link_to(_('Cancel Job'), cancel_foreman_tasks_task_path(task),
126
117
  :class => 'btn btn-danger',
127
118
  :title => _('Try to cancel the job'),
@@ -153,8 +144,9 @@ module RemoteExecutionHelper
153
144
  if invocation.queued?
154
145
  job_invocation_status(invocation)
155
146
  else
147
+ task_authorizer = Authorizer.new(User.current, :collection => [invocation.task])
156
148
  link_to_if_authorized job_invocation_status(invocation),
157
- hash_for_foreman_tasks_task_path(invocation.task).merge(:auth_object => invocation.task, :permission => :view_foreman_tasks)
149
+ hash_for_foreman_tasks_task_path(invocation.task).merge(:auth_object => invocation.task, :permission => :view_foreman_tasks, :authorizer => task_authorizer)
158
150
  end
159
151
  end
160
152
 
@@ -184,8 +176,14 @@ module RemoteExecutionHelper
184
176
  if (preview = renderer.preview)
185
177
  content_tag :pre, preview
186
178
  else
187
- alert :class => 'alert-block alert-danger base in fade has-error',
188
- :text => renderer.error_message.html_safe
179
+ if target.nil?
180
+ alert :text => _('Could not render the preview because no host matches the search query.'),
181
+ :class => 'alert alert-block alert-warning base',
182
+ :close => false
183
+ else
184
+ alert :class => 'alert-block alert-danger base in fade has-error',
185
+ :text => renderer.error_message.html_safe
186
+ end
189
187
  end
190
188
  end
191
189
 
@@ -240,11 +238,14 @@ module RemoteExecutionHelper
240
238
  :onchange => 'regenerate_description(this);',
241
239
  :class => 'description_format advanced',
242
240
  :disabled => disabled,
243
- :help_inline => popover(_('Explanation'),
244
- _('This template is used to generate the description ' +
245
- 'Input values can be used using the syntax %{package}. ' +
246
- 'You may also include the job category and template ' +
247
- 'name using %{job_category} and %{template_name}.'))
241
+ :label_help => description_format_help
242
+ end
243
+
244
+ def description_format_help
245
+ _('This template is used to generate the description.<br/>' +
246
+ 'Input values can be used using the syntax %{package}.<br/>' +
247
+ 'You may also include the job category and template<br/>' +
248
+ 'name using %{job_category} and %{template_name}.').html_safe
248
249
  end
249
250
 
250
251
  def advanced_switch_f(default_text, switch_text)
@@ -21,7 +21,6 @@ module Actions
21
21
  link!(template_invocation)
22
22
 
23
23
  verify_permissions(host, template_invocation)
24
- hostname = find_ip_or_hostname(host)
25
24
 
26
25
  raise _('Could not use any template used in the job invocation') if template_invocation.blank?
27
26
 
@@ -47,6 +46,7 @@ module Actions
47
46
  raise _('Failed rendering template: %s') % renderer.error_message unless script
48
47
 
49
48
  provider = template_invocation.template.provider
49
+ hostname = provider.find_ip_or_hostname(host)
50
50
  action_options = provider.proxy_command_options(template_invocation, host).merge(:hostname => hostname, :script => script)
51
51
  plan_delegated_action(proxy, ForemanRemoteExecutionCore::Actions::RunScript, action_options)
52
52
  plan_self
@@ -78,7 +78,7 @@ module Actions
78
78
 
79
79
  def finalize
80
80
  if exit_status.to_s != '0'
81
- error! _('Playbook execution failed')
81
+ error! _('Job execution failed')
82
82
  end
83
83
  end
84
84
 
@@ -113,19 +113,6 @@ module Actions
113
113
  delegated_output[:exit_status]
114
114
  end
115
115
 
116
- def find_ip_or_hostname(host)
117
- %w(execution primary provision).each do |flag|
118
- interface = host.send(flag + '_interface')
119
- return interface.ip if interface && interface.ip.present?
120
- end
121
-
122
- host.interfaces.each do |interface|
123
- return interface.ip unless interface.ip.blank?
124
- end
125
-
126
- return host.fqdn
127
- end
128
-
129
116
  private
130
117
 
131
118
  def delegated_output
@@ -2,6 +2,8 @@ module Actions
2
2
  module RemoteExecution
3
3
  class RunHostsJob < Actions::ActionWithSubPlans
4
4
 
5
+ include Dynflow::Action::WithBulkSubPlans
6
+
5
7
  middleware.use Actions::Middleware::BindJobInvocation
6
8
  middleware.use Actions::Middleware::RecurringLogic
7
9
 
@@ -25,15 +27,27 @@ module Actions
25
27
  job_invocation = JobInvocation.find(input[:job_invocation_id])
26
28
  proxy_selector = RemoteExecutionProxySelector.new
27
29
 
28
- # composer creates just "pattern" for template_invocations because target is evaluated
29
- # during actual run (here) so we build template invocations from these patterns
30
- job_invocation.targeting.hosts.map do |host|
30
+ current_batch.map do |host|
31
+ # composer creates just "pattern" for template_invocations because target is evaluated
32
+ # during actual run (here) so we build template invocations from these patterns
31
33
  template_invocation = job_invocation.pattern_template_invocation_for_host(host).deep_clone
32
34
  template_invocation.host_id = host.id
33
35
  trigger(RunHostJob, job_invocation, host, template_invocation, proxy_selector)
34
36
  end
35
37
  end
36
38
 
39
+ def batch(from, size)
40
+ hosts.offset(from).limit(size)
41
+ end
42
+
43
+ def total_count
44
+ hosts.count
45
+ end
46
+
47
+ def hosts
48
+ JobInvocation.find(input[:job_invocation_id]).targeting.hosts.order(:name, :id)
49
+ end
50
+
37
51
  def set_up_concurrency_control(invocation)
38
52
  limit_concurrency_level invocation.concurrency_level unless invocation.concurrency_level.nil?
39
53
  distribute_over_time invocation.time_span unless invocation.time_span.nil?
@@ -6,7 +6,7 @@ module ForemanRemoteExecution
6
6
  alias_method_chain :build_required_interfaces, :remote_execution
7
7
  alias_method_chain :reload, :remote_execution
8
8
  alias_method_chain :becomes, :remote_execution
9
- alias_method_chain :params, :remote_execution
9
+ alias_method_chain :host_params, :remote_execution
10
10
 
11
11
  has_many :targeting_hosts, :dependent => :destroy, :foreign_key => 'host_id'
12
12
  has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
@@ -24,11 +24,12 @@ module ForemanRemoteExecution
24
24
  @execution_status_label ||= get_status(HostStatus::ExecutionStatus).to_label(options)
25
25
  end
26
26
 
27
- def params_with_remote_execution
28
- params = params_without_remote_execution
27
+ def host_params_with_remote_execution
28
+ params = host_params_without_remote_execution
29
29
  keys = remote_execution_ssh_keys
30
30
  params['remote_execution_ssh_keys'] = keys unless keys.blank?
31
- [:remote_execution_ssh_user, :remote_execution_effective_user_method].each do |key|
31
+ [:remote_execution_ssh_user, :remote_execution_effective_user_method,
32
+ :remote_execution_connect_by_ip].each do |key|
32
33
  params[key.to_s] = Setting[key] unless params.key?(key.to_s)
33
34
  end
34
35
  params
@@ -44,10 +45,10 @@ module ForemanRemoteExecution
44
45
  proxies[:fallback] = smart_proxies.with_features(provider) if Setting[:remote_execution_fallback_proxy]
45
46
 
46
47
  if Setting[:remote_execution_global_proxy]
47
- proxy_scope = if Taxonomy.enabled_taxonomies.any?
48
+ proxy_scope = if Taxonomy.enabled_taxonomies.any? && User.current.present?
48
49
  ::SmartProxy.with_taxonomy_scope_override(location, organization)
49
50
  else
50
- proxy_scope = ::SmartProxy
51
+ proxy_scope = ::SmartProxy.unscoped
51
52
  end
52
53
 
53
54
  proxy_scope = proxy_scope.authorized if authorized
@@ -58,7 +58,7 @@ class InputTemplateRenderer
58
58
  def render_template(template_name, input_values = {}, options = {})
59
59
  options.assert_valid_keys(:with_foreign_input_set)
60
60
  with_foreign_input_set = options.fetch(:with_foreign_input_set, true)
61
- template = JobTemplate.authorized(:view_job_templates).find_by(:name => template_name)
61
+ template = @template.class.authorized("view_#{@template.class.to_s.underscore}").find_by(:name => template_name)
62
62
  unless template
63
63
  self.error_message = _('included template \'%s\' not found') % template_name
64
64
  raise error_message
@@ -48,7 +48,7 @@ class JobInvocation < ActiveRecord::Base
48
48
 
49
49
  scope :with_task, -> { references(:task) }
50
50
 
51
- scoped_search :in => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id', :auto_complete => true
51
+ scoped_search :in => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
52
52
 
53
53
  scoped_search :in => :recurring_logic, :on => 'id', :rename => 'recurring',
54
54
  :ext_method => :search_by_recurring_logic, :only_explicit => true,
@@ -86,8 +86,14 @@ class JobInvocation < ActiveRecord::Base
86
86
  end
87
87
 
88
88
  # returns progress in percents
89
- def progress
90
- queued? ? 0 : (task.progress * 100).to_i
89
+ def progress(total = nil, done = nil)
90
+ if queued? || !targeting.resolved? || done == 0
91
+ 0
92
+ else
93
+ total ||= targeting.hosts.count
94
+ done ||= sub_tasks.where(:result => %w(success warning error)).count
95
+ ((done.to_f / total) * 100).round
96
+ end
91
97
  end
92
98
 
93
99
  def queued?
@@ -160,9 +166,8 @@ class JobInvocation < ActiveRecord::Base
160
166
  def generate_description
161
167
  key_re = /%\{([^\}]+)\}/
162
168
  template_invocation = pattern_template_invocations.first
163
- input_names = template_invocation.template.template_inputs_with_foreign(&:name)
164
169
  hash_base = Hash.new { |hash, key| hash[key] = "%{#{key}}" }
165
- input_hash = hash_base.merge Hash[input_names.zip(template_invocation.input_values.map(&:value))]
170
+ input_hash = template_invocation.input_values.reduce(hash_base) { |h, v| h.update(v.template_input.name => v.value) }
166
171
  input_hash.update(:job_category => job_category)
167
172
  input_hash.update(:template_name => template_invocation.template.name)
168
173
  description_format.scan(key_re) { |key| input_hash[key.first] }
@@ -173,6 +178,19 @@ class JobInvocation < ActiveRecord::Base
173
178
  self.description = self.description[0..(JobInvocation.columns_hash['description'].limit - 1)]
174
179
  end
175
180
 
181
+ def progress_report
182
+ if queued? || !targeting.resolved?
183
+ %w(success total cancelled failed error warning pending progress).map(&:to_sym).reduce({}) do |acc, key|
184
+ acc.merge(key => 0)
185
+ end
186
+ else
187
+ counts = task.sub_tasks_counts
188
+ done = counts.values_at(:cancelled, :error, :success, :warning).reduce(:+)
189
+ percent = progress(counts[:total], done)
190
+ counts.merge(:progress => percent, :failed => counts[:error] + counts[:warning])
191
+ end
192
+ end
193
+
176
194
  private
177
195
 
178
196
  def failed_template_invocations