foreman_remote_execution 0.1.1 → 0.1.2
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/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
|
|