foreman_remote_execution 4.3.1 → 4.5.2
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/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')
|