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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +16 -5
- data/.tx/config +1 -1
- data/app/assets/javascripts/template_invocation.js +14 -1
- data/app/assets/stylesheets/job_invocations.css.scss +0 -13
- data/app/assets/stylesheets/template_invocation.css.scss +7 -13
- data/app/controllers/api/v2/foreign_input_sets_controller.rb +1 -1
- data/app/controllers/api/v2/job_invocations_controller.rb +12 -18
- data/app/controllers/api/v2/remote_execution_features_controller.rb +38 -0
- data/app/controllers/api/v2/template_inputs_controller.rb +1 -0
- data/app/controllers/job_invocations_controller.rb +3 -13
- data/app/controllers/job_templates_controller.rb +1 -1
- data/app/controllers/remote_execution_features_controller.rb +19 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -22
- data/app/helpers/remote_execution_helper.rb +30 -12
- data/app/lib/actions/remote_execution/helpers/live_output.rb +2 -2
- data/app/lib/actions/remote_execution/run_host_job.rb +7 -4
- data/app/lib/actions/remote_execution/run_hosts_job.rb +14 -0
- data/app/lib/actions/remote_execution/run_proxy_command.rb +2 -2
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -4
- data/app/models/concerns/foreman_remote_execution/subnet_extensions.rb +1 -0
- data/app/models/input_template_renderer.rb +14 -3
- data/app/models/job_invocation.rb +6 -15
- data/app/models/job_invocation_composer.rb +131 -21
- data/app/models/job_template.rb +77 -22
- data/app/models/job_template_effective_user.rb +0 -2
- data/app/models/remote_execution_feature.rb +34 -0
- data/app/models/setting/remote_execution.rb +4 -1
- data/app/models/targeting.rb +2 -2
- data/app/models/template_input.rb +17 -13
- data/app/views/api/v2/remote_execution_features/base.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/index.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/main.json.rabl +3 -0
- data/app/views/api/v2/remote_execution_features/show.json.rabl +3 -0
- data/app/views/job_invocations/_form.html.erb +51 -40
- data/app/views/job_invocations/_preview_hosts_list.html.erb +1 -1
- data/app/views/job_invocations/_tab_hosts.html.erb +6 -3
- data/app/views/job_invocations/index.html.erb +11 -11
- data/app/views/job_templates/_custom_tabs.html.erb +4 -4
- data/app/views/job_templates/index.html.erb +5 -5
- data/app/views/overrides/nics/_execution_interface.html.erb +9 -1
- data/app/views/remote_execution_features/_form.html.erb +24 -0
- data/app/views/remote_execution_features/index.html.erb +21 -0
- data/app/views/remote_execution_features/show.html.erb +3 -0
- data/app/views/template_inputs/_form.html.erb +1 -0
- data/app/views/template_inputs/_invocation_form.html.erb +7 -0
- data/app/views/templates/package_action.erb +17 -5
- data/app/views/templates/power_action.erb +22 -0
- data/app/views/templates/puppet_run_once.erb +1 -1
- data/config/routes.rb +4 -0
- data/db/migrate/20160113162007_expand_all_template_invocations.rb +1 -1
- data/db/migrate/20160118124600_create_remote_execution_features.rb +14 -0
- data/db/migrate/20160125155108_make_job_template_name_unique.rb +12 -0
- data/db/migrate/20160127134031_add_advanced_to_template_input.rb +11 -0
- data/db/migrate/20160127162711_reword_puppet_template_description.rb +9 -0
- data/db/migrate/20160203104056_add_concurrency_options_to_job_invocation.rb +6 -0
- data/db/seeds.d/70-job_templates.rb +2 -1
- data/doc/plugins/div_tag.rb +1 -1
- data/doc/plugins/plantuml.rb +1 -1
- data/doc/plugins/tags.rb +7 -8
- data/doc/plugins/toc.rb +0 -1
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +10 -2
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/action_names.rb +8 -0
- data/locale/en/foreman_remote_execution.po +767 -11
- data/locale/foreman_remote_execution.pot +1026 -8
- data/test/functional/api/v2/foreign_input_sets_controller_test.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +0 -9
- data/test/functional/api/v2/job_templates_controller_test.rb +11 -11
- data/test/functional/api/v2/remote_execution_features_controller_test.rb +35 -0
- data/test/functional/api/v2/template_inputs_controller_test.rb +1 -1
- data/test/unit/actions/run_hosts_job_test.rb +48 -7
- data/test/unit/actions/run_proxy_command_test.rb +1 -1
- data/test/unit/concerns/host_extensions_test.rb +11 -0
- data/test/unit/input_template_renderer_test.rb +4 -4
- data/test/unit/job_invocation_composer_test.rb +52 -2
- data/test/unit/job_invocation_test.rb +1 -1
- data/test/unit/job_template_test.rb +126 -3
- data/test/unit/remote_execution_feature_test.rb +42 -0
- data/test/unit/targeting_test.rb +4 -4
- metadata +26 -7
- data/app/views/job_invocation_task_groups/_job_invocation_task_group.html.erb +0 -31
- data/app/views/unattended/snippets/_remote_execution_ssh_keys.erb +0 -18
- data/db/seeds.d/80-provision_templates.rb +0 -21
data/app/models/job_template.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
template
|
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| %
|
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
|
-
|
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
|
@@ -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
|
|
data/app/models/targeting.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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).
|
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
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<script type="text/javascript">
|
2
2
|
$(function () {
|
3
|
-
|
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
|
44
|
+
<%= icon_text('eye', '', :kind => 'fa') %>
|
45
45
|
<% end %>
|
46
46
|
</div>
|
47
47
|
</div>
|
48
48
|
|
49
|
-
|
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
|
-
|
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(
|
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(
|
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
|
-
<%=
|
45
|
-
|
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><%=
|
24
|
-
<td><%=
|
25
|
-
<td><%=
|
26
|
-
<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 %>
|