foreman_remote_execution 4.3.1 → 4.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +27 -22
- data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/job_templates_controller.rb +4 -4
- data/app/controllers/ui_job_wizard_controller.rb +19 -0
- data/app/helpers/job_invocations_helper.rb +2 -2
- data/app/helpers/remote_execution_helper.rb +40 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
- data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
- data/app/models/host_proxy_invocation.rb +4 -0
- data/app/models/host_status/execution_status.rb +5 -5
- data/app/models/invocation_provider_input_value.rb +12 -0
- data/app/models/job_invocation.rb +35 -12
- data/app/models/job_invocation_composer.rb +74 -19
- data/app/models/remote_execution_provider.rb +18 -3
- data/app/models/setting/remote_execution.rb +11 -1
- data/app/models/ssh_execution_provider.rb +4 -4
- data/app/models/targeting.rb +5 -1
- data/app/models/template_invocation.rb +2 -0
- data/app/overrides/execution_interface.rb +8 -8
- data/app/overrides/subnet_proxies.rb +6 -6
- data/app/services/renderer_methods.rb +12 -0
- data/app/views/job_invocations/_form.html.erb +8 -0
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +1 -0
- data/app/views/templates/ssh/puppet_run_once.erb +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
- data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
- data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/extra/cockpit/foreman-cockpit-session +6 -6
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +14 -12
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +1 -0
- data/locale/de/foreman_remote_execution.po +77 -27
- data/locale/en/foreman_remote_execution.po +77 -27
- data/locale/en_GB/foreman_remote_execution.po +77 -27
- data/locale/es/foreman_remote_execution.po +77 -27
- data/locale/foreman_remote_execution.pot +241 -163
- data/locale/fr/foreman_remote_execution.po +77 -27
- data/locale/ja/foreman_remote_execution.po +77 -27
- data/locale/ko/foreman_remote_execution.po +77 -27
- data/locale/pt_BR/foreman_remote_execution.po +77 -27
- data/locale/ru/foreman_remote_execution.po +77 -27
- data/locale/zh_CN/foreman_remote_execution.po +77 -27
- data/locale/zh_TW/foreman_remote_execution.po +77 -27
- data/package.json +4 -2
- data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
- data/test/helpers/remote_execution_helper_test.rb +16 -0
- data/test/unit/job_invocation_composer_test.rb +100 -3
- data/test/unit/job_invocation_report_template_test.rb +57 -0
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +95 -11
- data/webpack/JobWizard/JobWizard.scss +53 -0
- data/webpack/JobWizard/JobWizardConstants.js +16 -0
- data/webpack/JobWizard/JobWizardSelectors.js +47 -0
- data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
- data/webpack/JobWizard/__tests__/fixtures.js +128 -0
- data/webpack/JobWizard/__tests__/integration.test.js +84 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +123 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
- data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
- data/webpack/JobWizard/steps/Schedule/index.js +41 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +20 -0
- data/webpack/JobWizard/steps/form/Formatter.js +149 -0
- data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +60 -0
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
- data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
- data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
- data/webpack/global_index.js +6 -0
- data/webpack/index.js +3 -4
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +83 -0
- data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
- data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
- data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
- data/webpack/react_app/extend/fillRecentJobsCard.js +11 -0
- data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
- data/webpack/react_app/extend/reducers.js +5 -0
- metadata +52 -8
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
- data/app/views/api/v2/registration/_form.html.erb +0 -12
- data/test/models/orchestration/ssh_test.rb +0 -56
@@ -64,15 +64,15 @@ class HostStatus::ExecutionStatus < HostStatus::Status
|
|
64
64
|
|
65
65
|
case status
|
66
66
|
when OK
|
67
|
-
[ "state = 'stopped' AND result = 'success'" ]
|
67
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'success'" ]
|
68
68
|
when CANCELLED
|
69
|
-
[ "state = 'stopped' AND result = 'cancelled'" ]
|
69
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND foreman_tasks_tasks.result = 'cancelled'" ]
|
70
70
|
when ERROR
|
71
|
-
[ "state = 'stopped' AND (result = 'error' OR result = 'warning')" ]
|
71
|
+
[ "foreman_tasks_tasks.state = 'stopped' AND (foreman_tasks_tasks.result = 'error' OR foreman_tasks_tasks.result = 'warning')" ]
|
72
72
|
when QUEUED
|
73
|
-
[ "state = 'scheduled' OR state IS NULL" ]
|
73
|
+
[ "foreman_tasks_tasks.state = 'scheduled' OR foreman_tasks_tasks.state IS NULL" ]
|
74
74
|
when RUNNING
|
75
|
-
[ "state <> 'stopped'" ]
|
75
|
+
[ "foreman_tasks_tasks.state <> 'stopped'" ]
|
76
76
|
else
|
77
77
|
[ '1 = 0' ]
|
78
78
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class InvocationProviderInputValue < ApplicationRecord
|
2
|
+
belongs_to :template_invocation
|
3
|
+
|
4
|
+
validates :name, :presence => true
|
5
|
+
|
6
|
+
validates :value, :inclusion => { :in => proc { |v| v.provider_input.options_array } },
|
7
|
+
:if => proc { |v| v.provider_input.options_array.present? }
|
8
|
+
|
9
|
+
def provider_input
|
10
|
+
template_invocation.template.provider.provider_inputs.find { |item| item.name == name }
|
11
|
+
end
|
12
|
+
end
|
@@ -15,8 +15,9 @@ class JobInvocation < ApplicationRecord
|
|
15
15
|
|
16
16
|
belongs_to :targeting, :dependent => :destroy
|
17
17
|
has_many :all_template_invocations, :inverse_of => :job_invocation, :dependent => :destroy, :class_name => 'TemplateInvocation'
|
18
|
-
has_many :template_invocations, -> { where('host_id IS NOT NULL') }, :inverse_of => :job_invocation
|
19
|
-
has_many :pattern_template_invocations, -> { where('host_id IS NULL') }, :inverse_of => :job_invocation, :class_name => 'TemplateInvocation'
|
18
|
+
has_many :template_invocations, -> { where('template_invocations.host_id IS NOT NULL') }, :inverse_of => :job_invocation
|
19
|
+
has_many :pattern_template_invocations, -> { where('template_invocations.host_id IS NULL') }, :inverse_of => :job_invocation, :class_name => 'TemplateInvocation'
|
20
|
+
has_many :pattern_templates, :through => :pattern_template_invocations, :source => :template
|
20
21
|
|
21
22
|
validates :targeting, :presence => true
|
22
23
|
validates :job_category, :presence => true
|
@@ -44,26 +45,31 @@ class JobInvocation < ApplicationRecord
|
|
44
45
|
:source => 'run_host_job_task'
|
45
46
|
has_one :user, through: :task
|
46
47
|
scoped_search relation: :user, on: :login, rename: 'user', complete_value: true,
|
47
|
-
|
48
|
-
|
48
|
+
value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
|
49
|
+
special_values: [:current_user], aliases: ['owner'], :only_explicit => true
|
49
50
|
scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
|
50
51
|
scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
|
51
52
|
scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
|
52
53
|
scoped_search :relation => :task, :on => :state, :rename => 'status', :ext_method => :search_by_status,
|
53
|
-
|
54
|
+
:only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
|
54
55
|
|
55
56
|
belongs_to :triggering, :class_name => 'ForemanTasks::Triggering'
|
56
57
|
has_one :recurring_logic, :through => :triggering, :class_name => 'ForemanTasks::RecurringLogic'
|
57
58
|
|
58
59
|
belongs_to :remote_execution_feature
|
59
60
|
|
60
|
-
|
61
|
+
has_many :targeted_hosts, :through => :targeting, :source => :hosts
|
62
|
+
scoped_search :on => 'targeted_host_id', :rename => 'targeted_host_id', :operators => ['= '],
|
63
|
+
:complete_value => false, :only_explicit => true, :ext_method => :search_by_targeted_host
|
64
|
+
|
65
|
+
scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
|
66
|
+
:complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
|
61
67
|
|
62
68
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
63
69
|
|
64
70
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
65
|
-
|
66
|
-
|
71
|
+
:ext_method => :search_by_recurring_logic, :only_explicit => true,
|
72
|
+
:complete_value => { :true => true, :false => false }
|
67
73
|
|
68
74
|
default_scope -> { order('job_invocations.id DESC') }
|
69
75
|
|
@@ -76,10 +82,22 @@ class JobInvocation < ApplicationRecord
|
|
76
82
|
|
77
83
|
encrypts :password, :key_passphrase, :effective_user_password
|
78
84
|
|
85
|
+
class Jail < Safemode::Jail
|
86
|
+
allow :sub_task_for_host, :template_invocations_hosts
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.search_by_targeted_host(key, operator, value)
|
90
|
+
{ :conditions => sanitize_sql_for_conditions(["hosts.id = ?", value]), :joins => :targeted_hosts }
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.search_by_pattern_template(key, operator, value)
|
94
|
+
{ :conditions => sanitize_sql_for_conditions(["templates.name = ?", value]), :joins => :pattern_templates }
|
95
|
+
end
|
96
|
+
|
79
97
|
def self.search_by_status(key, operator, value)
|
80
98
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
81
99
|
conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
|
82
|
-
{ :conditions => sanitize_sql_for_conditions(conditions), :
|
100
|
+
{ :conditions => sanitize_sql_for_conditions(conditions), :joins => :task }
|
83
101
|
end
|
84
102
|
|
85
103
|
def self.search_by_recurring_logic(key, operator, value)
|
@@ -173,11 +191,16 @@ class JobInvocation < ApplicationRecord
|
|
173
191
|
end
|
174
192
|
|
175
193
|
def total_hosts_count
|
194
|
+
count = _('N/A')
|
195
|
+
|
176
196
|
if targeting.resolved?
|
177
|
-
task&.main_action
|
178
|
-
|
179
|
-
|
197
|
+
count = if task&.main_action.respond_to?(:total_count)
|
198
|
+
task.main_action.total_count
|
199
|
+
else
|
200
|
+
targeting.hosts.count
|
201
|
+
end
|
180
202
|
end
|
203
|
+
count
|
181
204
|
end
|
182
205
|
|
183
206
|
def pattern_template_invocation_for_host(host)
|
@@ -1,4 +1,7 @@
|
|
1
1
|
class JobInvocationComposer
|
2
|
+
class JobTemplateNotFound < StandardError; end
|
3
|
+
|
4
|
+
class FeatureNotFound < StandardError; end
|
2
5
|
|
3
6
|
class UiParams
|
4
7
|
attr_reader :ui_params
|
@@ -64,6 +67,10 @@ class JobInvocationComposer
|
|
64
67
|
values.merge(:template_input_id => id)
|
65
68
|
end
|
66
69
|
|
70
|
+
provider_values_params = template_base.fetch(:provider_input_values, {})
|
71
|
+
template_base[:provider_input_values] = provider_values_params.map do |key, hash|
|
72
|
+
{ :name => key, :value => hash[:value] }
|
73
|
+
end
|
67
74
|
template_base
|
68
75
|
end
|
69
76
|
end
|
@@ -96,6 +103,17 @@ class JobInvocationComposer
|
|
96
103
|
|
97
104
|
def initialize(api_params)
|
98
105
|
@api_params = api_params
|
106
|
+
|
107
|
+
if api_params[:feature]
|
108
|
+
# set a default targeting type for backward compatibility
|
109
|
+
# when `for_feature` was used by the API it automatically set a default
|
110
|
+
api_params[:targeting_type] = Targeting::STATIC_TYPE
|
111
|
+
end
|
112
|
+
|
113
|
+
if api_params[:search_query].blank? && api_params[:host_ids].present?
|
114
|
+
translator = HostIdsTranslator.new(api_params[:host_ids])
|
115
|
+
api_params[:search_query] = translator.scoped_search
|
116
|
+
end
|
99
117
|
end
|
100
118
|
|
101
119
|
def params
|
@@ -103,12 +121,16 @@ class JobInvocationComposer
|
|
103
121
|
:targeting => targeting_params,
|
104
122
|
:triggering => triggering_params,
|
105
123
|
:description_format => api_params[:description_format],
|
106
|
-
:remote_execution_feature_id =>
|
124
|
+
:remote_execution_feature_id => remote_execution_feature_id,
|
107
125
|
:concurrency_control => concurrency_control_params,
|
108
126
|
:execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
|
109
127
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
110
128
|
end
|
111
129
|
|
130
|
+
def remote_execution_feature_id
|
131
|
+
feature&.id || api_params[:remote_execution_feature_id]
|
132
|
+
end
|
133
|
+
|
112
134
|
def targeting_params
|
113
135
|
raise ::Foreman::Exception, _('Cannot specify both bookmark_id and search_query') if api_params[:bookmark_id] && api_params[:search_query]
|
114
136
|
|
@@ -147,19 +169,39 @@ class JobInvocationComposer
|
|
147
169
|
|
148
170
|
def template_invocations_params
|
149
171
|
template_invocation_params = { :template_id => template.id, :effective_user => api_params[:effective_user] }
|
172
|
+
template_invocation_params[:provider_input_values] = filter_provider_inputs api_params
|
150
173
|
template_invocation_params[:input_values] = api_params.fetch(:inputs, {}).to_h.map do |name, value|
|
151
174
|
input = template.template_inputs_with_foreign.find { |i| i.name == name }
|
152
175
|
unless input
|
153
176
|
raise ::Foreman::Exception, _('Unknown input %{input_name} for template %{template_name}') %
|
154
|
-
|
177
|
+
{ :input_name => name, :template_name => template.name }
|
155
178
|
end
|
156
179
|
{ :template_input_id => input.id, :value => value }
|
157
180
|
end
|
158
181
|
[template_invocation_params]
|
159
182
|
end
|
160
183
|
|
184
|
+
def filter_provider_inputs(api_params)
|
185
|
+
return [] if template.provider.provider_input_namespace.empty?
|
186
|
+
inputs = api_params[template.provider.provider_input_namespace].to_h
|
187
|
+
provider_input_names = template.provider.provider_inputs.map(&:name)
|
188
|
+
inputs.select { |key, value| provider_input_names.include? key }.map { |key, value| { :name => key, :value => value } }
|
189
|
+
end
|
190
|
+
|
191
|
+
def feature
|
192
|
+
@feature ||= RemoteExecutionFeature.feature(api_params[:feature]) if api_params[:feature]
|
193
|
+
rescue => e
|
194
|
+
raise(FeatureNotFound, e.message)
|
195
|
+
end
|
196
|
+
|
197
|
+
def job_template_id
|
198
|
+
feature&.job_template_id || api_params[:job_template_id]
|
199
|
+
end
|
200
|
+
|
161
201
|
def template
|
162
|
-
@template ||= JobTemplate.authorized(:view_job_templates).find(
|
202
|
+
@template ||= JobTemplate.authorized(:view_job_templates).find(job_template_id)
|
203
|
+
rescue ActiveRecord::RecordNotFound
|
204
|
+
raise(JobTemplateNotFound, _("Template with id '%{id}' was not found") % { id: job_template_id })
|
163
205
|
end
|
164
206
|
|
165
207
|
private
|
@@ -227,25 +269,38 @@ class JobInvocationComposer
|
|
227
269
|
end
|
228
270
|
end
|
229
271
|
|
272
|
+
class HostIdsTranslator
|
273
|
+
attr_reader :bookmark, :hosts, :scoped_search, :host_ids
|
274
|
+
|
275
|
+
def initialize(input)
|
276
|
+
case input
|
277
|
+
when Bookmark
|
278
|
+
@bookmark = input
|
279
|
+
when Host::Base
|
280
|
+
@hosts = [input]
|
281
|
+
when Array
|
282
|
+
@hosts = input.map do |id|
|
283
|
+
Host::Managed.authorized.friendly.find(id)
|
284
|
+
end
|
285
|
+
when String
|
286
|
+
@scoped_search = input
|
287
|
+
else
|
288
|
+
@hosts = input
|
289
|
+
end
|
290
|
+
|
291
|
+
@scoped_search ||= Targeting.build_query_from_hosts(hosts.map(&:id)) if @hosts
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
230
295
|
class ParamsForFeature
|
231
296
|
attr_reader :feature_label, :feature, :provided_inputs
|
232
297
|
|
233
298
|
def initialize(feature_label, hosts, provided_inputs = {})
|
234
299
|
@feature = RemoteExecutionFeature.feature(feature_label)
|
235
300
|
@provided_inputs = provided_inputs
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
@host_objects = [hosts]
|
240
|
-
elsif hosts.is_a? Array
|
241
|
-
@host_objects = hosts.map do |id|
|
242
|
-
Host::Managed.authorized.friendly.find(id)
|
243
|
-
end
|
244
|
-
elsif hosts.is_a? String
|
245
|
-
@host_scoped_search = hosts
|
246
|
-
else
|
247
|
-
@host_objects = hosts
|
248
|
-
end
|
301
|
+
translator = HostIdsTranslator.new(hosts)
|
302
|
+
@host_bookmark = translator.bookmark
|
303
|
+
@host_scoped_search = translator.scoped_search
|
249
304
|
end
|
250
305
|
|
251
306
|
def params
|
@@ -263,7 +318,6 @@ class JobInvocationComposer
|
|
263
318
|
ret = {}
|
264
319
|
ret['targeting_type'] = Targeting::STATIC_TYPE
|
265
320
|
ret['search_query'] = @host_scoped_search if @host_scoped_search
|
266
|
-
ret['search_query'] = Targeting.build_query_from_hosts(@host_objects) if @host_objects
|
267
321
|
ret['bookmark_id'] = @host_bookmark.id if @host_bookmark
|
268
322
|
ret['user_id'] = User.current.id
|
269
323
|
ret
|
@@ -351,7 +405,7 @@ class JobInvocationComposer
|
|
351
405
|
job_invocation.effective_user_password = params[:effective_user_password]
|
352
406
|
|
353
407
|
if @reruns && job_invocation.targeting.static?
|
354
|
-
job_invocation.targeting.
|
408
|
+
job_invocation.targeting.assign_host_ids(JobInvocation.find(@reruns).targeting.host_ids)
|
355
409
|
job_invocation.targeting.mark_resolved!
|
356
410
|
end
|
357
411
|
|
@@ -503,6 +557,7 @@ class JobInvocationComposer
|
|
503
557
|
input = template_invocation.template.template_inputs_with_foreign.find { |i| i.id.to_s == attributes[:template_input_id].to_s }
|
504
558
|
input ? input.template_invocation_input_values.build(attributes) : nil
|
505
559
|
end.compact
|
560
|
+
template_invocation.provider_input_values.build job_template_base.fetch('provider_input_values', [])
|
506
561
|
end
|
507
562
|
|
508
563
|
def build_targeting
|
@@ -540,7 +595,7 @@ class JobInvocationComposer
|
|
540
595
|
|
541
596
|
params[:template_invocations].select { |t| valid_template_ids.include?(t[:template_id].to_i) }.map do |template_invocation_params|
|
542
597
|
template_invocation = job_invocation.pattern_template_invocations.build(:template_id => template_invocation_params[:template_id],
|
543
|
-
|
598
|
+
:effective_user => build_effective_user(template_invocation_params))
|
544
599
|
build_input_values_for(template_invocation, template_invocation_params)
|
545
600
|
template_invocation
|
546
601
|
end
|
@@ -20,7 +20,7 @@ class RemoteExecutionProvider
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def proxy_command_options(template_invocation, host)
|
23
|
-
{:proxy_operation_name => proxy_operation_name}
|
23
|
+
{:proxy_operation_name => proxy_operation_name}.merge(proxy_command_provider_inputs(template_invocation))
|
24
24
|
end
|
25
25
|
|
26
26
|
def secrets(_host)
|
@@ -35,6 +35,9 @@ class RemoteExecutionProvider
|
|
35
35
|
self.name
|
36
36
|
end
|
37
37
|
|
38
|
+
def provider_input_namespace
|
39
|
+
end
|
40
|
+
|
38
41
|
def supports_effective_user?
|
39
42
|
false
|
40
43
|
end
|
@@ -47,7 +50,7 @@ class RemoteExecutionProvider
|
|
47
50
|
method = host_setting(host, :remote_execution_effective_user_method)
|
48
51
|
unless EFFECTIVE_USER_METHODS.include?(method)
|
49
52
|
raise _('Effective user method "%{current_value}" is not one of %{valid_methods}') %
|
50
|
-
|
53
|
+
{ :current_value => method, :valid_methods => EFFECTIVE_USER_METHODS}
|
51
54
|
end
|
52
55
|
method
|
53
56
|
end
|
@@ -95,8 +98,20 @@ class RemoteExecutionProvider
|
|
95
98
|
def ssh_key_passphrase(_host)
|
96
99
|
end
|
97
100
|
|
101
|
+
def provider_inputs
|
102
|
+
[]
|
103
|
+
end
|
104
|
+
|
105
|
+
def provider_inputs_doc
|
106
|
+
{}
|
107
|
+
end
|
108
|
+
|
109
|
+
def proxy_command_provider_inputs(template_invocation)
|
110
|
+
{}
|
111
|
+
end
|
112
|
+
|
98
113
|
def proxy_action_class
|
99
|
-
ForemanRemoteExecutionCore::Actions::RunScript
|
114
|
+
'ForemanRemoteExecutionCore::Actions::RunScript'
|
100
115
|
end
|
101
116
|
|
102
117
|
# Return a specific proxy selector to use for running a given template
|
@@ -70,9 +70,19 @@ class Setting::RemoteExecution < Setting
|
|
70
70
|
self.set('remote_execution_form_job_template',
|
71
71
|
N_('Choose a job template that is pre-selected in job invocation form'),
|
72
72
|
'Run Command - SSH Default',
|
73
|
-
|
73
|
+
N_('Form Job Template'),
|
74
74
|
nil,
|
75
75
|
{ :collection => proc { Hash[JobTemplate.unscoped.map { |template| [template.name, template.name] }] } }),
|
76
|
+
self.set('remote_execution_job_invocation_report_template',
|
77
|
+
N_('Select a report template used for generating a report for a particular remote execution job'),
|
78
|
+
'Jobs - Invocation report template',
|
79
|
+
N_('Job Invocation Report Template'),
|
80
|
+
nil,
|
81
|
+
{ :collection => proc { self.job_invocation_report_templates_select } }),
|
76
82
|
]
|
77
83
|
end
|
84
|
+
|
85
|
+
def self.job_invocation_report_templates_select
|
86
|
+
Hash[ReportTemplate.unscoped.joins(:template_inputs).where(template_inputs: TemplateInput.where(name: 'job_id')).map { |template| [template.name, template.name] }]
|
87
|
+
end
|
78
88
|
end
|
@@ -2,10 +2,10 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
2
2
|
class << self
|
3
3
|
def proxy_command_options(template_invocation, host)
|
4
4
|
super.merge(:ssh_user => ssh_user(host),
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
:effective_user => effective_user(template_invocation),
|
6
|
+
:effective_user_method => effective_user_method(host),
|
7
|
+
:cleanup_working_dirs => cleanup_working_dirs?(host),
|
8
|
+
:ssh_port => ssh_port(host))
|
9
9
|
end
|
10
10
|
|
11
11
|
def humanized_name
|
data/app/models/targeting.rb
CHANGED
@@ -46,9 +46,13 @@ class Targeting < ApplicationRecord
|
|
46
46
|
# pluck(:id) returns duplicate results for HostCollections
|
47
47
|
host_ids = User.as(user.login) { Host.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).order(:name, :id).pluck(:id).uniq }
|
48
48
|
host_ids.shuffle!(random: Random.new) if randomized_ordering
|
49
|
+
self.assign_host_ids(host_ids)
|
50
|
+
self.save(:validate => false)
|
51
|
+
end
|
52
|
+
|
53
|
+
def assign_host_ids(host_ids)
|
49
54
|
# this can be optimized even more, by introducing bulk insert
|
50
55
|
self.targeting_hosts.build(host_ids.map { |id| { :host_id => id } })
|
51
|
-
self.save(:validate => false)
|
52
56
|
end
|
53
57
|
|
54
58
|
def dynamic?
|
@@ -11,6 +11,7 @@ class TemplateInvocation < ApplicationRecord
|
|
11
11
|
belongs_to :template, :class_name => 'JobTemplate', :foreign_key => 'template_id'
|
12
12
|
belongs_to :job_invocation, :inverse_of => :template_invocations
|
13
13
|
has_many :input_values, :class_name => 'TemplateInvocationInputValue', :dependent => :destroy
|
14
|
+
has_many :provider_input_values, :class_name => 'InvocationProviderInputValue', :dependent => :destroy
|
14
15
|
has_one :targeting, :through => :job_invocation
|
15
16
|
belongs_to :host, :class_name => 'Host::Managed', :foreign_key => :host_id
|
16
17
|
has_one :host_group, :through => :host, :source => :hostgroup
|
@@ -77,6 +78,7 @@ class TemplateInvocation < ApplicationRecord
|
|
77
78
|
def deep_clone
|
78
79
|
self.dup.tap do |invocation|
|
79
80
|
invocation.input_values = self.input_values.map(&:dup)
|
81
|
+
invocation.provider_input_values = self.provider_input_values.map(&:dup)
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
@@ -1,9 +1,9 @@
|
|
1
|
-
Deface::Override.new(:virtual_path
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
Deface::Override.new(:virtual_path => 'hosts/_form',
|
2
|
+
:name => 'add_execution_interface_js',
|
3
|
+
:insert_before => 'div#primary',
|
4
|
+
:text => '<%= javascript "foreman_remote_execution/execution_interface" %>')
|
5
5
|
|
6
|
-
Deface::Override.new(:virtual_path
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
Deface::Override.new(:virtual_path => 'nic/_base_form',
|
7
|
+
:name => 'add_execution_interface',
|
8
|
+
:insert_after => 'erb[loud]:contains("interface_provision")',
|
9
|
+
:partial => 'overrides/nics/execution_interface')
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Deface::Override.new(:virtual_path => 'subnets/_form',
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
:name => 'add_remote_execution_proxies_tab',
|
3
|
+
:insert_after => 'li.active',
|
4
|
+
:partial => 'overrides/subnets/rex_tab')
|
5
5
|
|
6
6
|
Deface::Override.new(:virtual_path => 'subnets/_form',
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
:name => 'add_remote_execution_proxies_tab_pane',
|
8
|
+
:insert_after => 'div#proxies',
|
9
|
+
:partial => 'overrides/subnets/rex_tab_pane')
|