foreman_remote_execution 1.3.0 → 1.3.1
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/.rubocop.yml +28 -2
- data/.rubocop_todo.yml +0 -7
- data/Gemfile +1 -1
- data/app/controllers/api/v2/foreign_input_sets_controller.rb +1 -2
- data/app/controllers/api/v2/job_invocations_controller.rb +2 -2
- data/app/controllers/api/v2/job_templates_controller.rb +2 -3
- data/app/controllers/api/v2/remote_execution_features_controller.rb +1 -2
- data/app/controllers/api/v2/template_inputs_controller.rb +1 -2
- data/app/controllers/concerns/foreman/controller/parameters/job_template.rb +3 -3
- data/app/controllers/concerns/foreman/controller/parameters/template_input.rb +2 -2
- data/app/controllers/job_invocations_controller.rb +4 -7
- data/app/controllers/job_templates_controller.rb +2 -2
- data/app/controllers/remote_execution_features_controller.rb +1 -2
- data/app/helpers/remote_execution_helper.rb +22 -17
- data/app/lib/actions/remote_execution/run_host_job.rb +4 -10
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +29 -1
- data/app/models/host_status/execution_status.rb +1 -1
- data/app/models/input_template_renderer.rb +1 -1
- data/app/models/job_invocation.rb +12 -7
- data/app/models/job_invocation_composer.rb +15 -6
- data/app/models/job_invocation_task_group.rb +1 -1
- data/app/models/job_template.rb +45 -29
- data/app/models/job_template_importer.rb +21 -9
- data/app/models/remote_execution_feature.rb +4 -4
- data/app/models/setting/remote_execution.rb +7 -5
- data/app/models/ssh_execution_provider.rb +1 -3
- data/app/models/targeting.rb +6 -5
- data/app/models/template_invocation.rb +43 -1
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/app/views/job_invocations/_tab_overview.html.erb +10 -1
- data/config/routes.rb +2 -2
- data/db/migrate/20150903192731_add_execution_to_interface.rb +6 -6
- data/db/migrate/20151215114631_add_host_id_to_template_invocation.rb +3 -2
- data/db/migrate/20160114120200_rename_job_categories.rb +1 -1
- data/db/migrate/20160127134031_add_advanced_to_template_input.rb +1 -1
- data/db/seeds.d/70-job_templates.rb +1 -1
- data/db/seeds.d/90-bookmarks.rb +1 -1
- data/doc/Gemfile +1 -1
- data/lib/foreman_remote_execution/engine.rb +1 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/lib/tasks/foreman_remote_execution_tasks.rake +1 -1
- data/locale/action_names.rb +1 -1
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +39 -12
- data/locale/en/foreman_remote_execution.po +38 -11
- data/locale/en_GB/foreman_remote_execution.po +38 -11
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +39 -12
- data/locale/foreman_remote_execution.pot +175 -141
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +39 -12
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +39 -12
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +39 -12
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +39 -12
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +39 -12
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +39 -12
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +39 -12
- data/test/benchmark/run_hosts_job_benchmark.rb +2 -2
- data/test/benchmark/targeting_benchmark.rb +2 -2
- data/test/factories/foreman_remote_execution_factories.rb +27 -2
- data/test/functional/api/v2/job_templates_controller_test.rb +1 -1
- data/test/test_plugin_helper.rb +2 -19
- data/test/unit/concerns/host_extensions_test.rb +54 -5
- data/test/unit/concerns/nic_extensions_test.rb +1 -1
- data/test/unit/execution_task_status_mapper_test.rb +1 -2
- data/test/unit/input_template_renderer_test.rb +1 -1
- data/test/unit/job_invocation_composer_test.rb +21 -29
- data/test/unit/job_invocation_test.rb +1 -2
- data/test/unit/job_template_effective_user_test.rb +1 -1
- data/test/unit/job_template_importer_test.rb +38 -20
- data/test/unit/job_template_test.rb +8 -8
- data/test/unit/remote_execution_feature_test.rb +6 -6
- data/test/unit/remote_execution_provider_test.rb +2 -1
- data/test/unit/targeting_test.rb +6 -6
- data/test/unit/template_input_test.rb +1 -1
- data/test/unit/template_invocation_input_value_test.rb +3 -3
- metadata +3 -3
@@ -6,7 +6,7 @@ class JobInvocation < ActiveRecord::Base
|
|
6
6
|
:pattern_template_invocations => lambda do |template_invocation|
|
7
7
|
_('template') + " #{template_invocation.template.name}"
|
8
8
|
end
|
9
|
-
}
|
9
|
+
}.freeze
|
10
10
|
|
11
11
|
belongs_to :targeting, :dependent => :destroy
|
12
12
|
has_many :all_template_invocations, :inverse_of => :job_invocation, :dependent => :destroy, :class_name => 'TemplateInvocation'
|
@@ -119,7 +119,7 @@ class JobInvocation < ActiveRecord::Base
|
|
119
119
|
end
|
120
120
|
|
121
121
|
def failed_template_invocation_tasks
|
122
|
-
template_invocation_tasks.where(:result =>
|
122
|
+
template_invocation_tasks.where(:result => TemplateInvocation::TaskResultMap.status_to_task_result(:failed))
|
123
123
|
end
|
124
124
|
|
125
125
|
def failed_host_ids
|
@@ -167,7 +167,9 @@ class JobInvocation < ActiveRecord::Base
|
|
167
167
|
key_re = /%\{([^\}]+)\}/
|
168
168
|
template_invocation = pattern_template_invocations.first
|
169
169
|
hash_base = Hash.new { |hash, key| hash[key] = "%{#{key}}" }
|
170
|
-
input_hash = template_invocation.input_values.reduce(hash_base)
|
170
|
+
input_hash = template_invocation.input_values.reduce(hash_base) do |h, v|
|
171
|
+
h.update(v.template_input.name => v.value)
|
172
|
+
end
|
171
173
|
input_hash.update(:job_category => job_category)
|
172
174
|
input_hash.update(:template_name => template_invocation.template.name)
|
173
175
|
description_format.scan(key_re) { |key| input_hash[key.first] }
|
@@ -179,21 +181,24 @@ class JobInvocation < ActiveRecord::Base
|
|
179
181
|
end
|
180
182
|
|
181
183
|
def progress_report
|
184
|
+
map = TemplateInvocation::TaskResultMap
|
185
|
+
all_keys = (map.results | map.statuses | [:progress, :total])
|
182
186
|
if queued? || !targeting.resolved?
|
183
|
-
|
187
|
+
all_keys.reduce({}) do |acc, key|
|
184
188
|
acc.merge(key => 0)
|
185
189
|
end
|
186
190
|
else
|
187
191
|
counts = task.sub_tasks_counts
|
188
|
-
done = counts.values_at(
|
192
|
+
done = counts.values_at(*map.results).reduce(:+)
|
189
193
|
percent = progress(counts[:total], done)
|
190
|
-
counts.merge(:progress => percent, :failed => counts
|
194
|
+
counts.merge(:progress => percent, :failed => counts.values_at(*map.status_to_task_result(:failed)).reduce(:+))
|
191
195
|
end
|
192
196
|
end
|
193
197
|
|
194
198
|
private
|
195
199
|
|
196
200
|
def failed_template_invocations
|
197
|
-
|
201
|
+
results = TemplateInvocation::TaskResultMap.status_to_task_result(:failed)
|
202
|
+
template_invocations.joins(:run_host_job_task).where("#{ForemanTasks::Task.table_name}.result" => results)
|
198
203
|
end
|
199
204
|
end
|
@@ -40,7 +40,7 @@ class JobInvocationComposer
|
|
40
40
|
# }
|
41
41
|
def template_invocations_params
|
42
42
|
providers_base.values.map do |template_params|
|
43
|
-
template_base =
|
43
|
+
template_base = template_params.fetch(:job_templates, {}).fetch(template_params[:job_template_id], {}).dup.with_indifferent_access
|
44
44
|
template_base[:template_id] = template_params[:job_template_id]
|
45
45
|
input_values_params = template_base.fetch(:input_values, {})
|
46
46
|
template_base[:input_values] = input_values_params.map do |id, values|
|
@@ -146,8 +146,13 @@ class JobInvocationComposer
|
|
146
146
|
class ParamsFromJobInvocation
|
147
147
|
attr_reader :job_invocation
|
148
148
|
|
149
|
-
def initialize(job_invocation)
|
149
|
+
def initialize(job_invocation, params = {})
|
150
150
|
@job_invocation = job_invocation
|
151
|
+
if params[:host_ids]
|
152
|
+
@host_ids = params[:host_ids]
|
153
|
+
elsif params[:failed_only]
|
154
|
+
@host_ids = job_invocation.failed_host_ids
|
155
|
+
end
|
151
156
|
end
|
152
157
|
|
153
158
|
def params
|
@@ -169,7 +174,11 @@ class JobInvocationComposer
|
|
169
174
|
end
|
170
175
|
|
171
176
|
def targeting_params
|
172
|
-
|
177
|
+
if @host_ids
|
178
|
+
{ :search_query => Targeting.build_query_from_hosts(@host_ids) }
|
179
|
+
else
|
180
|
+
job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'user_id', 'targeting_type')
|
181
|
+
end
|
173
182
|
end
|
174
183
|
|
175
184
|
def template_invocations_params
|
@@ -243,7 +252,7 @@ class JobInvocationComposer
|
|
243
252
|
raise Foreman::Exception.new(N_('No template mapped to feature %{feature_name}'),
|
244
253
|
:feature_name => feature.name)
|
245
254
|
end
|
246
|
-
template = JobTemplate.authorized(:view_job_templates).
|
255
|
+
template = JobTemplate.authorized(:view_job_templates).find_by(id: feature.job_template_id)
|
247
256
|
|
248
257
|
unless template
|
249
258
|
raise Foreman::Exception.new(N_('The template %{template_name} mapped to feature %{feature_name} is not accessible by the user'),
|
@@ -268,8 +277,8 @@ class JobInvocationComposer
|
|
268
277
|
@search_query = job_invocation.targeting.search_query unless job_invocation.targeting.bookmark_id.present?
|
269
278
|
end
|
270
279
|
|
271
|
-
def self.from_job_invocation(job_invocation)
|
272
|
-
self.new(ParamsFromJobInvocation.new(job_invocation).params)
|
280
|
+
def self.from_job_invocation(job_invocation, params = {})
|
281
|
+
self.new(ParamsFromJobInvocation.new(job_invocation, params).params)
|
273
282
|
end
|
274
283
|
|
275
284
|
def self.from_ui_params(ui_params)
|
data/app/models/job_template.rb
CHANGED
@@ -55,40 +55,56 @@ class JobTemplate < ::Template
|
|
55
55
|
has_one :effective_user, :class_name => 'JobTemplateEffectiveUser', :foreign_key => 'job_template_id', :dependent => :destroy
|
56
56
|
accepts_nested_attributes_for :effective_user, :update_only => true
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
class << self
|
59
|
+
# we have to override the base_class because polymorphic associations does not detect it correctly, more details at
|
60
|
+
# http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#1010-Polymorphic-has-many-within-inherited-class-gotcha
|
61
|
+
def base_class
|
62
|
+
self
|
63
|
+
end
|
64
|
+
table_name = 'templates'
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
transaction do
|
66
|
+
# Import a template from ERB, with YAML metadata in the first comment. It
|
67
|
+
# will overwrite (sync) an existing template if options[:update] is true.
|
68
|
+
def import_raw(contents, options = {})
|
69
69
|
metadata = parse_metadata(contents)
|
70
|
-
|
71
|
-
|
72
|
-
# Don't look for existing if we should always create a new template
|
73
|
-
existing = self.find_by_name(metadata['name']) unless options.delete(:build_new)
|
74
|
-
# Don't update if the template already exists, unless we're told to
|
75
|
-
return if !options.delete(:update) && existing
|
76
|
-
|
77
|
-
template = existing || self.new
|
78
|
-
template.sync_inputs(metadata.delete('template_inputs'))
|
79
|
-
template.sync_foreign_input_sets(metadata.delete('foreign_input_sets'))
|
80
|
-
template.sync_feature(metadata.delete('feature'))
|
81
|
-
template.assign_attributes(metadata.merge(:template => contents.gsub(/<%\#.+?.-?%>\n?/m, '').strip).merge(options))
|
82
|
-
template.assign_taxonomies if template.new_record?
|
70
|
+
import_parsed(metadata['name'], contents, metadata, options)
|
71
|
+
end
|
83
72
|
|
73
|
+
def import_raw!(contents, options = {})
|
74
|
+
template = import_raw(contents, options)
|
75
|
+
template.save! if template
|
84
76
|
template
|
85
77
|
end
|
86
|
-
end
|
87
78
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
79
|
+
# This method is used by foreman_templates to import templates, the API should be kept compatible with it
|
80
|
+
def import!(name, text, metadata, force = false)
|
81
|
+
metadata = metadata.dup
|
82
|
+
metadata.delete('associate')
|
83
|
+
JobTemplateImporter.import!(name, text, metadata)
|
84
|
+
end
|
85
|
+
|
86
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
87
|
+
def import_parsed(name, text, metadata, options = {})
|
88
|
+
transaction do
|
89
|
+
return if metadata.blank? || metadata.delete('kind') != 'job_template' ||
|
90
|
+
(metadata.key?('model') && metadata.delete('model') != self.to_s)
|
91
|
+
metadata['name'] = name
|
92
|
+
# Don't look for existing if we should always create a new template
|
93
|
+
existing = self.find_by(:name => name) unless options.delete(:build_new)
|
94
|
+
# Don't update if the template already exists, unless we're told to
|
95
|
+
return if !options.delete(:update) && existing
|
96
|
+
|
97
|
+
template = existing || self.new
|
98
|
+
template.sync_inputs(metadata.delete('template_inputs'))
|
99
|
+
template.sync_foreign_input_sets(metadata.delete('foreign_input_sets'))
|
100
|
+
template.sync_feature(metadata.delete('feature'))
|
101
|
+
template.locked = false if options.delete(:force)
|
102
|
+
template.assign_attributes(metadata.merge(:template => text.gsub(/<%\#.+?.-?%>\n?/m, '').strip).merge(options))
|
103
|
+
template.assign_taxonomies if template.new_record?
|
104
|
+
template
|
105
|
+
end
|
106
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
107
|
+
end
|
92
108
|
end
|
93
109
|
|
94
110
|
def metadata
|
@@ -130,7 +146,7 @@ class JobTemplate < ::Template
|
|
130
146
|
end
|
131
147
|
|
132
148
|
def effective_user
|
133
|
-
super ||
|
149
|
+
super || build_effective_user.tap(&:set_defaults)
|
134
150
|
end
|
135
151
|
|
136
152
|
def generate_description_format
|
@@ -5,20 +5,32 @@
|
|
5
5
|
# translation layer.
|
6
6
|
|
7
7
|
class JobTemplateImporter
|
8
|
-
def self.import!(name, text,
|
9
|
-
|
10
|
-
|
11
|
-
:update => true
|
12
|
-
)
|
8
|
+
def self.import!(name, text, metadata, force = false)
|
9
|
+
skip = skip_locked(name, force)
|
10
|
+
return skip if skip
|
13
11
|
|
12
|
+
template = JobTemplate.import_parsed(name, text, metadata, :update => true, :force => force)
|
14
13
|
c_or_u = template.new_record? ? 'Created' : 'Updated'
|
15
|
-
id_string = ('id' + template.id) rescue ''
|
16
14
|
|
17
|
-
result = " #{c_or_u} Template #{id_string}:#{name}"
|
15
|
+
result = " #{c_or_u} Template #{id_string template}:#{name}"
|
18
16
|
{ :old => template.template_was,
|
19
17
|
:new => template.template,
|
20
18
|
:status => template.save,
|
21
|
-
:result => result
|
22
|
-
|
19
|
+
:result => result}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.skip_locked(name, force)
|
23
|
+
template = JobTemplate.find_by :name => name
|
24
|
+
|
25
|
+
if template && template.locked? && !template.new_record? && !force
|
26
|
+
{ :old => template.template_was,
|
27
|
+
:new => template.template,
|
28
|
+
:status => false,
|
29
|
+
:result => "Skipping Template #{id_string template}:#{name} - template is locked" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.id_string(template)
|
34
|
+
template ? template.id.to_s : ''
|
23
35
|
end
|
24
36
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class RemoteExecutionFeature < ActiveRecord::Base
|
2
|
-
VALID_OPTIONS = [:provided_inputs, :description, :host_action_button]
|
2
|
+
VALID_OPTIONS = [:provided_inputs, :description, :host_action_button].freeze
|
3
3
|
validates :label, :name, :presence => true, :uniqueness => true
|
4
4
|
|
5
5
|
belongs_to :job_template
|
@@ -7,7 +7,7 @@ class RemoteExecutionFeature < ActiveRecord::Base
|
|
7
7
|
extend FriendlyId
|
8
8
|
friendly_id :label
|
9
9
|
|
10
|
-
scope :with_host_action_button,
|
10
|
+
scope :with_host_action_button, -> { where(:host_action_button => true) }
|
11
11
|
|
12
12
|
def provided_input_names
|
13
13
|
self.provided_inputs.to_s.split(',').map(&:strip)
|
@@ -18,7 +18,7 @@ class RemoteExecutionFeature < ActiveRecord::Base
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.feature(label)
|
21
|
-
self.
|
21
|
+
self.find_by(label: label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.register(label, name, options = {})
|
@@ -26,7 +26,7 @@ class RemoteExecutionFeature < ActiveRecord::Base
|
|
26
26
|
options.assert_valid_keys(*VALID_OPTIONS)
|
27
27
|
options[:host_action_button] = false unless options.key?(:host_action_button)
|
28
28
|
|
29
|
-
feature = self.
|
29
|
+
feature = self.find_by(label: label)
|
30
30
|
|
31
31
|
attributes = { :name => name, :provided_input_names => options[:provided_inputs], :description => options[:description], :host_action_button => options[:host_action_button] }
|
32
32
|
# in case DB does not have the attribute created yet but plugin initializer registers the feature, we need to skip this attribute
|
@@ -4,6 +4,7 @@ class Setting::RemoteExecution < Setting
|
|
4
4
|
# Check the table exists
|
5
5
|
return unless super
|
6
6
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
7
8
|
self.transaction do
|
8
9
|
[
|
9
10
|
self.set('remote_execution_fallback_proxy',
|
@@ -29,16 +30,17 @@ class Setting::RemoteExecution < Setting
|
|
29
30
|
'sudo',
|
30
31
|
'remote_execution_effective_user_method',
|
31
32
|
nil,
|
32
|
-
{ :collection =>
|
33
|
+
{ :collection => proc { Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] } }),
|
33
34
|
self.set('remote_execution_sync_templates',
|
34
35
|
N_('Whether we should sync templates from disk when running db:seed.'),
|
35
36
|
true),
|
36
37
|
self.set('remote_execution_ssh_port',
|
37
|
-
|
38
|
-
|
38
|
+
N_('Port to use for SSH communication. Default port 22. You may override per host by setting a parameter called remote_execution_ssh_port.'),
|
39
|
+
'22'),
|
39
40
|
self.set('remote_execution_connect_by_ip',
|
40
|
-
N_('Should the ip addresses on host interfaces be preferred over the fqdn?
|
41
|
-
|
41
|
+
N_('Should the ip addresses on host interfaces be preferred over the fqdn? '\
|
42
|
+
'It is useful, when DNS not resolving the fqdns properly. You may override this per host by setting a parameter called remote_execution_connect_by_ip.'),
|
43
|
+
false),
|
42
44
|
].each { |s| self.create! s.update(:category => 'Setting::RemoteExecution') }
|
43
45
|
end
|
44
46
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class SSHExecutionProvider < RemoteExecutionProvider
|
2
2
|
|
3
|
-
EFFECTIVE_USER_METHODS = %w[sudo su]
|
3
|
+
EFFECTIVE_USER_METHODS = %w[sudo su].freeze
|
4
4
|
|
5
5
|
class << self
|
6
6
|
def proxy_command_options(template_invocation, host)
|
@@ -58,8 +58,6 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
58
58
|
method
|
59
59
|
end
|
60
60
|
|
61
|
-
private
|
62
|
-
|
63
61
|
def effective_interfaces(host)
|
64
62
|
interfaces = []
|
65
63
|
%w(execution primary provision).map do |flag|
|
data/app/models/targeting.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
class Targeting < ActiveRecord::Base
|
2
2
|
|
3
|
-
STATIC_TYPE = 'static_query'
|
4
|
-
DYNAMIC_TYPE = 'dynamic_query'
|
5
|
-
TYPES = { STATIC_TYPE => N_('Static Query'), DYNAMIC_TYPE => N_('Dynamic Query') }
|
3
|
+
STATIC_TYPE = 'static_query'.freeze
|
4
|
+
DYNAMIC_TYPE = 'dynamic_query'.freeze
|
5
|
+
TYPES = { STATIC_TYPE => N_('Static Query'), DYNAMIC_TYPE => N_('Dynamic Query') }.freeze
|
6
6
|
RESOLVE_PERMISSION = :view_hosts
|
7
7
|
|
8
8
|
belongs_to :user
|
@@ -54,8 +54,9 @@ class Targeting < ActiveRecord::Base
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def self.build_query_from_hosts(ids)
|
57
|
-
|
58
|
-
hosts
|
57
|
+
return '' if ids.empty?
|
58
|
+
hosts = Host.where(:id => ids).distinct.pluck(:name)
|
59
|
+
"name ^ (#{hosts.join(', ')})"
|
59
60
|
end
|
60
61
|
|
61
62
|
def resolved?
|
@@ -5,7 +5,7 @@ class TemplateInvocation < ActiveRecord::Base
|
|
5
5
|
include ForemanRemoteExecution::ErrorsFlattener
|
6
6
|
FLATTENED_ERRORS_MAPPING = { :input_values => lambda do |input_value|
|
7
7
|
_('Input') + " #{input_value.template_input.name}"
|
8
|
-
end }
|
8
|
+
end }.freeze
|
9
9
|
|
10
10
|
|
11
11
|
belongs_to :template, :class_name => 'JobTemplate', :foreign_key => 'template_id'
|
@@ -25,6 +25,48 @@ class TemplateInvocation < ActiveRecord::Base
|
|
25
25
|
scoped_search :in => :template, :on => :job_category, :complete_value => true
|
26
26
|
scoped_search :in => :template, :on => :name, :complete_value => true
|
27
27
|
|
28
|
+
class TaskResultMap
|
29
|
+
MAP = {
|
30
|
+
:cancelled => :cancelled,
|
31
|
+
:error => :failed,
|
32
|
+
:pending => :pending,
|
33
|
+
:success => :success,
|
34
|
+
:warning => :failed
|
35
|
+
}.with_indifferent_access
|
36
|
+
|
37
|
+
REVERSE_MAP = MAP.reduce({}) do |acc, (key, value)|
|
38
|
+
acc[value] ||= []
|
39
|
+
acc[value] << key
|
40
|
+
acc
|
41
|
+
end.with_indifferent_access
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def results
|
45
|
+
MAP.keys.map(&:to_sym)
|
46
|
+
end
|
47
|
+
|
48
|
+
def statuses
|
49
|
+
REVERSE_MAP.keys.map(&:to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
# 1:1
|
53
|
+
# error => failed
|
54
|
+
def task_result_to_status(result)
|
55
|
+
MAP[result].try(:to_sym) || result
|
56
|
+
end
|
57
|
+
|
58
|
+
# 1:n
|
59
|
+
# failed => [:error, :warning]
|
60
|
+
def status_to_task_result(status)
|
61
|
+
if REVERSE_MAP.key? status
|
62
|
+
REVERSE_MAP[status].map(&:to_sym)
|
63
|
+
else
|
64
|
+
Array(status)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
28
70
|
def to_action_input
|
29
71
|
{ :id => id, :name => template.name }
|
30
72
|
end
|
@@ -72,7 +72,7 @@
|
|
72
72
|
|
73
73
|
<div class="advanced hidden">
|
74
74
|
<% if job_template.effective_user.overridable? %>
|
75
|
-
<%= text_f job_template_fields, :effective_user, :label => _('Effective user'), :help_inline => N_("A user to be used for executing the script. If it differs from the SSH user, su or sudo is used to switch the accounts.") %>
|
75
|
+
<%= text_f job_template_fields, :effective_user, :value => @composer.template_invocation(job_template).try(:effective_user), :label => _('Effective user'), :help_inline => N_("A user to be used for executing the script. If it differs from the SSH user, su or sudo is used to switch the accounts.") %>
|
76
76
|
<% end %>
|
77
77
|
<%= render :partial => 'description_fields', :locals => { :f => f, :job_template => job_template, :disabled => job_template != selected_templates_per_provider[provider_type] } %>
|
78
78
|
</div>
|
@@ -41,7 +41,16 @@
|
|
41
41
|
</ul>
|
42
42
|
<% end %>
|
43
43
|
<% if template_invocation.effective_user %>
|
44
|
-
<b><%= _("Effective user") %></b>: <%= template_invocation.effective_user
|
44
|
+
<b><%= _("Effective user") %></b>: <%= template_invocation.effective_user %><br>
|
45
|
+
<% end %>
|
46
|
+
<% if job_invocation.concurrency_level %>
|
47
|
+
<b><%= _("Concurrency level limited to") %></b>: <%= job_invocation.concurrency_level %> <%= _('tasks at a time') %><br>
|
48
|
+
<% end %>
|
49
|
+
<% if job_invocation.time_span %>
|
50
|
+
<b><%= _("Set to distribute over") %></b>: <%= job_invocation.concurrency_level %> <%= _('minutes') %><br>
|
51
|
+
<% end %>
|
52
|
+
<% if job_invocation.task && job_invocation.task.delayed? || job_invocation.task.recurring? %>
|
53
|
+
<b><%= _("Scheduled to start at") %></b>: <%= job_invocation.task.start_at.try(:in_time_zone) %><br>
|
45
54
|
<% end %>
|
46
55
|
<% end %>
|
47
56
|
</div>
|