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.
- 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
|