foreman_remote_execution 0.2.3 → 0.3.0

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +7 -0
  4. data/.rubocop_todo.yml +16 -5
  5. data/.tx/config +1 -1
  6. data/app/assets/javascripts/template_invocation.js +14 -1
  7. data/app/assets/stylesheets/job_invocations.css.scss +0 -13
  8. data/app/assets/stylesheets/template_invocation.css.scss +7 -13
  9. data/app/controllers/api/v2/foreign_input_sets_controller.rb +1 -1
  10. data/app/controllers/api/v2/job_invocations_controller.rb +12 -18
  11. data/app/controllers/api/v2/remote_execution_features_controller.rb +38 -0
  12. data/app/controllers/api/v2/template_inputs_controller.rb +1 -0
  13. data/app/controllers/job_invocations_controller.rb +3 -13
  14. data/app/controllers/job_templates_controller.rb +1 -1
  15. data/app/controllers/remote_execution_features_controller.rb +19 -0
  16. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -22
  17. data/app/helpers/remote_execution_helper.rb +30 -12
  18. data/app/lib/actions/remote_execution/helpers/live_output.rb +2 -2
  19. data/app/lib/actions/remote_execution/run_host_job.rb +7 -4
  20. data/app/lib/actions/remote_execution/run_hosts_job.rb +14 -0
  21. data/app/lib/actions/remote_execution/run_proxy_command.rb +2 -2
  22. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -4
  23. data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +1 -0
  24. data/app/models/input_template_renderer.rb +14 -3
  25. data/app/models/job_invocation.rb +6 -15
  26. data/app/models/job_invocation_composer.rb +131 -21
  27. data/app/models/job_template.rb +77 -22
  28. data/app/models/job_template_effective_user.rb +0 -2
  29. data/app/models/remote_execution_feature.rb +34 -0
  30. data/app/models/setting/remote_execution.rb +4 -1
  31. data/app/models/targeting.rb +2 -2
  32. data/app/models/template_input.rb +17 -13
  33. data/app/views/api/v2/remote_execution_features/base.json.rabl +3 -0
  34. data/app/views/api/v2/remote_execution_features/index.json.rabl +3 -0
  35. data/app/views/api/v2/remote_execution_features/main.json.rabl +3 -0
  36. data/app/views/api/v2/remote_execution_features/show.json.rabl +3 -0
  37. data/app/views/job_invocations/_form.html.erb +51 -40
  38. data/app/views/job_invocations/_preview_hosts_list.html.erb +1 -1
  39. data/app/views/job_invocations/_tab_hosts.html.erb +6 -3
  40. data/app/views/job_invocations/index.html.erb +11 -11
  41. data/app/views/job_templates/_custom_tabs.html.erb +4 -4
  42. data/app/views/job_templates/index.html.erb +5 -5
  43. data/app/views/overrides/nics/_execution_interface.html.erb +9 -1
  44. data/app/views/remote_execution_features/_form.html.erb +24 -0
  45. data/app/views/remote_execution_features/index.html.erb +21 -0
  46. data/app/views/remote_execution_features/show.html.erb +3 -0
  47. data/app/views/template_inputs/_form.html.erb +1 -0
  48. data/app/views/template_inputs/_invocation_form.html.erb +7 -0
  49. data/app/views/templates/package_action.erb +17 -5
  50. data/app/views/templates/power_action.erb +22 -0
  51. data/app/views/templates/puppet_run_once.erb +1 -1
  52. data/config/routes.rb +4 -0
  53. data/db/migrate/20160113162007_expand_all_template_invocations.rb +1 -1
  54. data/db/migrate/20160118124600_create_remote_execution_features.rb +14 -0
  55. data/db/migrate/20160125155108_make_job_template_name_unique.rb +12 -0
  56. data/db/migrate/20160127134031_add_advanced_to_template_input.rb +11 -0
  57. data/db/migrate/20160127162711_reword_puppet_template_description.rb +9 -0
  58. data/db/migrate/20160203104056_add_concurrency_options_to_job_invocation.rb +6 -0
  59. data/db/seeds.d/70-job_templates.rb +2 -1
  60. data/doc/plugins/div_tag.rb +1 -1
  61. data/doc/plugins/plantuml.rb +1 -1
  62. data/doc/plugins/tags.rb +7 -8
  63. data/doc/plugins/toc.rb +0 -1
  64. data/foreman_remote_execution.gemspec +1 -1
  65. data/lib/foreman_remote_execution/engine.rb +10 -2
  66. data/lib/foreman_remote_execution/version.rb +1 -1
  67. data/locale/action_names.rb +8 -0
  68. data/locale/en/foreman_remote_execution.po +767 -11
  69. data/locale/foreman_remote_execution.pot +1026 -8
  70. data/test/functional/api/v2/foreign_input_sets_controller_test.rb +1 -1
  71. data/test/functional/api/v2/job_invocations_controller_test.rb +0 -9
  72. data/test/functional/api/v2/job_templates_controller_test.rb +11 -11
  73. data/test/functional/api/v2/remote_execution_features_controller_test.rb +35 -0
  74. data/test/functional/api/v2/template_inputs_controller_test.rb +1 -1
  75. data/test/unit/actions/run_hosts_job_test.rb +48 -7
  76. data/test/unit/actions/run_proxy_command_test.rb +1 -1
  77. data/test/unit/concerns/host_extensions_test.rb +11 -0
  78. data/test/unit/input_template_renderer_test.rb +4 -4
  79. data/test/unit/job_invocation_composer_test.rb +52 -2
  80. data/test/unit/job_invocation_test.rb +1 -1
  81. data/test/unit/job_template_test.rb +126 -3
  82. data/test/unit/remote_execution_feature_test.rb +42 -0
  83. data/test/unit/targeting_test.rb +4 -4
  84. metadata +26 -7
  85. data/app/views/job_invocation_task_groups/_job_invocation_task_group.html.erb +0 -31
  86. data/app/views/unattended/snippets/_remote_execution_ssh_keys.erb +0 -18
  87. data/db/seeds.d/80-provision_templates.rb +0 -21
@@ -13,13 +13,9 @@ class JobTemplate < ::Template
13
13
  audited :allow_mass_assignment => true
14
14
  has_many :audits, :as => :auditable, :class_name => Audited.audit_class.name
15
15
  has_many :all_template_invocations, :dependent => :destroy, :foreign_key => 'template_id', :class_name => 'TemplateInvocation'
16
- if Rails::VERSION::MAJOR >= 4
17
- has_many :template_invocations, -> { where('host_id IS NOT NULL') }, :foreign_key => 'template_id'
18
- has_many :pattern_template_invocations, -> { where('host_id IS NULL') }, :foreign_key => 'template_id', :class_name => 'TemplateInvocation'
19
- else
20
- has_many :template_invocations, :conditions => 'host_id IS NOT NULL', :foreign_key => 'template_id'
21
- has_many :pattern_template_invocations, :conditions => 'host_id IS NULL', :foreign_key => 'template_id', :class_name => 'TemplateInvocation'
22
- end
16
+ has_many :template_invocations, -> { where('host_id IS NOT NULL') }, :foreign_key => 'template_id'
17
+ has_many :pattern_template_invocations, -> { where('host_id IS NULL') }, :foreign_key => 'template_id', :class_name => 'TemplateInvocation'
18
+ has_many :remote_execution_features, :dependent => :nullify
23
19
 
24
20
  # these can't be shared in parent class, scoped search can't handle STI properly
25
21
  # tested with scoped_search 3.2.0
@@ -41,6 +37,7 @@ class JobTemplate < ::Template
41
37
 
42
38
  validates :job_category, :presence => true, :unless => ->(job_template) { job_template.snippet }
43
39
  validates :provider_type, :presence => true
40
+ validates :name, :uniqueness => true
44
41
  validate :provider_type_whitelist
45
42
  validate :inputs_unchanged_when_locked, :if => ->(template) { (template.locked? || template.locked_changed?) && template.persisted? && !Foreman.in_rake? }
46
43
 
@@ -63,20 +60,31 @@ class JobTemplate < ::Template
63
60
  end
64
61
  self.table_name = 'templates'
65
62
 
66
- # Import a template from ERB, with YAML metadata in the first comment
67
- def self.import(template, options = {})
68
- metadata = parse_metadata(template)
69
- return if metadata.blank? || metadata.delete('kind') != 'job_template' || self.find_by_name(metadata['name'])
70
-
71
- inputs = metadata.delete('template_inputs')
72
-
73
- template = self.create(metadata.merge(:template => template.gsub(/<%\#.+?.-?%>\n?/m, '')).merge(options))
74
- template.assign_taxonomies
75
-
76
- inputs.each do |input|
77
- template.template_inputs << TemplateInput.create(input)
63
+ # Import a template from ERB, with YAML metadata in the first comment. It
64
+ # will overwrite (sync) an existing template if options[:update] is true.
65
+ def self.import(contents, options = {})
66
+ transaction do
67
+ metadata = parse_metadata(contents)
68
+ return if metadata.blank? || metadata.delete('kind') != 'job_template'
69
+
70
+ # Don't update if the template already exists, unless we're told to
71
+ existing = self.find_by_name(metadata['name'])
72
+ return if !options.delete(:update) && existing
73
+
74
+ template = existing || self.new
75
+ template.sync_inputs(metadata.delete('template_inputs'))
76
+ template.sync_foreign_input_sets(metadata.delete('foreign_input_sets'))
77
+ template.assign_attributes(metadata.merge(:template => contents.gsub(/<%\#.+?.-?%>\n?/m, '')).merge(options).except('feature'))
78
+ template.assign_taxonomies if template.new_record?
79
+ template.sync_feature(metadata.delete('feature'))
80
+
81
+ template
78
82
  end
83
+ end
79
84
 
85
+ def self.import!(template, options = {})
86
+ template = import(template, options)
87
+ template.save! if template
80
88
  template
81
89
  end
82
90
 
@@ -113,7 +121,7 @@ class JobTemplate < ::Template
113
121
  if description_format.blank?
114
122
  generated_description = '%{job_category}'
115
123
  unless template_inputs_with_foreign.empty?
116
- inputs = template_inputs_with_foreign.map(&:name).map { |name| %Q(#{name}="%{#{name}}") }.join(' ')
124
+ inputs = template_inputs_with_foreign.map(&:name).map { |name| %{#{name}="%{#{name}}"} }.join(' ')
117
125
  generated_description << " with inputs #{inputs}"
118
126
  end
119
127
  generated_description
@@ -129,13 +137,60 @@ class JobTemplate < ::Template
129
137
  end
130
138
  end
131
139
 
132
- private
140
+ def sync_inputs(inputs)
141
+ inputs ||= []
142
+ # Build a hash where keys are input names
143
+ inputs = inputs.inject({}) { |h, input| h.update(input['name'] => input ) }
144
+
145
+ # Sync existing inputs
146
+ template_inputs.each do |existing_input|
147
+ if inputs.include?(existing_input.name)
148
+ existing_input.assign_attributes(inputs.delete(existing_input.name))
149
+ else
150
+ existing_input.mark_for_destruction
151
+ end
152
+ end
153
+
154
+ # Create new inputs
155
+ inputs.values.each { |new_input| template_inputs.build(new_input) }
156
+ end
157
+
158
+ def sync_foreign_input_sets(input_sets)
159
+ input_sets ||= []
160
+
161
+ input_sets = input_sets.inject({}) do |h, input_set|
162
+ target_template = JobTemplate.find_by!(:name => input_set.delete('template'))
163
+ input_set['target_template_id'] = target_template.id
164
+ h.update(target_template.id => input_set)
165
+ end
166
+
167
+ # Sync existing input sets
168
+ foreign_input_sets.each do |existing_input_set|
169
+ if input_sets.include?(existing_input_set.target_template_id)
170
+ existing_input_set.assign_attributes(input_sets.delete(existing_input_set.target_template_id))
171
+ else
172
+ existing_input_set.mark_for_destruction
173
+ end
174
+ end
175
+
176
+ # Create new input_sets
177
+ input_sets.values.each { |input_set| self.foreign_input_sets.build(input_set) }
178
+ end
179
+
180
+ def sync_feature(feature_name)
181
+ if feature_name && (feature = RemoteExecutionFeature.feature(feature_name))
182
+ feature.job_template ||= self
183
+ feature.save!
184
+ end
185
+ end
133
186
 
134
187
  def self.parse_metadata(template)
135
188
  match = template.match(/<%\#(.+?).-?%>/m)
136
189
  match.nil? ? {} : YAML.load(match[1])
137
190
  end
138
191
 
192
+ private
193
+
139
194
  # we can't use standard validator, .provider_names output can change but the validator does not reflect it
140
195
  def provider_type_whitelist
141
196
  errors.add :provider_type, :uniq unless RemoteExecutionProvider.provider_names.include?(self.provider_type)
@@ -148,4 +203,4 @@ class JobTemplate < ::Template
148
203
  errors.add(:base, _('This template is locked. Please clone it to a new template to customize.'))
149
204
  end
150
205
  end
151
- end
206
+ end
@@ -4,8 +4,6 @@ class JobTemplateEffectiveUser < ActiveRecord::Base
4
4
 
5
5
  before_validation :set_defaults
6
6
 
7
- belongs_to :job_template
8
-
9
7
  def set_defaults
10
8
  self.overridable = true if self.overridable.nil?
11
9
  self.current_user = false if self.current_user.nil?
@@ -0,0 +1,34 @@
1
+ class RemoteExecutionFeature < ActiveRecord::Base
2
+ attr_accessible :label, :name, :provided_input_names, :description, :job_template_id
3
+
4
+ validate :label, :name, :presence => true, :unique => true
5
+
6
+ belongs_to :job_template
7
+
8
+ extend FriendlyId
9
+ friendly_id :label
10
+
11
+ def provided_input_names
12
+ self.provided_inputs.to_s.split(',').map(&:strip)
13
+ end
14
+
15
+ def provided_input_names=(values)
16
+ self.provided_inputs = Array(values).join(',')
17
+ end
18
+
19
+ def self.feature(label)
20
+ self.find_by_label(label) || raise(::Foreman::Exception.new(N_('Unknown remote execution feature %s'), label))
21
+ end
22
+
23
+ def self.register(label, name, options = {})
24
+ return false unless RemoteExecutionFeature.table_exists?
25
+ options.assert_valid_keys(:provided_inputs, :description)
26
+ feature = self.find_by_label(label)
27
+ if feature.nil?
28
+ feature = self.create!(:label => label, :name => name, :provided_input_names => options[:provided_inputs], :description => options[:description])
29
+ else
30
+ feature.update_attributes!(:name => name, :provided_input_names => options[:provided_inputs], :description => options[:description])
31
+ end
32
+ return feature
33
+ end
34
+ end
@@ -22,7 +22,10 @@ class Setting::RemoteExecution < Setting
22
22
  'root'),
23
23
  self.set('remote_execution_effective_user_method',
24
24
  N_('What command should be used to switch to the effective user. One of %s') % SSHExecutionProvider::EFFECTIVE_USER_METHODS.inspect,
25
- 'sudo')
25
+ 'sudo'),
26
+ self.set('remote_execution_sync_templates',
27
+ N_('Whether we should sync templates from disk when running db:seed.'),
28
+ true)
26
29
  ].each { |s| self.create! s.update(:category => 'Setting::RemoteExecution') }
27
30
  end
28
31
 
@@ -51,9 +51,9 @@ class Targeting < ActiveRecord::Base
51
51
  targeting_type == STATIC_TYPE
52
52
  end
53
53
 
54
- def build_query_from_hosts(ids)
54
+ def self.build_query_from_hosts(ids)
55
55
  hosts = Host.where(:id => ids).all.group_by(&:id)
56
- ids.map { |id| "name = #{hosts[id].first.name}" }.join(' or ')
56
+ hosts.map { |id, h| "name = #{h.first.name}" }.join(' or ')
57
57
  end
58
58
 
59
59
  def resolved?
@@ -9,7 +9,7 @@ class TemplateInput < ActiveRecord::Base
9
9
 
10
10
  attr_accessible :name, :required, :input_type, :fact_name, :variable_name,
11
11
  :puppet_class_name, :puppet_parameter_name, :description, :template_id,
12
- :options
12
+ :options, :advanced
13
13
 
14
14
  belongs_to :template
15
15
  has_many :template_invocation_input_values, :dependent => :destroy
@@ -52,21 +52,25 @@ class TemplateInput < ActiveRecord::Base
52
52
  self.options.blank? ? [] : self.options.split(/\r?\n/).map(&:strip)
53
53
  end
54
54
 
55
+ def basic?
56
+ !advanced
57
+ end
58
+
55
59
  private
56
60
 
57
61
  def get_resolver(renderer)
58
62
  resolver_class = case input_type
59
- when 'user'
60
- UserInputResolver
61
- when 'fact'
62
- FactInputResolver
63
- when 'variable'
64
- VariableInputResolver
65
- when 'puppet_parameter'
66
- PuppetParameterInputResolver
67
- else
68
- raise "unknown template input type #{input_type.inspect}"
69
- end
63
+ when 'user'
64
+ UserInputResolver
65
+ when 'fact'
66
+ FactInputResolver
67
+ when 'variable'
68
+ VariableInputResolver
69
+ when 'puppet_parameter'
70
+ PuppetParameterInputResolver
71
+ else
72
+ raise "unknown template input type #{input_type.inspect}"
73
+ end
70
74
  resolver_class.new(self, renderer)
71
75
  end
72
76
 
@@ -139,7 +143,7 @@ class TemplateInput < ActiveRecord::Base
139
143
  private
140
144
 
141
145
  def get_fact
142
- @fact ||= @renderer.host.fact_values.includes(:fact_name).where(:'fact_names.name' => @input.fact_name).first
146
+ @fact ||= @renderer.host.fact_values.includes(:fact_name).find_by(:'fact_names.name' => @input.fact_name)
143
147
  end
144
148
  end
145
149
 
@@ -0,0 +1,3 @@
1
+ object @remote_execution_feature
2
+
3
+ attributes :id, :label, :name, :description, :job_template_id, :job_template_name
@@ -0,0 +1,3 @@
1
+ collection @remote_execution_features
2
+
3
+ extends "api/v2/remote_execution_features/main"
@@ -0,0 +1,3 @@
1
+ object @remote_execution_feature
2
+
3
+ extends "api/v2/remote_execution_features/base"
@@ -0,0 +1,3 @@
1
+ object @remote_execution_feature
2
+
3
+ extends "api/v2/remote_execution_features/main"
@@ -1,6 +1,6 @@
1
1
  <script type="text/javascript">
2
2
  $(function () {
3
- regenerate_description($('.description'))
3
+ template_change($('select.job_template_selector'));
4
4
  });
5
5
  </script>
6
6
 
@@ -38,15 +38,59 @@
38
38
  <div class="col-md-4">
39
39
  <%= @composer.targeted_hosts_count %> <%= _('hosts') %>
40
40
  <%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Refresh"), :id => 'refresh_execution_form') do %>
41
- <%= icon_text('refresh') %>
41
+ <%= icon_text('refresh', '', :kind => 'fa') %>
42
42
  <% end %>
43
43
  <%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Preview"), :id => 'preview_hosts') do %>
44
- <%= icon_text('eye-open') %>
44
+ <%= icon_text('eye', '', :kind => 'fa') %>
45
45
  <% end %>
46
46
  </div>
47
47
  </div>
48
48
 
49
- <div class="form-group ">
49
+ <% @composer.displayed_provider_types.each do |provider_type| %>
50
+ <fieldset id="provider_<%= provider_type %>" class="provider_form">
51
+ <%= f.fields_for 'providers' do |providers_fields| %>
52
+ <%= providers_fields.fields_for provider_type do |provider_type_fields| %>
53
+ <%= provider_type_fields.fields_for :job_templates do |job_templates_fields| %>
54
+ <% @composer.templates_for_provider(provider_type).each do |job_template| %>
55
+ <fieldset id="job_template_<%= job_template.id %>" class="job_template_form <%= 'hidden' if job_template != selected_templates_per_provider[provider_type] %>">
56
+ <%= job_templates_fields.fields_for job_template.id.to_s do |job_template_fields| %>
57
+
58
+ <%= job_template_fields.fields_for :input_values do |input_values_fields| %>
59
+ <% inputs = job_template.template_inputs_with_foreign.select { |input| input.input_type == 'user' } %>
60
+ <% inputs.select { |i| i.basic? }.each do |input| %>
61
+ <%= render :partial => 'template_inputs/invocation_form', :locals => { :input_values_fields => input_values_fields, :composer => @composer, :job_template => job_template, :input => input } %>
62
+ <% end %>
63
+
64
+ <%= advanced_switch_f _('Display advanced fields'), _('Hide advanced fields') %>
65
+
66
+ <div class="advanced hidden">
67
+ <% inputs.select { |i| i.advanced? }.each do |input| %>
68
+ <%= render :partial => 'template_inputs/invocation_form', :locals => { :input_values_fields => input_values_fields, :composer => @composer, :job_template => job_template, :input => input } %>
69
+ <% end %>
70
+ </div>
71
+ <% end %>
72
+
73
+ <div class="advanced hidden">
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.") %>
76
+ <% end %>
77
+ <%= render :partial => 'description_fields', :locals => { :f => f, :job_template => job_template, :disabled => job_template != selected_templates_per_provider[provider_type] } %>
78
+ </div>
79
+ <% end %>
80
+ </fieldset>
81
+ <% end %>
82
+ <% end %>
83
+ <% end %>
84
+ <% end %>
85
+ </fieldset>
86
+ <% end %>
87
+
88
+ <div class="advanced hidden">
89
+ <%= number_f f, :concurrency_level, :label => _('Concurrency level'), :placeholder => 'N', :min => 1, :help_inline => N_("Run at most N tasks at a time") %>
90
+ <%= number_f f, :time_span, :label => _('Time span'), :placeholder => 'N', :min => 1, :help_inline => N_("Distribute execution over N seconds") %>
91
+ </div>
92
+
93
+ <div class="form-group advanced hidden">
50
94
  <label class="col-md-2 control-label"><%= _('Type of query') %></label>
51
95
 
52
96
  <div class="col-md-4">
@@ -55,47 +99,14 @@
55
99
  </div>
56
100
 
57
101
  <span class="help-inline"><%= popover(_('Explanation'),
58
- _("Type has impact on when is the query evaulated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it</li></ul>")) %>
102
+ _("Type has impact on when is the query evaulated to hosts.<br><ul><li><b>Static</b> - evaluates just after you submit this form</li><li><b>Dynamic</b> - evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it</li></ul>")) %>
59
103
  </span>
60
104
  </div>
61
105
  <% end %>
62
106
 
63
- <% @composer.displayed_provider_types.each do |provider_type| %>
64
- <fieldset id="provider_<%= provider_type %>" class="provider_form">
65
- <%= f.fields_for 'providers' do |providers_fields| %>
66
- <%= providers_fields.fields_for provider_type do |provider_type_fields| %>
67
- <%= provider_type_fields.fields_for :job_templates do |job_templates_fields| %>
68
- <% @composer.templates_for_provider(provider_type).each do |job_template| %>
69
- <fieldset id="job_template_<%= job_template.id %>" class="job_template_form <%= 'hidden' if job_template != selected_templates_per_provider[provider_type] %>">
70
- <%= job_templates_fields.fields_for job_template.id.to_s do |job_template_fields| %>
71
-
72
- <%= job_template_fields.fields_for :input_values do |input_values_fields| %>
73
- <% job_template.template_inputs_with_foreign.select { |input| input.input_type == 'user' }.each do |input| %>
74
- <%= input_values_fields.fields_for input.id.to_s, @composer.template_invocation_input_value_for(job_template, input) do |input_fields| %>
75
- <% unless input.options.blank? %>
76
- <%= selectable_f input_fields, :value, input.options_array, {:include_blank => !input.required}, :require => input.required, :label => input.name, :help_inline => input.description, :id => input.name, :onchange => "regenerate_description(this);" %>
77
- <% else %>
78
- <%= textarea_f input_fields, :value, :label => input.name, :help_inline => input.description, :required => input.required, :rows => 2, :onchange => "regenerate_description(this);", :id => input.name %>
79
- <% end %>
80
- <% end %>
81
- <% end %>
82
- <% end %>
83
- <% if job_template.effective_user.overridable? %>
84
- <%= 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.") %>
85
- <% end %>
86
- <%= render :partial => 'description_fields', :locals => { :f => f, :job_template => job_template, :disabled => job_template != selected_templates_per_provider[provider_type] } %>
87
- <% end %>
88
- </fieldset>
89
- <% end %>
90
- <% end %>
91
- <% end %>
92
- <% end %>
93
- </fieldset>
94
- <% end %>
95
-
96
107
  <%= trigger_selector f, @composer.triggering %>
97
- <%= render :partial => 'preview_hosts_modal' %>
98
108
 
109
+ <%= render :partial => 'preview_hosts_modal' %>
99
110
 
100
- <%= submit_or_cancel f %>
111
+ <%= submit_or_cancel f, false, :cancel_path => job_invocations_path %>
101
112
  <% end %>
@@ -11,7 +11,7 @@
11
11
  <% end -%>
12
12
 
13
13
  <% if @additional > 0 -%>
14
- <li><%= link_to(_("...and %{count} more" % {:count => @additional}), hosts_path(:search => @query, :page => 2), :target => '_blank') %></li>
14
+ <li><%= link_to(n_('...and %{count} more', '...and %{count} more', @additional) % {:count => @additional}, hosts_path(:search => @query, :page => 2), :target => '_blank') %></li>
15
15
  <% end -%>
16
16
  </ul>
17
17
  <% else -%>
@@ -6,7 +6,7 @@
6
6
  <%= auto_complete_search(:search, params[:search].try(:squeeze, " "), :placeholder => _("Filter") + ' ...', :path => hosts_path) %>
7
7
  <span class="input-group-btn">
8
8
  <button class="btn btn-default" type="submit">
9
- <%= icon_text("search", content_tag(:span, _("Search"), :class => "hidden-xs")) %>
9
+ <%= icon_text('search', content_tag(:span, _('Search'), :class => 'hidden-xs', :kind => 'fa')) %>
10
10
  </button>
11
11
  </span>
12
12
  </div>
@@ -41,7 +41,10 @@
41
41
  <%= will_paginate_with_info @hosts %>
42
42
  <% else %>
43
43
  <div class="alert alert-warning">
44
- <%= _("The dynamic query '#{job_invocation.targeting.search_query}' was not resolved yet. The list of hosts to which it would resolve now can be seen") %>
45
- <%= link_to('here.', hosts_url(:search => job_invocation.targeting.search_query)) %>
44
+ <%=
45
+ _("The dynamic query '%{query}' was not resolved yet. The list of hosts to which it would resolve now can be seen %{here}." %
46
+ { :query => job_invocation.targeting.search_query,
47
+ :here => link_to(_('here'), hosts_url(:search => job_invocation.targeting.search_query))})
48
+ %>
46
49
  </div>
47
50
  <% end %>
@@ -2,16 +2,16 @@
2
2
 
3
3
  <% title_actions(job_invocations_buttons) %>
4
4
 
5
- <table class="table table-bordered table-striped table-condensed">
5
+ <table class="table table-bordered table-striped table-condensed table-fixed">
6
6
  <thead>
7
7
  <tr>
8
8
  <th><%= sort :description, :as => _('Description') %></th>
9
- <th><%= _('Status') %></th>
10
- <th><%= _('Succeeded') %></th>
11
- <th><%= _('Failed') %></th>
12
- <th><%= _('Pending') %></th>
13
- <th><%= _('Total hosts') %></th>
14
- <th><%= sort :start_at, :as => _('Start') %></th>
9
+ <th class="col-md-1"><%= _('Status') %></th>
10
+ <th class="col-md-1"><%= _('Succeeded') %></th>
11
+ <th class="col-md-1"><%= _('Failed') %></th>
12
+ <th class="col-md-1"><%= _('Pending') %></th>
13
+ <th class="col-md-1"><%= _('Total hosts') %></th>
14
+ <th class="col-md-2"><%= sort :start_at, :as => _('Start') %></th>
15
15
  </tr>
16
16
  </thead>
17
17
 
@@ -20,10 +20,10 @@
20
20
  <tr>
21
21
  <td><%= link_to_if_authorized "#{invocation.description.try(:capitalize) || invocation.job_category}", hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations) %></td>
22
22
  <td><%= link_to_invocation_task_if_authorized(invocation) %></td>
23
- <td><%= invocation_count(invocation, :output_key => :success_count) %></td>
24
- <td><%= invocation_count(invocation, :output_key => :failed_count) %></td>
25
- <td><%= invocation_count(invocation, :output_key => :pending_count) %></td>
26
- <td><%= invocation_count(invocation, :output_key => :total_count) %></td>
23
+ <td><%= invocation_result(invocation, :success_count) %></td>
24
+ <td><%= invocation_result(invocation, :failed_count) %></td>
25
+ <td><%= invocation_result(invocation, :pending_count) %></td>
26
+ <td><%= invocation_result(invocation, :total_count) %></td>
27
27
  <td><%= time_in_words_span(invocation.start_at) %></td>
28
28
  </tr>
29
29
  <% end %>