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