foreman_remote_execution 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/template_invocation.js +48 -5
- data/app/controllers/api/v2/job_invocations_controller.rb +55 -10
- data/app/controllers/api/v2/job_templates_controller.rb +19 -4
- data/app/controllers/api/v2/template_inputs_controller.rb +88 -0
- data/app/controllers/job_invocations_controller.rb +17 -15
- data/app/controllers/template_invocations_controller.rb +2 -0
- data/app/helpers/remote_execution_helper.rb +27 -16
- data/app/lib/actions/middleware/bind_job_invocation.rb +7 -3
- data/app/lib/actions/remote_execution/run_host_job.rb +28 -17
- data/app/lib/actions/remote_execution/run_hosts_job.rb +9 -6
- data/app/models/concerns/foreman_remote_execution/foreman_tasks_task_extensions.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/foreman_tasks_triggering_extensions.rb +9 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +3 -1
- data/app/models/job_invocation.rb +48 -41
- data/app/models/job_invocation_composer.rb +205 -80
- data/app/models/job_invocation_task_group.rb +18 -0
- data/app/models/job_template.rb +25 -1
- data/app/models/job_template_effective_user.rb +23 -0
- data/app/models/remote_execution_provider.rb +25 -11
- data/app/models/setting/remote_execution.rb +6 -0
- data/app/models/ssh_execution_provider.rb +37 -0
- data/app/models/targeting.rb +13 -0
- data/app/models/template_input.rb +4 -1
- data/app/models/template_invocation.rb +23 -0
- data/app/views/api/v2/job_invocations/base.json.rabl +4 -0
- data/app/views/api/v2/job_invocations/index.json.rabl +1 -1
- data/app/views/api/v2/job_invocations/main.json.rabl +19 -0
- data/app/views/api/v2/job_invocations/show.json.rabl +0 -15
- data/app/views/api/v2/job_templates/base.json.rabl +1 -1
- data/app/views/api/v2/job_templates/index.json.rabl +1 -1
- data/app/views/api/v2/job_templates/main.json.rabl +5 -1
- data/app/views/api/v2/job_templates/show.json.rabl +4 -0
- data/app/views/api/v2/job_templates/update.json.rabl +3 -0
- data/app/views/api/v2/template_inputs/base.json.rabl +3 -0
- data/app/views/api/v2/template_inputs/create.json.rabl +3 -0
- data/app/views/api/v2/template_inputs/index.json.rabl +3 -0
- data/app/views/api/v2/template_inputs/main.json.rabl +9 -0
- data/app/views/api/v2/template_inputs/show.json.rabl +3 -0
- data/app/views/job_invocation_task_groups/_job_invocation_task_group.html.erb +31 -0
- data/app/views/job_invocation_task_groups/_job_invocation_task_groups.html.erb +3 -0
- data/app/views/job_invocations/_form.html.erb +102 -71
- data/app/views/job_invocations/_tab_overview.html.erb +5 -2
- data/app/views/job_invocations/index.html.erb +4 -4
- data/app/views/job_invocations/refresh.js.erb +2 -1
- data/app/views/job_invocations/show.html.erb +13 -2
- data/app/views/job_invocations/show.js.erb +1 -1
- data/app/views/job_templates/_custom_tabs.html.erb +16 -0
- data/app/views/templates/package_action.erb +1 -0
- data/app/views/templates/puppet_run_once.erb +1 -0
- data/app/views/templates/run_command.erb +1 -0
- data/app/views/templates/service_action.erb +1 -0
- data/config/routes.rb +15 -2
- data/db/migrate/20150923125825_add_job_invocation_task_group.rb +10 -0
- data/db/migrate/20151022105508_rename_last_task_id_column.rb +6 -0
- data/db/migrate/20151116105412_add_triggering_to_job_invocation.rb +10 -0
- data/db/migrate/20151120171100_add_effective_user_to_template_invocation.rb +5 -0
- data/db/migrate/20151124162300_create_job_template_effective_users.rb +13 -0
- data/db/migrate/20151203100824_add_description_to_job_invocation.rb +11 -0
- data/db/migrate/20151215114631_add_host_id_to_template_invocation.rb +29 -0
- data/db/migrate/20151217092555_migrate_to_task_groups.rb +16 -0
- data/foreman_remote_execution.gemspec +2 -1
- data/lib/foreman_remote_execution/engine.rb +30 -5
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/factories/foreman_remote_execution_factories.rb +5 -0
- data/test/functional/api/v2/job_invocations_controller_test.rb +3 -3
- data/test/functional/api/v2/template_inputs_controller_test.rb +61 -0
- data/test/unit/actions/run_hosts_job_test.rb +10 -3
- data/test/unit/concerns/host_extensions_test.rb +10 -6
- data/test/unit/job_invocation_composer_test.rb +229 -10
- data/test/unit/job_invocation_test.rb +27 -27
- data/test/unit/job_template_effective_user_test.rb +41 -0
- data/test/unit/job_template_test.rb +24 -0
- data/test/unit/remote_execution_provider_test.rb +39 -0
- metadata +42 -7
- data/app/models/job_invocation_api_composer.rb +0 -69
- data/test/unit/job_invocation_api_composer_test.rb +0 -143
@@ -17,6 +17,12 @@ class Setting::RemoteExecution < Setting
|
|
17
17
|
self.set('remote_execution_ssh_user',
|
18
18
|
N_("Default user to use for SSH. You may override per host by setting a parameter called remote_execution_ssh_user."),
|
19
19
|
'root'),
|
20
|
+
self.set('remote_execution_effective_user',
|
21
|
+
N_("Default user to use for executing the script. If the user differs from the SSH user, su or sudo is used to switch the user."),
|
22
|
+
'root'),
|
23
|
+
self.set('remote_execution_effective_user_method',
|
24
|
+
N_("What command should be used to switch to the effective user. One of %s") % SSHExecutionProvider::EFFECTIVE_USER_METHODS.inspect,
|
25
|
+
'sudo')
|
20
26
|
].each { |s| self.create! s.update(:category => "Setting::RemoteExecution") }
|
21
27
|
end
|
22
28
|
|
@@ -1,2 +1,39 @@
|
|
1
1
|
class SSHExecutionProvider < RemoteExecutionProvider
|
2
|
+
|
3
|
+
EFFECTIVE_USER_METHODS = %w[sudo su]
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def proxy_command_options(template_invocation, host)
|
7
|
+
super.merge(:ssh_user => ssh_user(host),
|
8
|
+
:effective_user => effective_user(template_invocation),
|
9
|
+
:effective_user_method => effective_user_method(host))
|
10
|
+
end
|
11
|
+
|
12
|
+
def humanized_name
|
13
|
+
_("Ssh")
|
14
|
+
end
|
15
|
+
|
16
|
+
def supports_effective_user?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def ssh_user(host)
|
23
|
+
host.params['remote_execution_ssh_user']
|
24
|
+
end
|
25
|
+
|
26
|
+
def effective_user(template_invocation)
|
27
|
+
template_invocation.effective_user
|
28
|
+
end
|
29
|
+
|
30
|
+
def effective_user_method(host)
|
31
|
+
method = host.params['remote_execution_effective_user_method'] || Setting[:remote_execution_effective_user_method]
|
32
|
+
unless EFFECTIVE_USER_METHODS.include?(method)
|
33
|
+
raise _('Effective user method "%{current_value}" is not one of %{valid_methods}') %
|
34
|
+
{ :current_value => method, :valid_methods => EFFECTIVE_USER_METHODS}
|
35
|
+
end
|
36
|
+
method
|
37
|
+
end
|
38
|
+
end
|
2
39
|
end
|
data/app/models/targeting.rb
CHANGED
@@ -20,6 +20,19 @@ class Targeting < ActiveRecord::Base
|
|
20
20
|
|
21
21
|
before_create :assign_search_query, :if => :static?
|
22
22
|
|
23
|
+
def clone
|
24
|
+
if static?
|
25
|
+
self.dup
|
26
|
+
else
|
27
|
+
Targeting.new(
|
28
|
+
:user => self.user,
|
29
|
+
:bookmark_id => self.bookmark.try(:id),
|
30
|
+
:targeting_type => self.targeting_type,
|
31
|
+
:search_query => self.search_query
|
32
|
+
)
|
33
|
+
end.tap(&:save)
|
34
|
+
end
|
35
|
+
|
23
36
|
def resolve_hosts!
|
24
37
|
raise ::Foreman::Exception, _('Cannot resolve hosts without a user') if user.nil?
|
25
38
|
raise ::Foreman::Exception, _('Cannot resolve hosts without a bookmark or search query') if bookmark.nil? && search_query.blank?
|
@@ -8,12 +8,15 @@ class TemplateInput < ActiveRecord::Base
|
|
8
8
|
:puppet_parameter => N_('Puppet parameter') }.with_indifferent_access
|
9
9
|
|
10
10
|
attr_accessible :name, :required, :input_type, :fact_name, :variable_name,
|
11
|
-
:puppet_class_name, :puppet_parameter_name, :description, :
|
11
|
+
:puppet_class_name, :puppet_parameter_name, :description, :template_id,
|
12
12
|
:options
|
13
13
|
|
14
14
|
belongs_to :template
|
15
15
|
has_many :template_invocation_input_values, :dependent => :destroy
|
16
16
|
|
17
|
+
scoped_search :on => :name, :complete_value => true
|
18
|
+
scoped_search :on => :input_type, :complete_value => true
|
19
|
+
|
17
20
|
validates :name, :presence => true, :uniqueness => { :scope => 'template_id' }
|
18
21
|
validates :input_type, :presence => true, :inclusion => TemplateInput::TYPES.keys
|
19
22
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
class TemplateInvocation < ActiveRecord::Base
|
2
|
+
include Authorizable
|
2
3
|
include ForemanTasks::Concerns::ActionSubject
|
3
4
|
|
4
5
|
include ForemanRemoteExecution::ErrorsFlattener
|
@@ -11,14 +12,28 @@ class TemplateInvocation < ActiveRecord::Base
|
|
11
12
|
belongs_to :job_invocation, :inverse_of => :template_invocations
|
12
13
|
has_many :input_values, :class_name => 'TemplateInvocationInputValue', :dependent => :destroy
|
13
14
|
has_one :targeting, :through => :job_invocation
|
15
|
+
belongs_to :host, :class_name => 'Host::Managed', :foreign_key => :host_id
|
16
|
+
has_one :host_group, :through => :host, :source => :hostgroup
|
14
17
|
|
15
18
|
validates_associated :input_values
|
16
19
|
validate :provides_required_input_values
|
20
|
+
before_validation :set_effective_user
|
21
|
+
|
22
|
+
scoped_search :in => :host, :on => :name, :rename => 'host.name', :complete_value => true
|
23
|
+
scoped_search :in => :host_group, :on => :name, :rename => 'host_group.name', :complete_value => true
|
24
|
+
scoped_search :in => :template, :on => :job_name, :complete_value => true
|
17
25
|
|
18
26
|
def to_action_input
|
19
27
|
{ :id => id, :name => template.name }
|
20
28
|
end
|
21
29
|
|
30
|
+
def deep_clone
|
31
|
+
self.dup.tap do |invocation|
|
32
|
+
invocation.input_values = self.input_values.map(&:dup)
|
33
|
+
invocation.input_values.each(&:save!)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
22
37
|
private
|
23
38
|
|
24
39
|
def provides_required_input_values
|
@@ -28,4 +43,12 @@ class TemplateInvocation < ActiveRecord::Base
|
|
28
43
|
errors.add(:base, _("Not all required inputs have values. Missing inputs: %s") % missing_inputs.map(&:name).join(', '))
|
29
44
|
end
|
30
45
|
end
|
46
|
+
|
47
|
+
def set_effective_user
|
48
|
+
if template.provider.supports_effective_user?
|
49
|
+
self.effective_user = Setting[:remote_execution_effective_user] if self.effective_user.blank?
|
50
|
+
else
|
51
|
+
self.effective_user = nil
|
52
|
+
end
|
53
|
+
end
|
31
54
|
end
|
@@ -1,3 +1,22 @@
|
|
1
1
|
object @job_invocation
|
2
2
|
|
3
3
|
extends "api/v2/job_invocations/base"
|
4
|
+
|
5
|
+
child :targeting do
|
6
|
+
attributes :bookmark_id, :search_query, :targeting_type, :user_id
|
7
|
+
|
8
|
+
child :hosts do
|
9
|
+
extends "api/v2/hosts/base"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
child :task do
|
14
|
+
attributes :id, :state
|
15
|
+
end
|
16
|
+
|
17
|
+
child :template_invocations do
|
18
|
+
attributes :template_id, :template_name
|
19
|
+
child :input_values do
|
20
|
+
attributes :template_input_name, :template_input_id, :value
|
21
|
+
end
|
22
|
+
end
|
@@ -1,18 +1,3 @@
|
|
1
1
|
object @job_invocation
|
2
2
|
|
3
3
|
extends "api/v2/job_invocations/main"
|
4
|
-
|
5
|
-
child :targeting do
|
6
|
-
attributes :bookmark_id, :search_query, :targeting_type, :user_id
|
7
|
-
end
|
8
|
-
|
9
|
-
child :last_task do
|
10
|
-
attributes :id, :state
|
11
|
-
end
|
12
|
-
|
13
|
-
child :template_invocations do
|
14
|
-
attributes :template_id
|
15
|
-
child :input_values do
|
16
|
-
attributes :template_input_id, :value
|
17
|
-
end
|
18
|
-
end
|
@@ -4,6 +4,10 @@ extends "api/v2/job_templates/main"
|
|
4
4
|
|
5
5
|
attributes :template, :locked
|
6
6
|
|
7
|
+
child :effective_user => :effective_user do
|
8
|
+
attributes :value, :current_user, :overridable
|
9
|
+
end
|
10
|
+
|
7
11
|
node do |job_template|
|
8
12
|
partial("api/v2/taxonomies/children_nodes", :object => job_template)
|
9
13
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<% job_invocation = task_group.job_invocation %>
|
2
|
+
<table class='table table-condensed'>
|
3
|
+
<tr>
|
4
|
+
<th>ID</th>
|
5
|
+
<td><%= link_to(job_invocation.id, job_invocation) %></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th>Job Name</th>
|
9
|
+
<td><%= job_invocation.job_name %></td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Status</th>
|
13
|
+
<td><%= link_to_invocation_task_if_authorized(job_invocation) %></td>
|
14
|
+
</tr>
|
15
|
+
<tr>
|
16
|
+
<th>Succeeded</th>
|
17
|
+
<td><%= invocation_count(job_invocation, :output_key => :success_count) %></td>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<th>Failed</th>
|
21
|
+
<td><%= invocation_count(job_invocation, :output_key => :failed_count) %></td>
|
22
|
+
</tr>
|
23
|
+
<tr>
|
24
|
+
<th>Pending</th>
|
25
|
+
<td><%= invocation_count(job_invocation, :output_key => :pending_count) %></td>
|
26
|
+
</tr>
|
27
|
+
<tr>
|
28
|
+
<th>Total hosts</th>
|
29
|
+
<td><%= invocation_count(job_invocation, :output_key => :total_count) %></td>
|
30
|
+
</tr>
|
31
|
+
</table>
|
@@ -1,88 +1,119 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
<script type="text/javascript">
|
2
|
+
$(function () {
|
3
|
+
regenerate_description($('#description'), true)
|
4
|
+
});
|
5
|
+
</script>
|
3
6
|
|
4
|
-
|
5
|
-
<span id="bookmark_query_map" >
|
6
|
-
<% @composer.available_bookmarks.each do |bookmark| %>
|
7
|
-
<span id="bookmark-<%= bookmark.id %>" data-query="<%= bookmark.query %>"></span>
|
8
|
-
<% end %>
|
9
|
-
</span>
|
10
|
-
<%= selectable_f targeting_fields, :bookmark_id, @composer.available_bookmarks.map {|b| [ b.name, b.id ] }, :selected => @composer.targeting.bookmark_id, :include_blank => true %>
|
11
|
-
<%= textarea_f targeting_fields, :search_query, :value => @composer.displayed_search_query, :rows => 5 %>
|
7
|
+
<%= form_for @composer.job_invocation, :html => {'data-refresh-url' => refresh_job_invocations_path, :id => 'job_invocation_form'} do |f| %>
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
<%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Refresh"), :id => 'refresh_execution_form') do %>
|
18
|
-
<%= icon_text('refresh') %>
|
19
|
-
<% end %>
|
20
|
-
<%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Preview"), :id => 'preview_hosts') do %>
|
21
|
-
<%= icon_text('eye-open') %>
|
22
|
-
<% end %>
|
23
|
-
</div>
|
24
|
-
</div>
|
9
|
+
<ul class="nav nav-tabs" data-tabs="tabs">
|
10
|
+
<li class="active"><a href="#primary" data-toggle="tab"><%= _('Job') %></a></li>
|
11
|
+
<li><a href="#scheduling" data-toggle="tab">Scheduling</a></li>
|
12
|
+
</ul>
|
25
13
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
14
|
+
<div class="tab-content">
|
15
|
+
<div class="tab-pane active" id="primary">
|
16
|
+
<%= selectable_f f, :job_name, @composer.available_job_names %>
|
17
|
+
|
18
|
+
<%= fields_for @composer.targeting do |targeting_fields| %>
|
19
|
+
<span id="bookmark_query_map">
|
20
|
+
<% @composer.available_bookmarks.each do |bookmark| %>
|
21
|
+
<span id="bookmark-<%= bookmark.id %>" data-query="<%= bookmark.query %>"></span>
|
22
|
+
<% end %>
|
23
|
+
</span>
|
24
|
+
<%= selectable_f targeting_fields, :bookmark_id, @composer.available_bookmarks.map { |b| [b.name, b.id] }, :selected => @composer.targeting.bookmark_id, :include_blank => true %>
|
25
|
+
<%= textarea_f targeting_fields, :search_query, :value => @composer.displayed_search_query, :rows => 5 %>
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
27
|
+
<div class="form-group ">
|
28
|
+
<label class="col-md-2 control-label"><%= _('Resolves to') %></label>
|
29
|
+
|
30
|
+
<div class="col-md-4">
|
31
|
+
<%= @composer.targeted_hosts_count %> <%= _('hosts') %>
|
32
|
+
<%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Refresh"), :id => 'refresh_execution_form') do %>
|
33
|
+
<%= icon_text('refresh') %>
|
34
|
+
<% end %>
|
35
|
+
<%= button_tag(:type => 'button', :class => 'btn btn-default btn-sm', :title => _("Preview"), :id => 'preview_hosts') do %>
|
36
|
+
<%= icon_text('eye-open') %>
|
47
37
|
<% end %>
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div class="form-group ">
|
42
|
+
<label class="col-md-2 control-label"><%= _('Type of query') %></label>
|
43
|
+
|
44
|
+
<div class="col-md-4">
|
45
|
+
<%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::STATIC_TYPE, :text => _(Targeting::TYPES[Targeting::STATIC_TYPE]) %>
|
46
|
+
<%= radio_button_f targeting_fields, :targeting_type, :value => Targeting::DYNAMIC_TYPE, :text => _(Targeting::TYPES[Targeting::DYNAMIC_TYPE]) %>
|
47
|
+
</div>
|
48
|
+
<span class="help-inline"><%= popover(_('Explanation'),
|
49
|
+
_("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>")) %></span>
|
50
|
+
</div>
|
51
|
+
<% end %>
|
54
52
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
<% @composer.displayed_provider_types.each do |provider_type| %>
|
54
|
+
<fieldset id="provider_<%= provider_type %>" class="provider_form">
|
55
|
+
<legend><%= _('Provider') + ': ' + provider_type %></legend>
|
56
|
+
<%= f.fields_for 'providers' do |providers_fields| %>
|
57
|
+
<%= providers_fields.fields_for provider_type do |provider_type_fields| %>
|
58
|
+
<%= provider_type_fields.fields_for :job_templates do |job_templates_fields| %>
|
59
|
+
<% if @composer.needs_provider_type_selection? %>
|
60
|
+
<%= radio_button_f provider_type_fields, 'job_template_id', :value => '', :text => _('Disable this provider'),
|
61
|
+
:class => 'job_template_selector', :checked => @composer.preselect_disabled_for_provider(provider_type) %>
|
62
|
+
<span/>
|
63
|
+
<% end %>
|
64
|
+
<% @composer.templates_for_provider(provider_type).each do |job_template| %>
|
65
|
+
<%= radio_button_f provider_type_fields, 'job_template_id',
|
66
|
+
:value => job_template.id,
|
67
|
+
:text => job_template.name,
|
68
|
+
:onchange => "regenerate_description($(this).parent().next('fieldset'));",
|
69
|
+
:class => 'job_template_selector',
|
70
|
+
:checked => @composer.job_template_ids.include?(job_template.id) || @composer.only_one_template_available? %>
|
71
|
+
|
72
|
+
<fieldset id="job_template_<%= job_template.id %>" class="job_template_form <%= 'hidden' if !@composer.job_template_ids.include?(job_template.id) && !@composer.only_one_template_available? %>">
|
73
|
+
<%= job_templates_fields.fields_for job_template.id.to_s do |job_template_fields| %>
|
74
|
+
|
75
|
+
<%= job_template_fields.fields_for :input_values do |input_values_fields| %>
|
76
|
+
<% job_template.template_inputs.where(:input_type => 'user').each do |input| %>
|
77
|
+
<%= input_values_fields.fields_for input.id.to_s, @composer.template_invocation_input_value_for(input) do |input_fields| %>
|
78
|
+
<% unless input.options.blank? %>
|
79
|
+
<%= 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);" %>
|
80
|
+
<% else %>
|
81
|
+
<%= textarea_f input_fields, :value, :label => input.name, :help_inline => input.description, :required => input.required, :rows => 2, :onchange => "regenerate_description(this);", :id => input.name %>
|
82
|
+
<% end %>
|
83
|
+
<% end %>
|
64
84
|
<% end %>
|
65
85
|
<% end %>
|
86
|
+
<% if job_template.effective_user.overridable? %>
|
87
|
+
<%= 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.") %>
|
88
|
+
<% end %>
|
89
|
+
|
66
90
|
<% end %>
|
67
|
-
|
91
|
+
<%= textarea_f f, :description, :label => _("Description"), :readonly => "true", :id => "description", :rows => 2, :help_inline => content_tag(:span, check_box_tag('description_format_override', job_template.generate_description_format, true, :onchange => "description_override(this);") + ' ' + _("Use default description template")) %>
|
92
|
+
<div id="description_format_container" class="hidden">
|
93
|
+
<%= textarea_f f, :description_format,
|
94
|
+
:label => _("Description template"),
|
95
|
+
:value => job_template.generate_description_format,
|
96
|
+
:rows => 2,
|
97
|
+
:onchange => "regenerate_description(this);",
|
98
|
+
:id => "description_format",
|
99
|
+
:help_inline => popover(_('Explanation'), _('Description template determines the job name once it is submitted. Input values can become part of the name
|
100
|
+
if they are specified using interpolation syntax, e.g. %{fqdn} where fqdn is the name of interpolated input.')) %>
|
101
|
+
</div>
|
102
|
+
</fieldset>
|
68
103
|
<% end %>
|
69
|
-
|
104
|
+
<% end %>
|
70
105
|
<% end %>
|
71
106
|
<% end %>
|
72
|
-
|
107
|
+
</fieldset>
|
73
108
|
<% end %>
|
74
|
-
</fieldset>
|
75
|
-
<% end %>
|
76
109
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
<%= text_f f, :start_before, :label => _('Start before') %>
|
84
|
-
</fieldset>
|
110
|
+
<%= render :partial => 'preview_hosts_modal' %>
|
111
|
+
</div>
|
112
|
+
|
113
|
+
<div class="tab-pane" id="scheduling">
|
114
|
+
<%= trigger_selector f, @composer.triggering %>
|
115
|
+
</div>
|
85
116
|
</div>
|
86
|
-
|
117
|
+
|
87
118
|
<%= submit_or_cancel f %>
|
88
119
|
<% end %>
|
@@ -15,7 +15,7 @@
|
|
15
15
|
<%= _('Evaluated at:') %> <%= job_invocation.targeting.resolved_at %><br>
|
16
16
|
<% if job_invocation.template_invocations.size > 1 %>
|
17
17
|
<% job_invocation.template_invocations.each do |template_invocation| %>
|
18
|
-
<%= host_counter
|
18
|
+
<%= host_counter template_invocation.template.provider.humanized_name, ForemanTasks::Task::DynflowTask.for_action(Actions::RemoteExecution::RunHostJob).for_resource(template_invocation).uniq.size %>
|
19
19
|
<% end %>
|
20
20
|
<% end %>
|
21
21
|
<%= host_counter(_('Total hosts'), job_invocation.total_hosts_count) %>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
<h4><%= _('Providers and templates') %></h4>
|
26
26
|
<% job_invocation.template_invocations.each do |template_invocation| %>
|
27
27
|
<h5>
|
28
|
-
<b><%= template_invocation.template.name %></b> <%= 'through' %> <%=
|
28
|
+
<b><%= template_invocation.template.name %></b> <%= 'through' %> <%= template_invocation.template.provider.humanized_name %>
|
29
29
|
</h5>
|
30
30
|
<% target = template_invocation.targeting.hosts.with_os.first || template_invocation.targeting.hosts.first %>
|
31
31
|
<%= _('Preview for target %s') % target.try(:name) || 'N/A' %>
|
@@ -40,5 +40,8 @@
|
|
40
40
|
<% end %>
|
41
41
|
</ul>
|
42
42
|
<% end %>
|
43
|
+
<% if template_invocation.effective_user %>
|
44
|
+
<b><%= _("Effective user") %></b>: <%= template_invocation.effective_user %>
|
45
|
+
<% end %>
|
43
46
|
<% end %>
|
44
47
|
</div>
|
@@ -3,28 +3,28 @@
|
|
3
3
|
<table class="table table-bordered table-striped table-condensed">
|
4
4
|
<thead>
|
5
5
|
<tr>
|
6
|
-
<th><%= _('Job name') %></th>
|
6
|
+
<th><%= sort :job_name, :as => _('Job name') %></th>
|
7
7
|
<th><%= _('Status') %></th>
|
8
8
|
<th><%= _('Succeeded') %></th>
|
9
9
|
<th><%= _('Failed') %></th>
|
10
10
|
<th><%= _('Pending') %></th>
|
11
11
|
<th><%= _('Total hosts') %></th>
|
12
|
+
<th><%= sort :started_at, :as => _('Started') %></th>
|
12
13
|
</tr>
|
13
14
|
</thead>
|
14
15
|
|
15
16
|
<tbody>
|
16
17
|
<% @job_invocations.each do |invocation| %>
|
17
18
|
<tr>
|
18
|
-
<td><%= link_to_if_authorized invocation.job_name, hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocation) %></td>
|
19
|
+
<td><%= link_to_if_authorized "#{invocation.description.try(:capitalize) || invocation.job_name}", hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocation) %></td>
|
19
20
|
<td><%= link_to_invocation_task_if_authorized(invocation) %></td>
|
20
21
|
<td><%= invocation_count(invocation, :output_key => :success_count) %></td>
|
21
22
|
<td><%= invocation_count(invocation, :output_key => :failed_count) %></td>
|
22
23
|
<td><%= invocation_count(invocation, :output_key => :pending_count) %></td>
|
23
24
|
<td><%= invocation_count(invocation, :output_key => :total_count) %></td>
|
25
|
+
<td><%= time_ago(invocation.task.try(:started_at)) %></td>
|
24
26
|
</tr>
|
25
27
|
<% end %>
|
26
28
|
</tbody>
|
27
|
-
|
28
29
|
</table>
|
29
|
-
|
30
30
|
<%= will_paginate_with_info @job_invocations %>
|
@@ -1 +1,2 @@
|
|
1
|
-
$('form#job_invocation_form').
|
1
|
+
$('form#job_invocation_form').replaceWith("<%=j render :partial => 'form' %>");
|
2
|
+
$('#job_invocation_form').find('a[rel="popover"]').popover();
|
@@ -2,13 +2,14 @@
|
|
2
2
|
<% stylesheet 'job_invocations' %>
|
3
3
|
<% javascript 'template_invocation' %>
|
4
4
|
|
5
|
-
<% if @job_invocation.
|
6
|
-
<% title_actions(button_group(job_invocation_task_buttons(@job_invocation.
|
5
|
+
<% if @job_invocation.task %>
|
6
|
+
<% title_actions(button_group(job_invocation_task_buttons(@job_invocation.task))) %>
|
7
7
|
<% end %>
|
8
8
|
|
9
9
|
<ul class="nav nav-tabs" data-tabs="tabs">
|
10
10
|
<li class="<%= job_invocation_active_tab(:overview, params) %>"><a href="#primary" data-toggle="tab"><%= _('Overview') %></a></li>
|
11
11
|
<li class="<%= job_invocation_active_tab(:hosts, params) %>"><a href="#hosts" data-toggle="tab"><%= _('Hosts') %></a></li>
|
12
|
+
<% unless @job_invocation.recurring_logic.nil? %><li><a href="#recurring_logic" data-toggle="tab"><%= _('Recurring logic') %></a></li><% end %>
|
12
13
|
</ul>
|
13
14
|
|
14
15
|
<div class="tab-content">
|
@@ -19,6 +20,16 @@
|
|
19
20
|
<div class="tab-pane <%= job_invocation_active_tab(:hosts, params) %>" id="hosts" data-refresh_required="<%= @job_invocation.resolved? ? '' : 'true' %>">
|
20
21
|
<%= render 'tab_hosts', :job_invocation => @job_invocation, :hosts => @hosts %>
|
21
22
|
</div>
|
23
|
+
|
24
|
+
<% unless @job_invocation.recurring_logic.nil? %>
|
25
|
+
<div class="tab-pane" id="recurring_logic">
|
26
|
+
<div class='col-md-6'>
|
27
|
+
<% if @job_invocation.recurring_logic.try(:task_group) %>
|
28
|
+
<%= render @job_invocation.recurring_logic.task_group, :task_group => @job_invocation.recurring_logic.try(:task_group) %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
<% end %>
|
22
33
|
</div>
|
23
34
|
|
24
35
|
<script id="job_invocation_refresh" data-refresh-url="<%= job_invocation_path(@job_invocation) %>">
|
@@ -1,4 +1,4 @@
|
|
1
|
-
$('div.btn-group').html('<%= button_group(job_invocation_task_buttons(@job_invocation.
|
1
|
+
$('div.btn-group').html('<%= button_group(job_invocation_task_buttons(@job_invocation.task)).html_safe %>');
|
2
2
|
$('div#status_chart').html('<%=j job_invocation_chart(@job_invocation) %>');
|
3
3
|
$('div#status').flot_pie();
|
4
4
|
|