foreman_remote_execution 0.2.3 → 0.3.0

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