foreman_remote_execution 4.4.0 → 4.5.3
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 +13 -24
- 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 +13 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
- 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/job_invocation.rb +31 -12
- data/app/models/job_invocation_composer.rb +61 -19
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/ssh_execution_provider.rb +4 -4
- data/app/models/targeting.rb +5 -1
- data/app/overrides/execution_interface.rb +8 -8
- data/app/overrides/subnet_proxies.rb +6 -6
- 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/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
- data/extra/cockpit/foreman-cockpit-session +6 -6
- data/lib/foreman_remote_execution/engine.rb +11 -8
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +2 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
- data/test/unit/job_invocation_composer_test.rb +59 -2
- data/test/unit/job_invocation_test.rb +1 -1
- data/webpack/JobWizard/JobWizard.js +80 -19
- data/webpack/JobWizard/JobWizard.scss +42 -1
- data/webpack/JobWizard/JobWizardConstants.js +11 -0
- data/webpack/JobWizard/JobWizardSelectors.js +27 -1
- 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 +34 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -44
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +9 -1
- 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 +3 -0
- data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
- data/webpack/JobWizard/steps/form/SelectField.js +24 -3
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
- data/webpack/global_index.js +5 -3
- data/webpack/index.js +3 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +1 -5
- 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/{fills.js → fillRecentJobsCard.js} +7 -6
- data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
- data/webpack/react_app/extend/reducers.js +2 -1
- metadata +24 -14
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
- data/test/models/orchestration/ssh_test.rb +0 -56
- data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -20
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -83
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -64
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -36
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -22
- data/webpack/fills_index.js +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 812237a4d9fd4c4000c8522be1ecdaf539f8ca15e9b6e63a599da92419ade6f4
|
|
4
|
+
data.tar.gz: 8a0c5f90892a268816c0b16184faef14b55a36f60fdd0750303291d0688ba83f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a572fa58d1c7b1776c5493afdb9e2940f97e03a996e5e56c5ed633497b825a8e77b2013a7570c8990403f3f2e0f197036aee556c7177cae3fb6feac585f5c95b
|
|
7
|
+
data.tar.gz: 57d64d09cbd7b14984564201e53df9a76bd6e95fc7008283e506924813d7ee18996ece236d2d6258da4f61a8a56816491b9b690a01f36e0a94de364db4c5ba64
|
|
@@ -80,18 +80,15 @@ module Api
|
|
|
80
80
|
api :POST, '/job_invocations/', N_('Create a job invocation')
|
|
81
81
|
param_group :job_invocation, :as => :create
|
|
82
82
|
def create
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
validate_template
|
|
87
|
-
composer = JobInvocationComposer.from_api_params(
|
|
88
|
-
job_invocation_params
|
|
89
|
-
)
|
|
90
|
-
end
|
|
83
|
+
composer = JobInvocationComposer.from_api_params(
|
|
84
|
+
job_invocation_params
|
|
85
|
+
)
|
|
91
86
|
composer.trigger!
|
|
92
87
|
@job_invocation = composer.job_invocation
|
|
93
88
|
@hosts = @job_invocation.targeting.hosts
|
|
94
89
|
process_response @job_invocation
|
|
90
|
+
rescue JobInvocationComposer::JobTemplateNotFound, JobInvocationComposer::FeatureNotFound => e
|
|
91
|
+
not_found(error: { message: e.message })
|
|
95
92
|
end
|
|
96
93
|
|
|
97
94
|
api :GET, '/job_invocations/:id/hosts/:host_id', N_('Get output for a host')
|
|
@@ -128,7 +125,7 @@ module Api
|
|
|
128
125
|
render :json => { :cancelled => result, :id => @job_invocation.id }
|
|
129
126
|
else
|
|
130
127
|
render :json => { :message => _('The job could not be cancelled.') },
|
|
131
|
-
|
|
128
|
+
:status => :unprocessable_entity
|
|
132
129
|
end
|
|
133
130
|
end
|
|
134
131
|
|
|
@@ -143,7 +140,7 @@ module Api
|
|
|
143
140
|
process_response @job_invocation
|
|
144
141
|
else
|
|
145
142
|
render :json => { :error => _('Could not rerun job %{id} because its template could not be found') % { :id => composer.reruns } },
|
|
146
|
-
|
|
143
|
+
:status => :not_found
|
|
147
144
|
end
|
|
148
145
|
end
|
|
149
146
|
|
|
@@ -190,19 +187,19 @@ module Api
|
|
|
190
187
|
not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
|
|
191
188
|
end
|
|
192
189
|
|
|
193
|
-
def validate_template
|
|
194
|
-
JobTemplate.authorized(:view_job_templates).find(job_invocation_params['job_template_id'])
|
|
195
|
-
rescue ActiveRecord::RecordNotFound
|
|
196
|
-
not_found({ :error => { :message => (_("Template with id '%{id}' was not found") % { :id => job_invocation_params['job_template_id'] }) } })
|
|
197
|
-
end
|
|
198
|
-
|
|
199
190
|
def job_invocation_params
|
|
200
191
|
return @job_invocation_params if @job_invocation_params.present?
|
|
201
192
|
|
|
202
193
|
job_invocation_params = params.fetch(:job_invocation, {}).dup
|
|
194
|
+
|
|
195
|
+
if job_invocation_params[:feature].present? && job_invocation_params[:job_template_id].present?
|
|
196
|
+
raise _("Only one of feature or job_template_id can be specified")
|
|
197
|
+
end
|
|
198
|
+
|
|
203
199
|
if job_invocation_params.key?(:ssh)
|
|
204
200
|
job_invocation_params.merge!(job_invocation_params.delete(:ssh).permit(:effective_user))
|
|
205
201
|
end
|
|
202
|
+
|
|
206
203
|
job_invocation_params[:inputs] ||= {}
|
|
207
204
|
job_invocation_params[:inputs].permit!
|
|
208
205
|
permit_provider_inputs job_invocation_params
|
|
@@ -214,14 +211,6 @@ module Api
|
|
|
214
211
|
providers.each { |provider| invocation_params[provider.provider_input_namespace]&.permit! }
|
|
215
212
|
end
|
|
216
213
|
|
|
217
|
-
def composer_for_feature
|
|
218
|
-
JobInvocationComposer.for_feature(
|
|
219
|
-
job_invocation_params[:feature],
|
|
220
|
-
job_invocation_params[:host_ids],
|
|
221
|
-
job_invocation_params[:inputs].to_hash
|
|
222
|
-
)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
214
|
def output_lines_since(task, time)
|
|
226
215
|
since = time.to_f if time.present?
|
|
227
216
|
line_sets = task.main_action.live_output
|
|
@@ -67,7 +67,7 @@ class JobInvocationsController < ApplicationController
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def index
|
|
70
|
-
@job_invocations = resource_base_search_and_page.
|
|
70
|
+
@job_invocations = resource_base_search_and_page.preload(:task, :targeting).order('job_invocations.id DESC')
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
# refreshes the form
|
|
@@ -24,10 +24,10 @@ class JobTemplatesController < ::TemplatesController
|
|
|
24
24
|
render :plain => output
|
|
25
25
|
else
|
|
26
26
|
render status: :not_acceptable,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
plain: _(
|
|
28
|
+
'Problem with previewing the template: %{error}. Note that you must save template input changes before you try to preview it.' %
|
|
29
|
+
{:error => renderer.error_message}
|
|
30
|
+
)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -8,6 +8,25 @@ class UiJobWizardController < ::Api::V2::BaseController
|
|
|
8
8
|
render :json => {:job_categories =>job_categories}
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def template
|
|
12
|
+
job_template = JobTemplate.authorized.find(params[:id])
|
|
13
|
+
advanced_template_inputs, template_inputs = map_template_inputs(job_template.template_inputs_with_foreign).partition { |x| x["advanced"] }
|
|
14
|
+
render :json => {
|
|
15
|
+
:job_template => job_template,
|
|
16
|
+
:effective_user => job_template.effective_user,
|
|
17
|
+
:template_inputs => template_inputs,
|
|
18
|
+
:advanced_template_inputs => advanced_template_inputs,
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resource_name(nested_resource = nil)
|
|
23
|
+
nested_resource || 'job_template'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def map_template_inputs(template_inputs_with_foreign)
|
|
27
|
+
template_inputs_with_foreign.map { |input| input.attributes.merge({:resource_type => input.resource_type&.tableize }) }
|
|
28
|
+
end
|
|
29
|
+
|
|
11
30
|
def resource_class
|
|
12
31
|
JobTemplate
|
|
13
32
|
end
|
|
@@ -34,8 +34,8 @@ module JobInvocationsHelper
|
|
|
34
34
|
hosts.map do |host|
|
|
35
35
|
collapsed_preview(host) +
|
|
36
36
|
render(:partial => 'job_invocations/user_input',
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
:locals => { :template_invocation => template_invocation,
|
|
38
|
+
:target => host })
|
|
39
39
|
end.reduce(:+)
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -7,6 +7,10 @@ module RemoteExecutionHelper
|
|
|
7
7
|
@job_hosts_authorizer ||= Authorizer.new(User.current, :collection => @hosts)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
def host_tasks_authorizer
|
|
11
|
+
@host_tasks_authorizer ||= Authorizer.new(User.current, :collection => @job_invocation.sub_tasks)
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def host_counter(label, count)
|
|
11
15
|
content_tag(:div, :class => 'host_counter') do
|
|
12
16
|
content_tag(:div, label, :class => 'header') + content_tag(:div, count.to_s, :class => 'count')
|
|
@@ -27,19 +31,19 @@ module RemoteExecutionHelper
|
|
|
27
31
|
|
|
28
32
|
if authorized_for(hash_for_host_path(host).merge(auth_object: host, permission: :view_hosts, authorizer: job_hosts_authorizer))
|
|
29
33
|
links << { title: _('Host detail'),
|
|
30
|
-
|
|
34
|
+
action: { href: host_path(host), 'data-method': 'get', id: "#{host.name}-actions-detail" } }
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
if authorized_for(hash_for_rerun_job_invocation_path(id: job_invocation, host_ids: [ host.id ], authorizer: job_hosts_authorizer))
|
|
34
38
|
links << { title: (_('Rerun on %s') % host.name),
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
action: { href: rerun_job_invocation_path(job_invocation, host_ids: [ host.id ]),
|
|
40
|
+
'data-method': 'get', id: "#{host.name}-actions-rerun" } }
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks))
|
|
43
|
+
if host_task.present? && authorized_for(hash_for_foreman_tasks_task_path(host_task).merge(auth_object: host_task, permission: :view_foreman_tasks, authorizer: host_tasks_authorizer))
|
|
40
44
|
links << { title: _('Host task'),
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
action: { href: foreman_tasks_task_path(host_task),
|
|
46
|
+
'data-method': 'get', id: "#{host.name}-actions-task" } }
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
links
|
|
@@ -159,11 +163,11 @@ module RemoteExecutionHelper
|
|
|
159
163
|
content_tag :pre, preview
|
|
160
164
|
elsif target.nil?
|
|
161
165
|
alert :text => _('Could not render the preview because no host matches the search query.'),
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
:class => 'alert alert-block alert-warning base',
|
|
167
|
+
:close => false
|
|
164
168
|
else
|
|
165
169
|
alert :class => 'alert-block alert-danger base in fade has-error',
|
|
166
|
-
|
|
170
|
+
:text => renderer.error_message.html_safe # rubocop:disable Rails/OutputSafety
|
|
167
171
|
end
|
|
168
172
|
end
|
|
169
173
|
|
|
@@ -5,6 +5,8 @@ module Actions
|
|
|
5
5
|
include ::Actions::Helpers::WithDelegatedAction
|
|
6
6
|
include ::Actions::ObservableAction
|
|
7
7
|
|
|
8
|
+
execution_plan_hooks.use :emit_feature_event, :on => :success
|
|
9
|
+
|
|
8
10
|
middleware.do_not_use Dynflow::Middleware::Common::Transaction
|
|
9
11
|
middleware.use Actions::Middleware::HideSecrets
|
|
10
12
|
|
|
@@ -17,7 +19,12 @@ module Actions
|
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def plan(job_invocation, host, template_invocation, proxy_selector = ::RemoteExecutionProxySelector.new, options = {})
|
|
20
|
-
|
|
22
|
+
features = template_invocation.template.remote_execution_features.pluck(:label).uniq
|
|
23
|
+
action_subject(host,
|
|
24
|
+
:job_category => job_invocation.job_category,
|
|
25
|
+
:description => job_invocation.description,
|
|
26
|
+
:job_invocation_id => job_invocation.id,
|
|
27
|
+
:job_features => features)
|
|
21
28
|
|
|
22
29
|
template_invocation.host_id = host.id
|
|
23
30
|
template_invocation.run_host_job_task_id = task.id
|
|
@@ -40,12 +47,16 @@ module Actions
|
|
|
40
47
|
script = renderer.render
|
|
41
48
|
raise _('Failed rendering template: %s') % renderer.error_message unless script
|
|
42
49
|
|
|
50
|
+
first_execution = host.executed_through_proxies.where(:id => proxy.id).none?
|
|
51
|
+
host.executed_through_proxies << proxy if first_execution
|
|
52
|
+
|
|
43
53
|
additional_options = { :hostname => provider.find_ip_or_hostname(host),
|
|
44
54
|
:script => script,
|
|
45
55
|
:execution_timeout_interval => job_invocation.execution_timeout_interval,
|
|
46
56
|
:secrets => secrets(host, job_invocation, provider),
|
|
47
57
|
:use_batch_triggering => true,
|
|
48
|
-
:use_concurrency_control => options[:use_concurrency_control]
|
|
58
|
+
:use_concurrency_control => options[:use_concurrency_control],
|
|
59
|
+
:first_execution => first_execution }
|
|
49
60
|
action_options = provider.proxy_command_options(template_invocation, host)
|
|
50
61
|
.merge(additional_options)
|
|
51
62
|
|
|
@@ -58,6 +69,22 @@ module Actions
|
|
|
58
69
|
check_exit_status
|
|
59
70
|
end
|
|
60
71
|
|
|
72
|
+
def self.feature_job_event_name(label, suffix = :success)
|
|
73
|
+
::Foreman::Observable.event_name_for("#{::Actions::RemoteExecution::RunHostJob.event_name_base}_#{label}_#{::Actions::RemoteExecution::RunHostJob.event_name_suffix(suffix)}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def emit_feature_event(execution_plan, hook = :success)
|
|
77
|
+
return unless root_action?
|
|
78
|
+
|
|
79
|
+
payload = event_payload(execution_plan)
|
|
80
|
+
if input["job_features"]&.any?
|
|
81
|
+
input['job_features'].each do |feature|
|
|
82
|
+
name = "#{self.class.event_name_base}_#{feature}_#{self.class.event_name_suffix(hook)}"
|
|
83
|
+
trigger_hook name, payload: payload
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
61
88
|
def secrets(host, job_invocation, provider)
|
|
62
89
|
job_secrets = { :ssh_password => job_invocation.password,
|
|
63
90
|
:key_passphrase => job_invocation.key_passphrase,
|
|
@@ -78,7 +105,7 @@ module Actions
|
|
|
78
105
|
|
|
79
106
|
def humanized_input
|
|
80
107
|
N_('%{description} on %{host}') % { :host => input[:host].try(:[], :name),
|
|
81
|
-
|
|
108
|
+
:description => input[:description].try(:capitalize) || input[:job_category] }
|
|
82
109
|
end
|
|
83
110
|
|
|
84
111
|
def humanized_name
|
|
@@ -186,10 +213,13 @@ module Actions
|
|
|
186
213
|
'All %{count} applicable proxies are down. Tried %{proxy_names}',
|
|
187
214
|
offline_proxies.count) % settings
|
|
188
215
|
elsif proxy == :not_defined
|
|
189
|
-
settings = {
|
|
190
|
-
|
|
216
|
+
settings = {
|
|
217
|
+
global_proxy: 'remote_execution_global_proxy',
|
|
218
|
+
fallback_proxy: 'remote_execution_fallback_proxy',
|
|
219
|
+
provider: provider,
|
|
220
|
+
}
|
|
191
221
|
|
|
192
|
-
raise _('Could not use any proxy. Consider configuring %{global_proxy}, ' +
|
|
222
|
+
raise _('Could not use any proxy for the %{provider} job. Consider configuring %{global_proxy}, ' +
|
|
193
223
|
'%{fallback_proxy} in settings') % settings
|
|
194
224
|
end
|
|
195
225
|
proxy
|
|
@@ -6,17 +6,19 @@ module ForemanRemoteExecution
|
|
|
6
6
|
has_many :template_invocations, :dependent => :destroy, :foreign_key => 'host_id'
|
|
7
7
|
has_one :execution_status_object, :class_name => 'HostStatus::ExecutionStatus', :foreign_key => 'host_id', :dependent => :destroy
|
|
8
8
|
has_many :run_host_job_tasks, :through => :template_invocations
|
|
9
|
+
has_many :host_proxy_invocations, :foreign_key => 'host_id', :dependent => :destroy
|
|
10
|
+
has_many :executed_through_proxies, :through => :host_proxy_invocations, :source => 'smart_proxy'
|
|
9
11
|
|
|
10
12
|
scoped_search :relation => :run_host_job_tasks, :on => :result, :rename => 'job_invocation.result',
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
:ext_method => :search_by_job_invocation,
|
|
14
|
+
:only_explicit => true,
|
|
15
|
+
:complete_value => TemplateInvocation::TaskResultMap::REVERSE_MAP
|
|
14
16
|
|
|
15
17
|
scoped_search :relation => :template_invocations, :on => :job_invocation_id,
|
|
16
|
-
|
|
18
|
+
:rename => 'job_invocation.id', :only_explicit => true, :ext_method => :search_by_job_invocation
|
|
17
19
|
|
|
18
20
|
scoped_search :relation => :execution_status_object, :on => :status, :rename => :execution_status,
|
|
19
|
-
|
|
21
|
+
:complete_value => { :ok => HostStatus::ExecutionStatus::OK, :error => HostStatus::ExecutionStatus::ERROR }
|
|
20
22
|
|
|
21
23
|
def search_by_job_invocation(key, operator, value)
|
|
22
24
|
if key == 'job_invocation.result'
|
|
@@ -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
|
|
@@ -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
|
|
|
@@ -80,10 +86,18 @@ class JobInvocation < ApplicationRecord
|
|
|
80
86
|
allow :sub_task_for_host, :template_invocations_hosts
|
|
81
87
|
end
|
|
82
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
|
+
|
|
83
97
|
def self.search_by_status(key, operator, value)
|
|
84
98
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
|
85
99
|
conditions[0] = "NOT (#{conditions[0]})" if operator == '<>'
|
|
86
|
-
{ :conditions => sanitize_sql_for_conditions(conditions), :
|
|
100
|
+
{ :conditions => sanitize_sql_for_conditions(conditions), :joins => :task }
|
|
87
101
|
end
|
|
88
102
|
|
|
89
103
|
def self.search_by_recurring_logic(key, operator, value)
|
|
@@ -177,11 +191,16 @@ class JobInvocation < ApplicationRecord
|
|
|
177
191
|
end
|
|
178
192
|
|
|
179
193
|
def total_hosts_count
|
|
194
|
+
count = _('N/A')
|
|
195
|
+
|
|
180
196
|
if targeting.resolved?
|
|
181
|
-
task&.main_action
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
count = if task&.main_action.respond_to?(:total_count)
|
|
198
|
+
task.main_action.total_count
|
|
199
|
+
else
|
|
200
|
+
targeting.hosts.count
|
|
201
|
+
end
|
|
184
202
|
end
|
|
203
|
+
count
|
|
185
204
|
end
|
|
186
205
|
|
|
187
206
|
def pattern_template_invocation_for_host(host)
|