foreman_remote_execution 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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