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.
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>