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.
- checksums.yaml +4 -4
- data/README.md +11 -0
- data/app/assets/javascripts/template_invocation.js +6 -11
- data/app/controllers/api/v2/job_templates_controller.rb +1 -0
- data/app/controllers/concerns/foreman/controller/parameters/remote_execution_feature.rb +1 -1
- data/app/controllers/job_invocations_controller.rb +6 -2
- data/app/controllers/job_templates_controller.rb +0 -5
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +18 -2
- data/app/helpers/concerns/foreman_remote_execution/job_templates_extensions.rb +1 -2
- data/app/helpers/remote_execution_helper.rb +42 -41
- data/app/lib/actions/remote_execution/run_host_job.rb +2 -15
- data/app/lib/actions/remote_execution/run_hosts_job.rb +17 -3
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -6
- data/app/models/input_template_renderer.rb +1 -1
- data/app/models/job_invocation.rb +23 -5
- data/app/models/remote_execution_feature.rb +16 -3
- data/app/models/setting/remote_execution.rb +4 -1
- data/app/models/ssh_execution_provider.rb +33 -2
- data/app/models/targeting.rb +7 -3
- data/app/views/api/v2/remote_execution_features/base.json.rabl +1 -1
- data/app/views/job_invocations/_form.html.erb +1 -4
- data/app/views/job_invocations/_host_name_td.html.erb +2 -1
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/job_invocations/show.html.erb +1 -1
- data/app/views/job_templates/_custom_tabs.html.erb +1 -4
- data/app/views/overrides/nics/_execution_interface.html.erb +2 -3
- data/app/views/remote_execution_features/index.html.erb +1 -1
- data/db/migrate/20170110145641_add_host_action_button_to_remote_execution_feature.rb +5 -0
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +3 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +2 -5
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +13 -23
- data/locale/en/foreman_remote_execution.po +12 -22
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +12 -22
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +13 -23
- data/locale/foreman_remote_execution.pot +71 -80
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +13 -23
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +13 -23
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +13 -23
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +13 -23
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +13 -23
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +13 -23
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +13 -23
- data/test/benchmark/run_hosts_job_benchmark.rb +70 -0
- data/test/benchmark/targeting_benchmark.rb +31 -0
- data/test/factories/foreman_remote_execution_factories.rb +3 -0
- data/test/functional/api/v2/job_templates_controller_test.rb +1 -1
- data/test/unit/actions/run_hosts_job_test.rb +1 -0
- data/test/unit/concerns/host_extensions_test.rb +1 -4
- data/test/unit/job_invocation_test.rb +28 -1
- data/test/unit/remote_execution_feature_test.rb +40 -0
- data/test/unit/remote_execution_provider_test.rb +55 -1
- metadata +10 -7
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e008f8d140c9b90f475730f271f2eef1722d9ea3
|
4
|
+
data.tar.gz: 0a60b9e6cf46ec5d233aec53f39c2c8b60ab77b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
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
|
-
|
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 + [[_('
|
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(
|
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(_('
|
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 (
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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 =>
|
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
|
-
|
188
|
-
|
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
|
-
:
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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! _('
|
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
|
-
|
29
|
-
|
30
|
-
|
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 :
|
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
|
28
|
-
params =
|
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
|
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 =
|
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'
|
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? ?
|
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
|
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
|