foreman_remote_execution 3.3.4 → 4.1.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/.github/workflows/ci.yml +1 -1
- data/app/controllers/api/v2/job_invocations_controller.rb +1 -0
- data/app/controllers/foreman_remote_execution/concerns/api/v2/subnets_controller_extensions.rb +21 -0
- data/app/controllers/job_invocations_controller.rb +22 -8
- data/app/helpers/job_invocations_helper.rb +3 -2
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +4 -3
- data/app/lib/foreman_remote_execution/renderer/scope/input.rb +35 -0
- data/app/models/concerns/api/v2/interfaces_controller_extensions.rb +13 -0
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +11 -4
- data/app/models/job_invocation.rb +11 -4
- data/app/models/job_invocation_composer.rb +2 -2
- data/app/models/remote_execution_provider.rb +2 -2
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/ssh_execution_provider.rb +1 -1
- data/app/views/api/v2/interfaces/execution_flag.json.rabl +1 -0
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +1 -1
- data/app/views/api/v2/subnets/remote_execution_proxies.json.rabl +3 -0
- data/app/views/job_invocations/_form.html.erb +1 -1
- data/app/views/job_invocations/_tab_hosts.html.erb +1 -20
- data/app/views/job_invocations/_tab_overview.html.erb +13 -1
- data/app/views/job_invocations/show.html.erb +9 -0
- data/app/views/job_invocations/show.js.erb +5 -0
- data/app/views/job_invocations/show.json.erb +2 -1
- data/db/migrate/20200623073022_rename_sudo_password_to_effective_user_password.rb +34 -0
- data/db/seeds.d/20-permissions.rb +9 -0
- data/lib/foreman_remote_execution/engine.rb +19 -1
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +65 -2
- data/test/functional/job_invocations_controller_test.rb +71 -0
- data/test/models/orchestration/ssh_test.rb +1 -1
- data/test/support/remote_execution_helper.rb +5 -0
- data/test/unit/actions/run_host_job_test.rb +3 -3
- data/test/unit/actions/run_hosts_job_test.rb +2 -2
- data/test/unit/job_invocation_composer_test.rb +5 -5
- data/test/unit/remote_execution_provider_test.rb +6 -6
- data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +2 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +2 -0
- data/webpack/__mocks__/foremanReact/constants.js +21 -0
- data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +2 -0
- data/webpack/__mocks__/foremanReact/redux/middlewares/IntervalMiddleware/IntervalSelectors.js +1 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHosts.js +21 -15
- data/webpack/react_app/components/TargetingHosts/TargetingHostsHelpers.js +10 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +62 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +6 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsSelectors.js +10 -2
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsPage.test.js +9 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +26 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHosts.test.js.snap +16 -1
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +68 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +11 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/fixtures.js +35 -19
- data/webpack/react_app/components/TargetingHosts/index.js +73 -13
- metadata +22 -3
- data/webpack/react_app/components/TargetingHosts/TargetingHostsActions.js +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ed74f64c21956170e95cbab53cd7580205f0620166573980c12efe06e90b960
|
4
|
+
data.tar.gz: 6ca59984908170bc003eb797883bb37c53c5a01a9f5a7bb5a95db2c8242b1fb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0588d33348d69b3ecae2b0c50c143f564a8f32fbff548e31b7da2a8b055b2e400784136f4eb98fc4d2d26740d907ced7cb27522856c6fcbf8736cf94fc5e2a93'
|
7
|
+
data.tar.gz: 1069f8025c92c09c3e9c780ae8b5040e8dd15c5a282d76a9195569226b18c0801e5dc533635c804d4f3e377bebcd48501de57abd9141040c6b46b13e7496415b
|
data/.github/workflows/ci.yml
CHANGED
data/app/controllers/foreman_remote_execution/concerns/api/v2/subnets_controller_extensions.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module ForemanRemoteExecution
|
2
|
+
module Concerns
|
3
|
+
module Api::V2::SubnetsControllerExtensions
|
4
|
+
module ApiPieExtensions
|
5
|
+
extend ::Apipie::DSL::Concern
|
6
|
+
|
7
|
+
update_api(:create, :update) do
|
8
|
+
param :subnet, Hash do
|
9
|
+
param :remote_execution_proxy_ids, Array, _('List of proxy IDs to be used for remote execution')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
included do
|
17
|
+
include ApiPieExtensions
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -52,16 +52,18 @@ class JobInvocationsController < ApplicationController
|
|
52
52
|
|
53
53
|
def show
|
54
54
|
@job_invocation = resource_base.includes(:template_invocations => :run_host_job_task).find(params[:id])
|
55
|
-
@auto_refresh = @job_invocation.task.try(:pending?)
|
56
|
-
@resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
57
|
-
# There's no need to do the joining if we're not filtering
|
58
|
-
unless params[:search].nil?
|
59
|
-
@resource_base = @resource_base.joins(:template_invocations)
|
60
|
-
.where(:template_invocations => { :job_invocation_id => @job_invocation.id})
|
61
|
-
end
|
62
|
-
@hosts = resource_base_search_and_page
|
63
55
|
@job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
|
64
56
|
@job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
|
57
|
+
@auto_refresh = @job_invocation.task.try(:pending?)
|
58
|
+
|
59
|
+
respond_to do |format|
|
60
|
+
format.json do
|
61
|
+
targeting_hosts_resources
|
62
|
+
end
|
63
|
+
|
64
|
+
format.html
|
65
|
+
format.js
|
66
|
+
end
|
65
67
|
end
|
66
68
|
|
67
69
|
def index
|
@@ -153,4 +155,16 @@ class JobInvocationsController < ApplicationController
|
|
153
155
|
JobInvocationComposer.from_ui_params(with_triggering)
|
154
156
|
end
|
155
157
|
end
|
158
|
+
|
159
|
+
def targeting_hosts_resources
|
160
|
+
@auto_refresh = @job_invocation.task.try(:pending?)
|
161
|
+
@resource_base = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
162
|
+
|
163
|
+
unless params[:search].nil?
|
164
|
+
@resource_base = @resource_base.joins(:template_invocations)
|
165
|
+
.where(:template_invocations => { :job_invocation_id => @job_invocation.id})
|
166
|
+
end
|
167
|
+
@hosts = resource_base_search_and_page
|
168
|
+
@total_hosts = resource_base_with_search.size
|
169
|
+
end
|
156
170
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal:true
|
2
2
|
|
3
3
|
module JobInvocationsHelper
|
4
|
-
def minicard(icon, number, text)
|
4
|
+
def minicard(icon, number, text, tooltip: nil)
|
5
|
+
tooltip_options = tooltip ? { :'data-original-title' => tooltip, :rel => 'twipsy' } : {}
|
5
6
|
content_tag(:div, :class => 'card-pf card-pf-accented
|
6
7
|
card-pf-aggregate-status card-pf-aggregate-status-mini') do
|
7
|
-
content_tag(:h2, :class => 'card-pf-title', :style => 'line-height: 1.1') do
|
8
|
+
content_tag(:h2, { :class => 'card-pf-title', :style => 'line-height: 1.1' }.merge(tooltip_options)) do
|
8
9
|
icon_text(icon, '', :kind => 'pficon') +
|
9
10
|
content_tag(:span, number, :class =>'card-pf-aggregate-status-count') +
|
10
11
|
text
|
@@ -60,7 +60,7 @@ module Actions
|
|
60
60
|
def secrets(host, job_invocation, provider)
|
61
61
|
job_secrets = { :ssh_password => job_invocation.password,
|
62
62
|
:key_passphrase => job_invocation.key_passphrase,
|
63
|
-
:
|
63
|
+
:effective_user_password => job_invocation.effective_user_password }
|
64
64
|
|
65
65
|
job_secrets.merge(provider.secrets(host)) { |_key, job_secret, provider_secret| job_secret || provider_secret }
|
66
66
|
end
|
@@ -47,7 +47,7 @@ module Actions
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def finalize
|
50
|
-
job_invocation.password = job_invocation.key_passphrase = job_invocation.
|
50
|
+
job_invocation.password = job_invocation.key_passphrase = job_invocation.effective_user_password = nil
|
51
51
|
job_invocation.save!
|
52
52
|
|
53
53
|
Rails.logger.debug "cleaning cache for keys that begin with 'job_invocation_#{job_invocation.id}'"
|
@@ -57,7 +57,8 @@ module Actions
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def job_invocation
|
60
|
-
|
60
|
+
id = input[:job_invocation_id] || input.fetch(:job_invocation, {})[:id]
|
61
|
+
@job_invocation ||= JobInvocation.find(id)
|
61
62
|
end
|
62
63
|
|
63
64
|
def batch(from, size)
|
@@ -65,7 +66,7 @@ module Actions
|
|
65
66
|
end
|
66
67
|
|
67
68
|
def total_count
|
68
|
-
hosts.count
|
69
|
+
output[:total_count] || hosts.count
|
69
70
|
end
|
70
71
|
|
71
72
|
def hosts
|
@@ -3,14 +3,33 @@ module ForemanRemoteExecution
|
|
3
3
|
module Scope
|
4
4
|
class Input < ::Foreman::Renderer::Scope::Template
|
5
5
|
include Foreman::Renderer::Scope::Macros::HostTemplate
|
6
|
+
extend ApipieDSL::Class
|
6
7
|
|
7
8
|
attr_reader :template, :host, :invocation, :input_template_instance, :current_user
|
8
9
|
delegate :input, to: :input_template_instance
|
9
10
|
|
11
|
+
apipie :class, 'Macros related to template rendering' do
|
12
|
+
name 'Template Input Render'
|
13
|
+
sections only: %w[all jobs]
|
14
|
+
end
|
15
|
+
|
16
|
+
apipie :method, 'Always raises an error with a description provided as an argument' do
|
17
|
+
desc 'This method is useful for aborting script execution if some of the conditions are not met'
|
18
|
+
required :message, String, desc: 'Description for the error'
|
19
|
+
raises error: ::InputTemplateRenderer::RenderError, desc: 'The error is always being raised'
|
20
|
+
returns nil, desc: "Doesn't return anything"
|
21
|
+
example "<%
|
22
|
+
@host.operatingsystem #=> nil
|
23
|
+
render_error(N_('Unsupported or no operating system found for this host.')) unless @host.operatingsystem #=> InputTemplateRenderer::RenderError is raised and the execution of the script is aborted
|
24
|
+
%>"
|
25
|
+
end
|
10
26
|
def render_error(message)
|
11
27
|
raise ::InputTemplateRenderer::RenderError.new(message)
|
12
28
|
end
|
13
29
|
|
30
|
+
apipie :method, 'Check whether the template in preview mode or not' do
|
31
|
+
returns one_of: [true, false], desc: 'Returns true if the template in preview mode, false otherwise'
|
32
|
+
end
|
14
33
|
def preview?
|
15
34
|
!!@preview
|
16
35
|
end
|
@@ -23,6 +42,16 @@ module ForemanRemoteExecution
|
|
23
42
|
Rails.cache.fetch(cache_key, &block)
|
24
43
|
end
|
25
44
|
|
45
|
+
# rubocop:disable Lint/InterpolationCheck
|
46
|
+
apipie :method, 'Render template by given name' do
|
47
|
+
required :template_name, String, desc: 'name of the template to render'
|
48
|
+
optional :input_values, Hash, desc: 'key:value list of input values for the template'
|
49
|
+
optional :options, Hash, desc: 'Additional options such as :with_foreign_input_set. Set to true if a foreign input set should be considered when rendering the template'
|
50
|
+
raises error: StandardError, desc: 'raises an error if there is no template or template input with such name'
|
51
|
+
returns String, desc: 'Rendered template'
|
52
|
+
example '<%= render_template("Run Command - Ansible Default", command: "yum -y group install #{input("package")}") %>'
|
53
|
+
end
|
54
|
+
# rubocop:enable Lint/InterpolationCheck
|
26
55
|
def render_template(template_name, input_values = {}, options = {})
|
27
56
|
options.assert_valid_keys(:with_foreign_input_set)
|
28
57
|
with_foreign_input_set = options.fetch(:with_foreign_input_set, true)
|
@@ -59,6 +88,12 @@ module ForemanRemoteExecution
|
|
59
88
|
input_values.merge(overrides).with_indifferent_access
|
60
89
|
end
|
61
90
|
|
91
|
+
apipie :method, 'Returns the value of template input' do
|
92
|
+
required :name, String, desc: 'name of the template input'
|
93
|
+
raises error: UndefinedInput, desc: 'when there is no input with such name defined for the current template'
|
94
|
+
returns Object, desc: 'The value of template input'
|
95
|
+
example 'input("Include Facts") #=> "yes"'
|
96
|
+
end
|
62
97
|
def input(name)
|
63
98
|
return template_input_values[name.to_s] if template_input_values.key?(name.to_s)
|
64
99
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Api
|
2
|
+
module V2
|
3
|
+
module InterfacesControllerExtensions
|
4
|
+
extend Apipie::DSL::Concern
|
5
|
+
|
6
|
+
update_api(:create, :update) do
|
7
|
+
param :interface, Hash do
|
8
|
+
param :execution, :bool, :desc => N_('Should this interface be used for remote execution?')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -15,9 +15,13 @@ module ForemanRemoteExecution
|
|
15
15
|
proxy = ::SmartProxy.find(proxy_id)
|
16
16
|
begin
|
17
17
|
proxy.drop_host_from_known_hosts(target)
|
18
|
-
rescue
|
19
|
-
|
20
|
-
|
18
|
+
rescue ::ProxyAPI::ProxyException => e
|
19
|
+
if e.wrapped_exception.is_a?(RestClient::NotFound)
|
20
|
+
# ignore 404 when known_hosts entry is missing or the module was not enabled
|
21
|
+
Foreman::Logging.exception "Proxy failed to delete SSH known_hosts for #{name}, #{ip}", e, :level => :error
|
22
|
+
else
|
23
|
+
raise e
|
24
|
+
end
|
21
25
|
rescue => e
|
22
26
|
Rails.logger.warn e.message
|
23
27
|
return false
|
@@ -29,7 +33,10 @@ module ForemanRemoteExecution
|
|
29
33
|
logger.debug "Scheduling SSH known_hosts cleanup"
|
30
34
|
|
31
35
|
host, _kind, _target = host_kind_target
|
32
|
-
|
36
|
+
# #remote_execution_proxies may not be defined on the host object in some case
|
37
|
+
# for example Host::Discovered does not have it defined, even though these hosts
|
38
|
+
# have Nic::Managed interfaces associated with them
|
39
|
+
proxies = (host.try(:remote_execution_proxies, 'SSH') || {}).values
|
33
40
|
proxies.flatten.uniq.each do |proxy|
|
34
41
|
queue.create(id: queue_id(proxy.id), name: _("Remove SSH known hosts for %s") % self,
|
35
42
|
priority: 200, action: [self, :drop_from_known_hosts, proxy.id])
|
@@ -41,7 +41,10 @@ class JobInvocation < ApplicationRecord
|
|
41
41
|
has_many :template_invocation_tasks, :through => :template_invocations,
|
42
42
|
:class_name => 'ForemanTasks::Task',
|
43
43
|
:source => 'run_host_job_task'
|
44
|
-
|
44
|
+
has_one :user, through: :task
|
45
|
+
scoped_search relation: :user, on: :login, rename: 'user', complete_value: true,
|
46
|
+
value_translation: ->(value) { value == 'current_user' ? User.current.login : value },
|
47
|
+
special_values: [:current_user], aliases: ['owner'], :only_explicit => true
|
45
48
|
scoped_search :relation => :task, :on => :started_at, :rename => 'started_at', :complete_value => true
|
46
49
|
scoped_search :relation => :task, :on => :start_at, :rename => 'start_at', :complete_value => true
|
47
50
|
scoped_search :relation => :task, :on => :ended_at, :rename => 'ended_at', :complete_value => true
|
@@ -70,7 +73,7 @@ class JobInvocation < ApplicationRecord
|
|
70
73
|
|
71
74
|
delegate :start_at, :to => :task, :allow_nil => true
|
72
75
|
|
73
|
-
encrypts :password, :key_passphrase, :
|
76
|
+
encrypts :password, :key_passphrase, :effective_user_password
|
74
77
|
|
75
78
|
def self.search_by_status(key, operator, value)
|
76
79
|
conditions = HostStatus::ExecutionStatus::ExecutionTaskStatusMapper.sql_conditions_for(value)
|
@@ -139,7 +142,7 @@ class JobInvocation < ApplicationRecord
|
|
139
142
|
invocation.pattern_template_invocations = self.pattern_template_invocations.map(&:deep_clone)
|
140
143
|
invocation.password = self.password
|
141
144
|
invocation.key_passphrase = self.key_passphrase
|
142
|
-
invocation.
|
145
|
+
invocation.effective_user_password = self.effective_user_password
|
143
146
|
end
|
144
147
|
end
|
145
148
|
|
@@ -170,7 +173,7 @@ class JobInvocation < ApplicationRecord
|
|
170
173
|
|
171
174
|
def total_hosts_count
|
172
175
|
if targeting.resolved?
|
173
|
-
targeting.hosts.count
|
176
|
+
task&.main_action&.total_count || targeting.hosts.count
|
174
177
|
else
|
175
178
|
_('N/A')
|
176
179
|
end
|
@@ -240,6 +243,10 @@ class JobInvocation < ApplicationRecord
|
|
240
243
|
!task.pending?
|
241
244
|
end
|
242
245
|
|
246
|
+
def missing_hosts_count
|
247
|
+
targeting.resolved? ? total_hosts_count - targeting.hosts.count : 0
|
248
|
+
end
|
249
|
+
|
243
250
|
private
|
244
251
|
|
245
252
|
def failed_template_invocations
|
@@ -15,7 +15,7 @@ class JobInvocationComposer
|
|
15
15
|
:description_format => job_invocation_base[:description_format],
|
16
16
|
:password => blank_to_nil(job_invocation_base[:password]),
|
17
17
|
:key_passphrase => blank_to_nil(job_invocation_base[:key_passphrase]),
|
18
|
-
:
|
18
|
+
:effective_user_password => blank_to_nil(job_invocation_base[:effective_user_password]),
|
19
19
|
:concurrency_control => concurrency_control_params,
|
20
20
|
:execution_timeout_interval => execution_timeout_interval,
|
21
21
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
@@ -348,7 +348,7 @@ class JobInvocationComposer
|
|
348
348
|
job_invocation.execution_timeout_interval = params[:execution_timeout_interval]
|
349
349
|
job_invocation.password = params[:password]
|
350
350
|
job_invocation.key_passphrase = params[:key_passphrase]
|
351
|
-
job_invocation.
|
351
|
+
job_invocation.effective_user_password = params[:effective_user_password]
|
352
352
|
|
353
353
|
if @reruns && job_invocation.targeting.static?
|
354
354
|
job_invocation.targeting.host_ids = JobInvocation.find(@reruns).targeting.host_ids
|
@@ -57,8 +57,8 @@ class RemoteExecutionProvider
|
|
57
57
|
[true, 'true', 'True', 'TRUE', '1'].include?(setting)
|
58
58
|
end
|
59
59
|
|
60
|
-
def
|
61
|
-
host_setting(host, :
|
60
|
+
def effective_user_password(host)
|
61
|
+
host_setting(host, :remote_execution_effective_user_password)
|
62
62
|
end
|
63
63
|
|
64
64
|
def effective_interfaces(host)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Setting::RemoteExecution < Setting
|
2
2
|
|
3
|
-
::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_cockpit_url remote_execution_form_job_template}
|
3
|
+
::Setting::BLANK_ATTRS.concat %w{remote_execution_ssh_password remote_execution_ssh_key_passphrase remote_execution_sudo_password remote_execution_effective_user_password remote_execution_cockpit_url remote_execution_form_job_template}
|
4
4
|
|
5
5
|
def self.default_settings
|
6
6
|
[
|
@@ -27,7 +27,7 @@ class Setting::RemoteExecution < Setting
|
|
27
27
|
N_('Effective User Method'),
|
28
28
|
nil,
|
29
29
|
{ :collection => proc { Hash[SSHExecutionProvider::EFFECTIVE_USER_METHODS.map { |method| [method, method] }] } }),
|
30
|
-
self.set('
|
30
|
+
self.set('remote_execution_effective_user_password', N_("Effective user password"), '', N_("Effective user password"), nil, {:encrypted => true}),
|
31
31
|
self.set('remote_execution_sync_templates',
|
32
32
|
N_('Whether we should sync templates from disk when running db:seed.'),
|
33
33
|
true,
|
@@ -32,7 +32,7 @@ class SSHExecutionProvider < RemoteExecutionProvider
|
|
32
32
|
{
|
33
33
|
:ssh_password => ssh_password(host),
|
34
34
|
:key_passphrase => ssh_key_passphrase(host),
|
35
|
-
:
|
35
|
+
:effective_user_password => effective_user_password(host),
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
@@ -0,0 +1 @@
|
|
1
|
+
attributes :execution
|
@@ -8,6 +8,7 @@ node do |invocation|
|
|
8
8
|
:failed => invocation_count(invocation, :output_key => :failed_count),
|
9
9
|
:pending => invocation_count(invocation, :output_key => :pending_count),
|
10
10
|
:total => invocation_count(invocation, :output_key => :total_count),
|
11
|
+
:missing => invocation.missing_hosts_count,
|
11
12
|
}
|
12
13
|
end
|
13
14
|
|
@@ -95,7 +95,7 @@
|
|
95
95
|
<div class="advanced hidden">
|
96
96
|
<%= password_f f, :password, :placeholder => '*****', :label => _('Password'), :label_help => N_('Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
97
97
|
<%= password_f f, :key_passphrase, :placeholder => '*****', :label => _('Private key passphrase'), :label_help => N_('Key passhprase is only applicable for SSH provider. Other providers ignore this field. <br> Passphrase is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
98
|
-
<%= password_f f, :
|
98
|
+
<%= password_f f, :effective_user_password, :placeholder => '*****', :label => _('Effective user password'), :label_help => N_('Effective user password is only applicable for SSH provider. Other providers ignore this field. <br> Password is stored encrypted in DB until the job finishes. For future or recurring executions, it is removed after the last execution.') %>
|
99
99
|
</div>
|
100
100
|
|
101
101
|
<div class="advanced hidden">
|
@@ -1,24 +1,5 @@
|
|
1
1
|
<% if job_invocation.resolved? %>
|
2
|
-
<%=
|
3
|
-
<div class="row">
|
4
|
-
<div class="title_filter col-md-6">
|
5
|
-
<div class="input-group">
|
6
|
-
<%= autocomplete_f(f, :search, value: params[:search].try(:squeeze, " "), placeholder: _("Filter") + ' ...', path: hosts_path, only_input: true) %>
|
7
|
-
<span class="input-group-btn">
|
8
|
-
<button class="btn btn-default" type="submit">
|
9
|
-
<%= icon_text('search', content_tag(:span, _('Search'), :class => 'hidden-xs', :kind => 'fa')) %>
|
10
|
-
</button>
|
11
|
-
</span>
|
12
|
-
</div>
|
13
|
-
</div>
|
14
|
-
</div>
|
15
|
-
<% end %>
|
16
|
-
<br>
|
17
|
-
|
18
|
-
<div id="targeting_hosts">
|
19
|
-
<%= mount_react_component('TargetingHosts', '#targeting_hosts') %>
|
20
|
-
</div>
|
21
|
-
<%= will_paginate_with_info @hosts, :container => true %>
|
2
|
+
<%= react_component('TargetingHosts' ) %>
|
22
3
|
<% else %>
|
23
4
|
<div class="alert alert-warning">
|
24
5
|
<%=
|
@@ -9,7 +9,19 @@
|
|
9
9
|
<% template_invocations.each do |template_invocation| %>
|
10
10
|
<%= minicard('user', template_invocation.effective_user || Setting[:remote_execution_effective_user],
|
11
11
|
template_invocation.template.name + ' ' + _('effective user')) %>
|
12
|
-
|
12
|
+
<div class="row">
|
13
|
+
<% missing = job_invocation.missing_hosts_count %>
|
14
|
+
<% size = missing.zero? ? 12 : 6 %>
|
15
|
+
<div class="col-xs-12 col-sm-<%= size %> col-md-<%= size %>" >
|
16
|
+
<%= minicard('cluster', job_invocation.total_hosts_count, _('Total hosts')) %>
|
17
|
+
</div>
|
18
|
+
<% unless missing.zero? %>
|
19
|
+
<div class="col-xs-12 col-sm-6 col-md-6" >
|
20
|
+
<%= minicard('warning-triangle-o', missing, _('Hosts gone missing'),
|
21
|
+
:tooltip => _('This can happen if the host is removed or moved to another organization or location after the job was started')) %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
13
25
|
<% if template_invocation.input_values.present? %>
|
14
26
|
<%= render :partial => 'card_user_input', :locals => { :template_invocation => template_invocation } %>
|
15
27
|
<% end %>
|