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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +28 -2
  3. data/.rubocop_todo.yml +0 -7
  4. data/Gemfile +1 -1
  5. data/app/controllers/api/v2/foreign_input_sets_controller.rb +1 -2
  6. data/app/controllers/api/v2/job_invocations_controller.rb +2 -2
  7. data/app/controllers/api/v2/job_templates_controller.rb +2 -3
  8. data/app/controllers/api/v2/remote_execution_features_controller.rb +1 -2
  9. data/app/controllers/api/v2/template_inputs_controller.rb +1 -2
  10. data/app/controllers/concerns/foreman/controller/parameters/job_template.rb +3 -3
  11. data/app/controllers/concerns/foreman/controller/parameters/template_input.rb +2 -2
  12. data/app/controllers/job_invocations_controller.rb +4 -7
  13. data/app/controllers/job_templates_controller.rb +2 -2
  14. data/app/controllers/remote_execution_features_controller.rb +1 -2
  15. data/app/helpers/remote_execution_helper.rb +22 -17
  16. data/app/lib/actions/remote_execution/run_host_job.rb +4 -10
  17. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +29 -1
  18. data/app/models/host_status/execution_status.rb +1 -1
  19. data/app/models/input_template_renderer.rb +1 -1
  20. data/app/models/job_invocation.rb +12 -7
  21. data/app/models/job_invocation_composer.rb +15 -6
  22. data/app/models/job_invocation_task_group.rb +1 -1
  23. data/app/models/job_template.rb +45 -29
  24. data/app/models/job_template_importer.rb +21 -9
  25. data/app/models/remote_execution_feature.rb +4 -4
  26. data/app/models/setting/remote_execution.rb +7 -5
  27. data/app/models/ssh_execution_provider.rb +1 -3
  28. data/app/models/targeting.rb +6 -5
  29. data/app/models/template_invocation.rb +43 -1
  30. data/app/views/job_invocations/_form.html.erb +1 -1
  31. data/app/views/job_invocations/_tab_overview.html.erb +10 -1
  32. data/config/routes.rb +2 -2
  33. data/db/migrate/20150903192731_add_execution_to_interface.rb +6 -6
  34. data/db/migrate/20151215114631_add_host_id_to_template_invocation.rb +3 -2
  35. data/db/migrate/20160114120200_rename_job_categories.rb +1 -1
  36. data/db/migrate/20160127134031_add_advanced_to_template_input.rb +1 -1
  37. data/db/seeds.d/70-job_templates.rb +1 -1
  38. data/db/seeds.d/90-bookmarks.rb +1 -1
  39. data/doc/Gemfile +1 -1
  40. data/lib/foreman_remote_execution/engine.rb +1 -1
  41. data/lib/foreman_remote_execution/version.rb +1 -1
  42. data/lib/tasks/foreman_remote_execution_tasks.rake +1 -1
  43. data/locale/action_names.rb +1 -1
  44. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  45. data/locale/de/foreman_remote_execution.po +39 -12
  46. data/locale/en/foreman_remote_execution.po +38 -11
  47. data/locale/en_GB/foreman_remote_execution.po +38 -11
  48. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  49. data/locale/es/foreman_remote_execution.po +39 -12
  50. data/locale/foreman_remote_execution.pot +175 -141
  51. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  52. data/locale/fr/foreman_remote_execution.po +39 -12
  53. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  54. data/locale/ja/foreman_remote_execution.po +39 -12
  55. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  56. data/locale/ko/foreman_remote_execution.po +39 -12
  57. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  58. data/locale/pt_BR/foreman_remote_execution.po +39 -12
  59. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  60. data/locale/ru/foreman_remote_execution.po +39 -12
  61. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  62. data/locale/zh_CN/foreman_remote_execution.po +39 -12
  63. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  64. data/locale/zh_TW/foreman_remote_execution.po +39 -12
  65. data/test/benchmark/run_hosts_job_benchmark.rb +2 -2
  66. data/test/benchmark/targeting_benchmark.rb +2 -2
  67. data/test/factories/foreman_remote_execution_factories.rb +27 -2
  68. data/test/functional/api/v2/job_templates_controller_test.rb +1 -1
  69. data/test/test_plugin_helper.rb +2 -19
  70. data/test/unit/concerns/host_extensions_test.rb +54 -5
  71. data/test/unit/concerns/nic_extensions_test.rb +1 -1
  72. data/test/unit/execution_task_status_mapper_test.rb +1 -2
  73. data/test/unit/input_template_renderer_test.rb +1 -1
  74. data/test/unit/job_invocation_composer_test.rb +21 -29
  75. data/test/unit/job_invocation_test.rb +1 -2
  76. data/test/unit/job_template_effective_user_test.rb +1 -1
  77. data/test/unit/job_template_importer_test.rb +38 -20
  78. data/test/unit/job_template_test.rb +8 -8
  79. data/test/unit/remote_execution_feature_test.rb +6 -6
  80. data/test/unit/remote_execution_provider_test.rb +2 -1
  81. data/test/unit/targeting_test.rb +6 -6
  82. data/test/unit/template_input_test.rb +1 -1
  83. data/test/unit/template_invocation_input_value_test.rb +3 -3
  84. 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 => [ 'error', 'warning' ])
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) { |h, v| h.update(v.template_input.name => v.value) }
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
- %w(success total cancelled failed error warning pending progress).map(&:to_sym).reduce({}) do |acc, key|
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(:cancelled, :error, :success, :warning).reduce(:+)
192
+ done = counts.values_at(*map.results).reduce(:+)
189
193
  percent = progress(counts[:total], done)
190
- counts.merge(:progress => percent, :failed => counts[:error] + counts[:warning])
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
- template_invocations.joins(:run_host_job_task).where("#{ForemanTasks::Task.table_name}.result" => ['error', 'warning'])
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 = (template_params.fetch(:job_templates, {}).fetch(template_params[:job_template_id], {})).dup.with_indifferent_access
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
- job_invocation.targeting.attributes.slice('search_query', 'bookmark_id', 'user_id', 'targeting_type')
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).find_by_id(feature.job_template_id)
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)
@@ -2,7 +2,7 @@ class JobInvocationTaskGroup < ::ForemanTasks::TaskGroup
2
2
 
3
3
  has_one :job_invocation, :foreign_key => :task_group_id
4
4
 
5
- alias_method :resource, :job_invocation
5
+ alias resource job_invocation
6
6
 
7
7
  def resource_name
8
8
  N_('Job Invocation')
@@ -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
- # we have to override the base_class because polymorphic associations does not detect it correctly, more details at
59
- # http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#1010-Polymorphic-has-many-within-inherited-class-gotcha
60
- def self.base_class
61
- self
62
- end
63
- self.table_name = 'templates'
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
- # Import a template from ERB, with YAML metadata in the first comment. It
66
- # will overwrite (sync) an existing template if options[:update] is true.
67
- def self.import(contents, options = {})
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
- return if metadata.blank? || metadata.delete('kind') != 'job_template'
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
- def self.import!(template, options = {})
89
- template = import(template, options)
90
- template.save! if template
91
- template
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 || (build_effective_user.tap(&:set_defaults))
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, _metadata)
9
- template = JobTemplate.import(
10
- text.sub(/^name: .*$/, "name: #{name}").sub(/^model: .*$/, ''),
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, lambda { where(:host_action_button => true) }
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.find_by_label(label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
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.find_by_label(label)
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 => Proc.new {Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map{|method| [method, method]}]} }),
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
- N_('Port to use for SSH communication. Default port 22. You may override per host by setting a parameter called remote_execution_ssh_port.'),
38
- '22'),
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? 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.'),
41
- false),
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|
@@ -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
- hosts = Host.where(:id => ids).all.group_by(&:id)
58
- hosts.map { |id, h| "name = #{h.first.name}" }.join(' or ')
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>