foreman_remote_execution 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|