foreman_remote_execution 4.5.5 → 4.8.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/ruby_ci.yml +7 -0
- data/.rubocop_todo.yml +1 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +7 -1
- data/app/graphql/types/job_invocation.rb +16 -0
- data/app/lib/actions/remote_execution/run_host_job.rb +2 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +57 -3
- data/app/mailers/rex_job_mailer.rb +15 -0
- data/app/models/job_invocation.rb +4 -0
- data/app/models/job_invocation_composer.rb +21 -13
- data/app/models/job_template.rb +1 -1
- data/app/models/remote_execution_provider.rb +17 -2
- data/app/models/rex_mail_notification.rb +13 -0
- data/app/models/setting/remote_execution.rb +7 -1
- data/app/services/ui_notifications/remote_execution_jobs/base_job_finish.rb +2 -1
- data/app/views/dashboard/_latest-jobs.html.erb +21 -0
- data/app/views/rex_job_mailer/job_finished.html.erb +24 -0
- data/app/views/rex_job_mailer/job_finished.text.erb +9 -0
- data/app/views/template_invocations/show.html.erb +2 -1
- data/db/seeds.d/50-notification_blueprints.rb +14 -0
- data/db/seeds.d/95-mail_notifications.rb +24 -0
- data/foreman_remote_execution.gemspec +2 -4
- data/lib/foreman_remote_execution/engine.rb +4 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +6 -6
- data/test/functional/api/v2/job_invocations_controller_test.rb +10 -0
- data/test/graphql/queries/job_invocation_query_test.rb +31 -0
- data/test/graphql/queries/job_invocations_query_test.rb +35 -0
- data/test/unit/actions/run_hosts_job_test.rb +99 -4
- data/test/unit/concerns/host_extensions_test.rb +4 -4
- data/test/unit/input_template_renderer_test.rb +1 -89
- data/test/unit/job_invocation_composer_test.rb +1 -12
- data/test/unit/job_invocation_report_template_test.rb +15 -12
- data/test/unit/remote_execution_provider_test.rb +34 -0
- data/webpack/JobWizard/JobWizard.js +53 -20
- data/webpack/JobWizard/JobWizard.scss +33 -4
- data/webpack/JobWizard/JobWizardConstants.js +17 -0
- data/webpack/JobWizard/__tests__/fixtures.js +8 -0
- data/webpack/JobWizard/__tests__/integration.test.js +3 -7
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +16 -5
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +48 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +29 -14
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +4 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +3 -2
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +25 -0
- data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +23 -0
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/SelectedChips.test.js +37 -0
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +50 -0
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +66 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +24 -21
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +36 -21
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +155 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +9 -8
- data/webpack/JobWizard/steps/Schedule/index.js +89 -28
- data/webpack/JobWizard/steps/form/DateTimePicker.js +93 -0
- data/webpack/JobWizard/steps/form/Formatter.js +10 -9
- data/webpack/JobWizard/steps/form/NumberInput.js +2 -0
- data/webpack/JobWizard/steps/form/WizardTitle.js +14 -0
- data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +43 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +73 -66
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +98 -0
- data/webpack/react_app/components/RecentJobsCard/constants.js +11 -0
- data/webpack/react_app/components/RecentJobsCard/styles.scss +11 -0
- data/webpack/react_app/extend/fillRecentJobsCard.js +1 -1
- metadata +26 -19
- data/webpack/react_app/components/RecentJobsCard/styles.css +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7565defbf0881174acb2a48e274aacb3d790d2ee8b9de3119307c2afabd7355
|
4
|
+
data.tar.gz: acffdb2df04d0c1caeff65513e1a63e5cf66276897206affd606c5136b7afba8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7064c4e3984fd4ef3ef463fc10cb27e911d1f24ac0b74ccf5fb3b7682ba3d4c3418e130f5b6e0f1920ed83406dbf0787585ef1e1b83cfd0c0e412219c059309c
|
7
|
+
data.tar.gz: ab22827c2750d67893621267a43095c2b845937b6d72d5a665bf800ab97542a06cda1480187174943077c9365caac0076d67f765b6c43c4e01650dca1f7b9010
|
@@ -71,3 +71,10 @@ jobs:
|
|
71
71
|
run: |
|
72
72
|
bundle exec rake test:foreman_remote_execution
|
73
73
|
bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
|
74
|
+
- name: 'Upload logs'
|
75
|
+
uses: actions/upload-artifact@v2
|
76
|
+
if: failure()
|
77
|
+
with:
|
78
|
+
name: logs
|
79
|
+
path: log/*.log
|
80
|
+
retention-days: 5
|
data/.rubocop_todo.yml
CHANGED
@@ -220,6 +220,7 @@ Naming/FileName:
|
|
220
220
|
- 'db/seeds.d/60-ssh_proxy_feature.rb'
|
221
221
|
- 'db/seeds.d/70-job_templates.rb'
|
222
222
|
- 'db/seeds.d/90-bookmarks.rb'
|
223
|
+
- 'db/seeds.d/95-mail_notifications.rb'
|
223
224
|
|
224
225
|
# Offense count: 1
|
225
226
|
# Configuration parameters: ForbiddenDelimiters.
|
@@ -41,12 +41,18 @@ module Api
|
|
41
41
|
param :effective_user, String,
|
42
42
|
:required => false,
|
43
43
|
:desc => N_('What user should be used to run the script (using sudo-like mechanisms). Defaults to a template parameter or global setting.')
|
44
|
+
param :effective_user_password, String,
|
45
|
+
:required => false,
|
46
|
+
:desc => N_('Set password for effective user (using sudo-like mechanisms)')
|
44
47
|
end
|
48
|
+
param :password, String, :required => false, :desc => N_('Set SSH password')
|
49
|
+
param :key_passphrase, String, :required => false, :desc => N_('Set SSH key passphrase')
|
45
50
|
|
46
51
|
param :recurrence, Hash, :desc => N_('Create a recurring job') do
|
47
52
|
param :cron_line, String, :required => false, :desc => N_('How often the job should occur, in the cron format')
|
48
53
|
param :max_iteration, :number, :required => false, :desc => N_('Repeat a maximum of N times')
|
49
54
|
param :end_time, DateTime, :required => false, :desc => N_('Perform no more executions after this time')
|
55
|
+
param :purpose, String, :required => false, :desc => N_('Designation of a special purpose')
|
50
56
|
end
|
51
57
|
|
52
58
|
param :scheduling, Hash, :desc => N_('Schedule the job to start at a later time') do
|
@@ -197,7 +203,7 @@ module Api
|
|
197
203
|
end
|
198
204
|
|
199
205
|
if job_invocation_params.key?(:ssh)
|
200
|
-
job_invocation_params.merge!(job_invocation_params.delete(:ssh).permit(:effective_user))
|
206
|
+
job_invocation_params.merge!(job_invocation_params.delete(:ssh).permit(:effective_user, :effective_user_password))
|
201
207
|
end
|
202
208
|
|
203
209
|
job_invocation_params[:inputs] ||= {}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Types
|
2
|
+
class JobInvocation < BaseObject
|
3
|
+
description 'A Job Invocation'
|
4
|
+
|
5
|
+
global_id_field :id
|
6
|
+
field :job_category, String
|
7
|
+
field :description, String
|
8
|
+
field :time_span, Integer
|
9
|
+
field :start_at, GraphQL::Types::ISO8601DateTime
|
10
|
+
field :status_label, String
|
11
|
+
|
12
|
+
belongs_to :triggering, Types::Triggering
|
13
|
+
belongs_to :task, Types::Task
|
14
|
+
field :recurring_logic, Types::RecurringLogic
|
15
|
+
end
|
16
|
+
end
|
@@ -56,7 +56,8 @@ module Actions
|
|
56
56
|
:secrets => secrets(host, job_invocation, provider),
|
57
57
|
:use_batch_triggering => true,
|
58
58
|
:use_concurrency_control => options[:use_concurrency_control],
|
59
|
-
:first_execution => first_execution
|
59
|
+
:first_execution => first_execution,
|
60
|
+
:alternative_names => provider.alternative_names(host) }
|
60
61
|
action_options = provider.proxy_command_options(template_invocation, host)
|
61
62
|
.merge(additional_options)
|
62
63
|
|
@@ -9,7 +9,9 @@ module Actions
|
|
9
9
|
middleware.use Actions::Middleware::BindJobInvocation
|
10
10
|
middleware.use Actions::Middleware::RecurringLogic
|
11
11
|
middleware.use Actions::Middleware::WatchDelegatedProxySubTasks
|
12
|
-
|
12
|
+
|
13
|
+
execution_plan_hooks.use :notify_on_success, :on => :success
|
14
|
+
execution_plan_hooks.use :notify_on_failure, :on => :failure
|
13
15
|
|
14
16
|
class CheckOnProxyActions; end
|
15
17
|
|
@@ -32,6 +34,10 @@ module Actions
|
|
32
34
|
set_up_concurrency_control job_invocation
|
33
35
|
input.update(:job_category => job_invocation.job_category)
|
34
36
|
plan_self(:job_invocation_id => job_invocation.id)
|
37
|
+
provider = job_invocation.pattern_template_invocations.first&.template&.provider
|
38
|
+
input[:proxy_batch_size] ||= provider&.proxy_batch_size || Setting['foreman_tasks_proxy_batch_size']
|
39
|
+
trigger_action = plan_action(Actions::TriggerProxyBatch, batch_size: proxy_batch_size, total_count: hosts.count)
|
40
|
+
input[:trigger_run_step_id] = trigger_action.run_step_id
|
35
41
|
end
|
36
42
|
|
37
43
|
def create_sub_plans
|
@@ -46,14 +52,47 @@ module Actions
|
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
55
|
+
def spawn_plans
|
56
|
+
super
|
57
|
+
ensure
|
58
|
+
trigger_remote_batch
|
59
|
+
end
|
60
|
+
|
61
|
+
def trigger_remote_batch
|
62
|
+
batches_ready = (output[:planned_count] - output[:remote_triggered_count]) / proxy_batch_size
|
63
|
+
return unless batches_ready > 0
|
64
|
+
|
65
|
+
plan_event(Actions::TriggerProxyBatch::TriggerNextBatch[batches_ready], nil, step_id: input[:trigger_run_step_id])
|
66
|
+
output[:remote_triggered_count] += proxy_batch_size * batches_ready
|
67
|
+
end
|
68
|
+
|
69
|
+
def on_planning_finished
|
70
|
+
plan_event(Actions::TriggerProxyBatch::TriggerLastBatch, nil, step_id: input[:trigger_run_step_id])
|
71
|
+
super
|
72
|
+
end
|
73
|
+
|
49
74
|
def finalize
|
50
75
|
job_invocation.password = job_invocation.key_passphrase = job_invocation.effective_user_password = nil
|
51
76
|
job_invocation.save!
|
52
77
|
|
53
78
|
Rails.logger.debug "cleaning cache for keys that begin with 'job_invocation_#{job_invocation.id}'"
|
54
79
|
Rails.cache.delete_matched(/\A#{JobInvocation::CACHE_PREFIX}_#{job_invocation.id}/)
|
55
|
-
|
80
|
+
end
|
81
|
+
|
82
|
+
def notify_on_success(_plan)
|
56
83
|
job_invocation.build_notification.deliver!
|
84
|
+
|
85
|
+
if [RexMailNotification::SUCCEEDED_JOBS, RexMailNotification::ALL_JOBS].include?(mail_notification_preference&.interval)
|
86
|
+
RexJobMailer.job_finished(job_invocation, subject: _("REX job has succeeded - %s") % job_invocation.to_s).deliver_now
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def notify_on_failure(_plan)
|
91
|
+
job_invocation.build_notification.deliver!
|
92
|
+
|
93
|
+
if [RexMailNotification::FAILED_JOBS, RexMailNotification::ALL_JOBS].include?(mail_notification_preference&.interval)
|
94
|
+
RexJobMailer.job_finished(job_invocation, subject: _("REX job has failed - %s") % job_invocation.to_s).deliver_now
|
95
|
+
end
|
57
96
|
end
|
58
97
|
|
59
98
|
def job_invocation
|
@@ -67,6 +106,7 @@ module Actions
|
|
67
106
|
|
68
107
|
def initiate
|
69
108
|
output[:host_count] = total_count
|
109
|
+
output[:remote_triggered_count] = 0
|
70
110
|
super
|
71
111
|
end
|
72
112
|
|
@@ -94,7 +134,11 @@ module Actions
|
|
94
134
|
end
|
95
135
|
|
96
136
|
def run(event = nil)
|
97
|
-
|
137
|
+
if event == Dynflow::Action::Skip
|
138
|
+
plan_event(Dynflow::Action::Skip, nil, step_id: input[:trigger_run_step_id])
|
139
|
+
else
|
140
|
+
super
|
141
|
+
end
|
98
142
|
end
|
99
143
|
|
100
144
|
def humanized_input
|
@@ -104,6 +148,16 @@ module Actions
|
|
104
148
|
def humanized_name
|
105
149
|
'%s:' % _(super)
|
106
150
|
end
|
151
|
+
|
152
|
+
def proxy_batch_size
|
153
|
+
input[:proxy_batch_size]
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def mail_notification_preference
|
159
|
+
UserMailNotification.where(mail_notification_id: RexMailNotification.first, user_id: User.current.id).first
|
160
|
+
end
|
107
161
|
end
|
108
162
|
end
|
109
163
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class RexJobMailer < ApplicationMailer
|
2
|
+
add_template_helper(ApplicationHelper)
|
3
|
+
|
4
|
+
def job_finished(job, opts = {})
|
5
|
+
@job = job
|
6
|
+
@subject = opts[:subject] || _('REX job has finished - %s') % @job.to_s
|
7
|
+
|
8
|
+
if @job.user.nil?
|
9
|
+
Rails.logger.warn 'Job user no longer exist, skipping email notification'
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
mail(to: @job.user.mail, subject: @subject)
|
14
|
+
end
|
15
|
+
end
|
@@ -65,6 +65,8 @@ class JobInvocation < ApplicationRecord
|
|
65
65
|
scoped_search :on => 'pattern_template_name', :rename => 'pattern_template_name', :operators => ['= '],
|
66
66
|
:complete_value => false, :only_explicit => true, :ext_method => :search_by_pattern_template
|
67
67
|
|
68
|
+
scoped_search :relation => :recurring_logic, :on => 'purpose', :rename => 'recurring_logic.purpose'
|
69
|
+
|
68
70
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring_logic.id'
|
69
71
|
|
70
72
|
scoped_search :relation => :recurring_logic, :on => 'id', :rename => 'recurring',
|
@@ -73,6 +75,8 @@ class JobInvocation < ApplicationRecord
|
|
73
75
|
|
74
76
|
default_scope -> { order('job_invocations.id DESC') }
|
75
77
|
|
78
|
+
scope :latest_jobs, -> { unscoped.joins(:task).order('foreman_tasks_tasks.start_at DESC').authorized(:view_job_invocations).limit(5) }
|
79
|
+
|
76
80
|
validates_lengths_from_database :only => [:description]
|
77
81
|
|
78
82
|
attr_accessor :start_before, :description_format
|
@@ -121,7 +121,10 @@ class JobInvocationComposer
|
|
121
121
|
:targeting => targeting_params,
|
122
122
|
:triggering => triggering_params,
|
123
123
|
:description_format => api_params[:description_format],
|
124
|
+
:password => api_params[:password],
|
124
125
|
:remote_execution_feature_id => remote_execution_feature_id,
|
126
|
+
:effective_user_password => api_params[:effective_user_password],
|
127
|
+
:key_passphrase => api_params[:key_passphrase],
|
125
128
|
:concurrency_control => concurrency_control_params,
|
126
129
|
:execution_timeout_interval => api_params[:execution_timeout_interval] || template.execution_timeout_interval,
|
127
130
|
:template_invocations => template_invocations_params }.with_indifferent_access
|
@@ -138,17 +141,11 @@ class JobInvocationComposer
|
|
138
141
|
end
|
139
142
|
|
140
143
|
def triggering_params
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
:cronline => api_params[:recurrence][:cron_line],
|
147
|
-
:end_time => format_datetime(api_params[:recurrence][:end_time]),
|
148
|
-
:input_type => :cronline,
|
149
|
-
:max_iteration => api_params[:recurrence][:max_iteration],
|
150
|
-
}
|
151
|
-
elsif api_params[:scheduling].present?
|
144
|
+
if api_params[:recurrence].present? && api_params[:scheduling].present?
|
145
|
+
recurring_mode_params.merge :start_at_raw => format_datetime(api_params[:scheduling][:start_at])
|
146
|
+
elsif api_params[:recurrence].present? && api_params[:scheduling].empty?
|
147
|
+
recurring_mode_params
|
148
|
+
elsif api_params[:recurrence].empty? && api_params[:scheduling].present?
|
152
149
|
{
|
153
150
|
:mode => :future,
|
154
151
|
:start_at_raw => format_datetime(api_params[:scheduling][:start_at]),
|
@@ -160,6 +157,17 @@ class JobInvocationComposer
|
|
160
157
|
end
|
161
158
|
end
|
162
159
|
|
160
|
+
def recurring_mode_params
|
161
|
+
{
|
162
|
+
:mode => :recurring,
|
163
|
+
:cronline => api_params[:recurrence][:cron_line],
|
164
|
+
:end_time => format_datetime(api_params[:recurrence][:end_time]),
|
165
|
+
:input_type => :cronline,
|
166
|
+
:max_iteration => api_params[:recurrence][:max_iteration],
|
167
|
+
:purpose => api_params[:recurrence][:purpose],
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
163
171
|
def concurrency_control_params
|
164
172
|
{
|
165
173
|
:level => api_params.fetch(:concurrency_control, {})[:concurrency_level],
|
@@ -513,7 +521,7 @@ class JobInvocationComposer
|
|
513
521
|
end
|
514
522
|
|
515
523
|
def available_bookmarks
|
516
|
-
Bookmark.
|
524
|
+
Bookmark.my_bookmarks.where(:controller => ['hosts', 'dashboard'])
|
517
525
|
end
|
518
526
|
|
519
527
|
def targeted_hosts
|
@@ -636,7 +644,7 @@ class JobInvocationComposer
|
|
636
644
|
setting_value = Setting['remote_execution_form_job_template']
|
637
645
|
return default_value unless setting_value
|
638
646
|
|
639
|
-
form_template = JobTemplate.find_by :name => setting_value
|
647
|
+
form_template = JobTemplate.authorized(:view_job_templates).find_by :name => setting_value
|
640
648
|
return default_value unless form_template
|
641
649
|
|
642
650
|
if block_given?
|
data/app/models/job_template.rb
CHANGED
@@ -80,7 +80,14 @@ class RemoteExecutionProvider
|
|
80
80
|
|
81
81
|
def find_ip(host, interfaces)
|
82
82
|
if host_setting(host, :remote_execution_connect_by_ip)
|
83
|
-
interfaces.find { |i| i.ip.present? }.try(:ip)
|
83
|
+
ip4_address = interfaces.find { |i| i.ip.present? }.try(:ip)
|
84
|
+
ip6_address = interfaces.find { |i| i.ip6.present? }.try(:ip6)
|
85
|
+
|
86
|
+
if host_setting(host, :remote_execution_connect_by_ip_prefer_ipv6)
|
87
|
+
ip6_address || ip4_address
|
88
|
+
else
|
89
|
+
ip4_address || ip6_address
|
90
|
+
end
|
84
91
|
end
|
85
92
|
end
|
86
93
|
|
@@ -112,7 +119,11 @@ class RemoteExecutionProvider
|
|
112
119
|
end
|
113
120
|
|
114
121
|
def proxy_action_class
|
115
|
-
'
|
122
|
+
'Proxy::RemoteExecution::Ssh::Actions::RunScript'
|
123
|
+
end
|
124
|
+
|
125
|
+
def proxy_batch_size
|
126
|
+
Setting['foreman_tasks_proxy_batch_size']
|
116
127
|
end
|
117
128
|
|
118
129
|
# Return a specific proxy selector to use for running a given template
|
@@ -124,5 +135,9 @@ class RemoteExecutionProvider
|
|
124
135
|
::DefaultProxyProxySelector.new
|
125
136
|
end
|
126
137
|
end
|
138
|
+
|
139
|
+
def alternative_names(host)
|
140
|
+
{ :fqdn => find_fqdn(effective_interfaces(host)) }
|
141
|
+
end
|
127
142
|
end
|
128
143
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class RexMailNotification < MailNotification
|
2
|
+
FAILED_JOBS = N_("Subscribe to my failed jobs")
|
3
|
+
SUCCEEDED_JOBS = N_("Subscribe to my succeeded jobs")
|
4
|
+
ALL_JOBS = N_("Subscribe to all my jobs")
|
5
|
+
|
6
|
+
def subscription_options
|
7
|
+
[
|
8
|
+
FAILED_JOBS,
|
9
|
+
SUCCEEDED_JOBS,
|
10
|
+
ALL_JOBS,
|
11
|
+
]
|
12
|
+
end
|
13
|
+
end
|
@@ -39,9 +39,15 @@ class Setting::RemoteExecution < Setting
|
|
39
39
|
self.set('remote_execution_connect_by_ip',
|
40
40
|
N_('Should the ip addresses on host interfaces be preferred over the fqdn? '\
|
41
41
|
'It is useful when DNS not resolving the fqdns properly. You may override this per host by setting a parameter called remote_execution_connect_by_ip. '\
|
42
|
-
'
|
42
|
+
'For dual-stacked hosts you should consider the remote_execution_connect_by_ip_prefer_ipv6 setting'),
|
43
43
|
false,
|
44
44
|
N_('Connect by IP')),
|
45
|
+
self.set('remote_execution_connect_by_ip_prefer_ipv6',
|
46
|
+
N_('When connecting using ip address, should the IPv6 addresses be preferred? '\
|
47
|
+
'If no IPv6 address is set, it falls back to IPv4 automatically. You may override this per host by setting a parameter called remote_execution_connect_by_ip_prefer_ipv6. '\
|
48
|
+
'By default and for compatibility, IPv4 will be preferred over IPv6 by default'),
|
49
|
+
false,
|
50
|
+
N_('Prefer IPv6 over IPv4')),
|
45
51
|
self.set('remote_execution_ssh_password',
|
46
52
|
N_('Default password to use for SSH. You may override per host by setting a parameter called remote_execution_ssh_password'),
|
47
53
|
nil,
|
@@ -20,7 +20,8 @@ module UINotifications
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def blueprint
|
23
|
-
|
23
|
+
blueprint = @subject.status == HostStatus::ExecutionStatus::ERROR ? 'rex_job_failed' : 'rex_job_succeeded'
|
24
|
+
@blueprint ||= NotificationBlueprint.unscoped.find_by(:name => blueprint)
|
24
25
|
end
|
25
26
|
|
26
27
|
def message
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h4 class="header">
|
2
|
+
<%= link_to _("Latest Jobs"), job_invocations_path(:order=>'start_at DESC') %>
|
3
|
+
</h4>
|
4
|
+
<% if JobInvocation.latest_jobs.any? %>
|
5
|
+
<table class="<%= table_css_classes('table-fixed') %>">
|
6
|
+
<tr>
|
7
|
+
<th class="col-md-5"><%= _("Name") %></th>
|
8
|
+
<th class="col-md-2"><%= _("State") %></th>
|
9
|
+
<th class="col-md-3"><%= _("Started") %></th>
|
10
|
+
</tr>
|
11
|
+
<% JobInvocation.latest_jobs.each do |invocation| %>
|
12
|
+
<tr>
|
13
|
+
<td class="ellipsis"><%= link_to_if_authorized invocation_description(invocation), hash_for_job_invocation_path(invocation).merge(:auth_object => invocation, :permission => :view_job_invocations, :authorizer => authorizer) %></td>
|
14
|
+
<td><%= link_to_invocation_task_if_authorized(invocation) %></td>
|
15
|
+
<td><%= time_in_words_span(invocation.start_at) %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
</table>
|
19
|
+
<% else %>
|
20
|
+
<p class="ca"><%= _("No jobs available") %></p>
|
21
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<p>
|
2
|
+
<%= _("A job '%{job_name}' has %{status} at %{time}") % {
|
3
|
+
:job_name => @job.to_s, :status => @job.status_label, :time => date_time_absolute_value(@job.task.ended_at)
|
4
|
+
} %>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<div class="dashboard">
|
8
|
+
<table>
|
9
|
+
<tr>
|
10
|
+
<td width="18%" class="hosts-rows"><b><%= _("Job result") %></b></td>
|
11
|
+
<td width="82%" class="hosts-rows"><%= @job.status_label %></td>
|
12
|
+
</tr>
|
13
|
+
<tr>
|
14
|
+
<td width="18%" class="hosts-rows"><b><%= _("Total hosts") %></b></td>
|
15
|
+
<td width="82%" class="hosts-rows"><%= @job.total_hosts_count %></td>
|
16
|
+
</tr>
|
17
|
+
<tr>
|
18
|
+
<td width="18%" class="hosts-rows"><b><%= _("Failed hosts") %></b></td>
|
19
|
+
<td width="82%" class="hosts-rows"><%= @job.failed_hosts.count %></td>
|
20
|
+
</tr>
|
21
|
+
</table>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<%= link_to 'More details', job_invocation_url(@job) %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= _("A job '%{job_name}' has %{status} at %{time}") % {
|
2
|
+
:job_name => @job.to_s, :status => @job.status_label, :time => date_time_absolute_value(@job.task.ended_at)
|
3
|
+
} %>
|
4
|
+
|
5
|
+
<%= _('Job result') %>: <%= @job.status_label %>
|
6
|
+
<%= _('Total hosts') %>: <%= @job.total_hosts_count %>
|
7
|
+
<%= _('Failed hosts') %>: <%= @job.failed_hosts.count %>
|
8
|
+
|
9
|
+
<%= _('See more details at %s') % job_invocation_url(@job) %>
|