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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +27 -22
  3. data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
  4. data/app/controllers/job_invocations_controller.rb +1 -1
  5. data/app/controllers/job_templates_controller.rb +4 -4
  6. data/app/controllers/ui_job_wizard_controller.rb +19 -0
  7. data/app/helpers/job_invocations_helper.rb +2 -2
  8. data/app/helpers/remote_execution_helper.rb +40 -9
  9. data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
  10. data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
  11. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
  12. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
  13. data/app/models/host_proxy_invocation.rb +4 -0
  14. data/app/models/host_status/execution_status.rb +5 -5
  15. data/app/models/invocation_provider_input_value.rb +12 -0
  16. data/app/models/job_invocation.rb +35 -12
  17. data/app/models/job_invocation_composer.rb +74 -19
  18. data/app/models/remote_execution_provider.rb +18 -3
  19. data/app/models/setting/remote_execution.rb +11 -1
  20. data/app/models/ssh_execution_provider.rb +4 -4
  21. data/app/models/targeting.rb +5 -1
  22. data/app/models/template_invocation.rb +2 -0
  23. data/app/overrides/execution_interface.rb +8 -8
  24. data/app/overrides/subnet_proxies.rb +6 -6
  25. data/app/services/renderer_methods.rb +12 -0
  26. data/app/views/job_invocations/_form.html.erb +8 -0
  27. data/app/views/job_invocations/index.html.erb +1 -1
  28. data/app/views/templates/ssh/module_action.erb +1 -0
  29. data/app/views/templates/ssh/puppet_run_once.erb +1 -0
  30. data/config/routes.rb +1 -0
  31. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
  32. data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
  33. data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
  34. data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
  35. data/extra/cockpit/foreman-cockpit-session +6 -6
  36. data/foreman_remote_execution.gemspec +1 -1
  37. data/lib/foreman_remote_execution/engine.rb +14 -12
  38. data/lib/foreman_remote_execution/version.rb +1 -1
  39. data/locale/action_names.rb +1 -0
  40. data/locale/de/foreman_remote_execution.po +77 -27
  41. data/locale/en/foreman_remote_execution.po +77 -27
  42. data/locale/en_GB/foreman_remote_execution.po +77 -27
  43. data/locale/es/foreman_remote_execution.po +77 -27
  44. data/locale/foreman_remote_execution.pot +241 -163
  45. data/locale/fr/foreman_remote_execution.po +77 -27
  46. data/locale/ja/foreman_remote_execution.po +77 -27
  47. data/locale/ko/foreman_remote_execution.po +77 -27
  48. data/locale/pt_BR/foreman_remote_execution.po +77 -27
  49. data/locale/ru/foreman_remote_execution.po +77 -27
  50. data/locale/zh_CN/foreman_remote_execution.po +77 -27
  51. data/locale/zh_TW/foreman_remote_execution.po +77 -27
  52. data/package.json +4 -2
  53. data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
  54. data/test/helpers/remote_execution_helper_test.rb +16 -0
  55. data/test/unit/job_invocation_composer_test.rb +100 -3
  56. data/test/unit/job_invocation_report_template_test.rb +57 -0
  57. data/test/unit/job_invocation_test.rb +1 -1
  58. data/webpack/JobWizard/JobWizard.js +95 -11
  59. data/webpack/JobWizard/JobWizard.scss +53 -0
  60. data/webpack/JobWizard/JobWizardConstants.js +16 -0
  61. data/webpack/JobWizard/JobWizardSelectors.js +47 -0
  62. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
  63. data/webpack/JobWizard/__tests__/fixtures.js +128 -0
  64. data/webpack/JobWizard/__tests__/integration.test.js +84 -0
  65. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
  66. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
  67. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
  68. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
  69. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
  70. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
  71. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +123 -0
  72. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
  73. data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
  74. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
  75. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
  76. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
  77. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
  78. data/webpack/JobWizard/steps/Schedule/index.js +41 -0
  79. data/webpack/JobWizard/steps/form/FormHelpers.js +20 -0
  80. data/webpack/JobWizard/steps/form/Formatter.js +149 -0
  81. data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
  82. data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
  83. data/webpack/JobWizard/steps/form/SelectField.js +60 -0
  84. data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
  85. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  86. data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
  87. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
  88. data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
  89. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  90. data/webpack/global_index.js +6 -0
  91. data/webpack/index.js +3 -4
  92. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +83 -0
  93. data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
  94. data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
  95. data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
  96. data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
  97. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
  98. data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
  99. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
  100. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
  101. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
  102. data/webpack/react_app/extend/fillRecentJobsCard.js +11 -0
  103. data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
  104. data/webpack/react_app/extend/reducers.js +5 -0
  105. metadata +52 -8
  106. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
  107. data/app/views/api/v2/registration/_form.html.erb +0 -12
  108. 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
- value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
48
- special_values: [:current_user], aliases: ['owner'], :only_explicit => true
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
- :only_explicit => true, :complete_value => Hash[HostStatus::ExecutionStatus::STATUS_NAMES.values.map { |v| [v, v] }]
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
- scope :with_task, -> { references(:task) }
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
- :ext_method => :search_by_recurring_logic, :only_explicit => true,
66
- :complete_value => { :true => true, :false => false }
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), :include => :task }
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&.total_count || targeting.hosts.count
178
- else
179
- _('N/A')
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 => api_params[: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
- { :input_name => name, :template_name => template.name }
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(api_params[:job_template_id])
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
- if hosts.is_a? Bookmark
237
- @host_bookmark = hosts
238
- elsif hosts.is_a? Host::Base
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.host_ids = JobInvocation.find(@reruns).targeting.host_ids
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
- :effective_user => build_effective_user(template_invocation_params))
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
- { :current_value => method, :valid_methods => EFFECTIVE_USER_METHODS}
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
- _('Form Job Template'),
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
- :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))
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
@@ -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 => 'hosts/_form',
2
- :name => 'add_execution_interface_js',
3
- :insert_before => 'div#primary',
4
- :text => '<%= javascript "foreman_remote_execution/execution_interface" %>')
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 => 'nic/_base_form',
7
- :name => 'add_execution_interface',
8
- :insert_after => 'erb[loud]:contains("interface_provision")',
9
- :partial => 'overrides/nics/execution_interface')
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
- :name => 'add_remote_execution_proxies_tab',
3
- :insert_after => 'li.active',
4
- :partial => 'overrides/subnets/rex_tab')
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
- :name => 'add_remote_execution_proxies_tab_pane',
8
- :insert_after => 'div#proxies',
9
- :partial => 'overrides/subnets/rex_tab_pane')
7
+ :name => 'add_remote_execution_proxies_tab_pane',
8
+ :insert_after => 'div#proxies',
9
+ :partial => 'overrides/subnets/rex_tab_pane')