foreman_remote_execution 16.7.0 → 17.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/app/controllers/api/v2/job_invocations_controller.rb +24 -2
- data/app/controllers/job_invocations_controller.rb +1 -28
- data/app/views/api/v2/job_invocations/hosts.json.rabl +8 -0
- data/app/views/api/v2/job_invocations/main.json.rabl +1 -0
- data/app/views/job_templates/_alerts.html.erb +4 -0
- data/config/routes.rb +0 -1
- data/lib/foreman_remote_execution/plugin.rb +3 -3
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +46 -7
- data/webpack/JobInvocationDetail/JobInvocationActions.js +86 -91
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +6 -4
- data/webpack/JobInvocationDetail/JobInvocationDetail.scss +5 -3
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +92 -46
- data/webpack/JobInvocationDetail/JobInvocationSelectors.js +1 -9
- data/webpack/JobInvocationDetail/JobInvocationSystemStatusChart.js +5 -2
- data/webpack/JobInvocationDetail/JobInvocationToolbarButtons.js +10 -12
- data/webpack/JobInvocationDetail/TemplateInvocation.js +38 -26
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputCodeBlock.js +2 -1
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/TemplateActionButtons.js +17 -6
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/index.scss +0 -1
- data/webpack/JobInvocationDetail/__tests__/JobInvocationHostTablePolling.test.js +369 -0
- data/webpack/JobInvocationDetail/__tests__/JobInvocationPolling.test.js +228 -0
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +205 -154
- data/webpack/JobInvocationDetail/__tests__/TemplateInvocationPolling.test.js +254 -0
- data/webpack/JobInvocationDetail/__tests__/fixtures.js +2 -0
- data/webpack/JobInvocationDetail/index.js +25 -25
- data/webpack/JobWizard/JobWizard.js +5 -1
- data/webpack/JobWizard/JobWizard.scss +21 -0
- data/webpack/JobWizard/JobWizardConstants.js +10 -1
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +4 -2
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +4 -2
- data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +2 -1
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +4 -1
- data/webpack/JobWizard/steps/form/DateTimePicker.js +12 -6
- data/webpack/JobWizard/steps/form/FormHelpers.js +5 -4
- data/webpack/react_app/components/FeaturesDropdown/index.scss +2 -0
- data/webpack/react_app/components/TargetingHosts/TargetingHostsLabelsRow.scss +6 -2
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
- data/webpack/react_app/components/TargetingHosts/index.js +2 -1
- data/webpack/react_app/redux/actions/jobInvocations/index.js +3 -2
- data/webpack/test_setup.js +1 -13
- metadata +7 -4
- data/webpack/__mocks__/foremanReact/constants.js +0 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 248ba991802a19c3ad70d4c93a1a80931065c77f5e075520c01d35d07d3ef4e3
|
|
4
|
+
data.tar.gz: 83cad360b0893506f1ace4b8c38e60bdab716a74aaa2ff0a741908059f92e56f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d4221e0916237573f00934dac48f0e169cb78ac278ec127b6f57e72cb8982ba12e79624a082986efe878ccbb325dd426ee41dcf0f2a155e08830791e389e3124
|
|
7
|
+
data.tar.gz: 927fa72b970f31090f729ade6acdbf7b73f33c4ca337af7e3ae4f0dfc998d0f77bc69a6bc32ede1ba3a052a46e98acf38d016277ab0085c75b0bd1edeec93068
|
|
@@ -117,12 +117,14 @@ module Api
|
|
|
117
117
|
param_group :search_and_pagination, ::Api::V2::BaseController
|
|
118
118
|
add_scoped_search_description_for(JobInvocation)
|
|
119
119
|
param :id, :identifier, :required => true
|
|
120
|
+
param :include_permissions, :bool, :required => false, :desc => N_('Include per-host task and permission data in the response')
|
|
120
121
|
def hosts
|
|
121
122
|
set_hosts_and_template_invocations
|
|
122
123
|
@total = @hosts.size
|
|
123
124
|
@hosts = @hosts.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page])
|
|
124
125
|
@subtotal = @hosts.total_entries
|
|
125
126
|
set_statuses_and_smart_proxies
|
|
127
|
+
set_tasks_and_permissions if Foreman::Cast.to_bool(params[:include_permissions])
|
|
126
128
|
if params[:awaiting]
|
|
127
129
|
@hosts = @hosts.select { |host| @host_statuses[host.id] == 'N/A' }
|
|
128
130
|
end
|
|
@@ -321,15 +323,35 @@ module Api
|
|
|
321
323
|
template_invocations = @template_invocations.where(host_id: @hosts.select(:id))
|
|
322
324
|
.includes(:run_host_job_task).to_a
|
|
323
325
|
hosts = @hosts.to_a
|
|
324
|
-
template_invocations_by_host_id = template_invocations.index_by(&:host_id)
|
|
326
|
+
@template_invocations_by_host_id = template_invocations.index_by(&:host_id)
|
|
325
327
|
@host_statuses = hosts.to_h do |host|
|
|
326
|
-
template_invocation = template_invocations_by_host_id[host.id]
|
|
328
|
+
template_invocation = @template_invocations_by_host_id[host.id]
|
|
327
329
|
task = template_invocation.try(:run_host_job_task)
|
|
328
330
|
[host.id, template_invocation_status(task, @job_invocation.task)]
|
|
329
331
|
end
|
|
330
332
|
@smart_proxy_id = template_invocations.to_h { |ti| [ti.host_id, ti.smart_proxy_id] }
|
|
331
333
|
@smart_proxy_name = template_invocations.to_h { |ti| [ti.host_id, ti.smart_proxy_name] }
|
|
332
334
|
end
|
|
335
|
+
|
|
336
|
+
def set_tasks_and_permissions
|
|
337
|
+
can_cancel = authorized_for(:permission => :cancel_job_invocations, :auth_object => @job_invocation)
|
|
338
|
+
can_create = authorized_for(controller: :job_invocations, action: :create)
|
|
339
|
+
can_execute_on_infra = User.current.can?(:execute_jobs_on_infrastructure_hosts)
|
|
340
|
+
|
|
341
|
+
hosts = @hosts.to_a
|
|
342
|
+
@task_by_host = hosts.to_h do |host|
|
|
343
|
+
task = @template_invocations_by_host_id[host.id].try(:run_host_job_task)
|
|
344
|
+
[host.id, task ? { :id => task.id, :cancellable => task.try(:cancellable?) || false } : nil]
|
|
345
|
+
end
|
|
346
|
+
@permissions_by_host = hosts.to_h do |host|
|
|
347
|
+
task = @template_invocations_by_host_id[host.id].try(:run_host_job_task)
|
|
348
|
+
[host.id, {
|
|
349
|
+
:view_foreman_tasks => authorized_for(:permission => :view_foreman_tasks, :auth_object => task),
|
|
350
|
+
:cancel_job_invocations => can_cancel,
|
|
351
|
+
:execute_jobs => can_create && (!host.infrastructure_host? || can_execute_on_infra),
|
|
352
|
+
}]
|
|
353
|
+
end
|
|
354
|
+
end
|
|
333
355
|
end
|
|
334
356
|
end
|
|
335
357
|
end
|
|
@@ -149,33 +149,6 @@ class JobInvocationsController < ApplicationController
|
|
|
149
149
|
render :json => {:job_invocations => job_invocations}
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def list_jobs_hosts
|
|
153
|
-
@job_invocation = resource_base.find(params[:id])
|
|
154
|
-
hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
|
155
|
-
hosts = hosts.search_for(params[:search])
|
|
156
|
-
template_invocations_task_by_hosts = {}
|
|
157
|
-
hosts.each do |host|
|
|
158
|
-
template_invocation = @job_invocation.template_invocations.find { |template_inv| template_inv.host_id == host.id }
|
|
159
|
-
next unless template_invocation
|
|
160
|
-
template_invocation_task = template_invocation.run_host_job_task
|
|
161
|
-
template_invocations_task_by_hosts[host.id] =
|
|
162
|
-
{
|
|
163
|
-
:host_name => host.name,
|
|
164
|
-
:id => host.id,
|
|
165
|
-
:task => template_invocation_task.attributes.merge({cancellable: template_invocation_task.cancellable? }),
|
|
166
|
-
:permissions => {
|
|
167
|
-
:view_foreman_tasks => authorized_for(:permission => :view_foreman_tasks, :auth_object => template_invocation_task),
|
|
168
|
-
:cancel_job_invocations => authorized_for(:permission => :cancel_job_invocations, :auth_object => @job_invocation),
|
|
169
|
-
:execute_jobs => authorized_for(controller: :job_invocations, action: :create) && (!host.infrastructure_host? || User.current.can?(:execute_jobs_on_infrastructure_hosts)),
|
|
170
|
-
},
|
|
171
|
-
}
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
render json: {
|
|
175
|
-
:template_invocations_task_by_hosts => template_invocations_task_by_hosts,
|
|
176
|
-
}
|
|
177
|
-
end
|
|
178
|
-
|
|
179
152
|
private
|
|
180
153
|
|
|
181
154
|
def action_permission
|
|
@@ -186,7 +159,7 @@ class JobInvocationsController < ApplicationController
|
|
|
186
159
|
'create'
|
|
187
160
|
when 'cancel'
|
|
188
161
|
'cancel'
|
|
189
|
-
when 'chart', 'preview_job_invocations_per_host'
|
|
162
|
+
when 'chart', 'preview_job_invocations_per_host'
|
|
190
163
|
'view'
|
|
191
164
|
else
|
|
192
165
|
super
|
|
@@ -13,3 +13,11 @@ end
|
|
|
13
13
|
node :smart_proxy_name do |host|
|
|
14
14
|
@smart_proxy_name[host.id]
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
node(:task, :if => ->(_host) { @task_by_host }) do |host|
|
|
18
|
+
@task_by_host[host.id]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
node(:permissions, :if => ->(_host) { @permissions_by_host }) do |host|
|
|
22
|
+
@permissions_by_host[host.id]
|
|
23
|
+
end
|
data/config/routes.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Foreman::Plugin.register :foreman_remote_execution do
|
|
2
|
-
requires_foreman '>=
|
|
2
|
+
requires_foreman '>= 5.0'
|
|
3
3
|
register_global_js_file 'global'
|
|
4
4
|
register_gettext
|
|
5
5
|
|
|
@@ -134,9 +134,9 @@ Foreman::Plugin.register :foreman_remote_execution do
|
|
|
134
134
|
permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate'
|
|
135
135
|
permission :create_job_invocations, { :job_invocations => [:new, :create, :legacy_create, :refresh, :rerun, :preview_hosts],
|
|
136
136
|
'api/v2/job_invocations' => [:create, :rerun] }, :resource_type => 'JobInvocation'
|
|
137
|
-
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search, :preview_job_invocations_per_host
|
|
137
|
+
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search, :preview_job_invocations_per_host], :template_invocations => [:show, :show_template_invocation_by_host],
|
|
138
138
|
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs, :hosts] }, :resource_type => 'JobInvocation'
|
|
139
|
-
permission :view_template_invocations, { :template_invocations => [:show, :template_invocation_preview, :show_template_invocation_by_host],
|
|
139
|
+
permission :view_template_invocations, { :template_invocations => [:show, :template_invocation_preview, :show_template_invocation_by_host],
|
|
140
140
|
'api/v2/template_invocations' => [:template_invocations], :ui_job_wizard => [:job_invocation] }, :resource_type => 'TemplateInvocation'
|
|
141
141
|
permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
|
|
142
142
|
permission :execute_jobs_on_infrastructure_hosts, {}, :resource_type => 'JobInvocation'
|
|
@@ -284,20 +284,59 @@ module Api
|
|
|
284
284
|
end
|
|
285
285
|
|
|
286
286
|
describe '#hosts' do
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
2.times {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
287
|
+
setup do
|
|
288
|
+
@hosts_invocation = FactoryBot.create(:job_invocation, :with_template, :with_task)
|
|
289
|
+
2.times { @hosts_invocation.template_invocations << FactoryBot.create(:template_invocation, :with_task, :with_host, :job_invocation => @hosts_invocation) }
|
|
290
|
+
@hosts_invocation.job_category = @hosts_invocation.pattern_template_invocations.first.template.job_category
|
|
291
|
+
@hosts_invocation.targeting.hosts = @hosts_invocation.template_invocations.map(&:host)
|
|
292
|
+
@hosts_invocation.save!
|
|
293
|
+
end
|
|
293
294
|
|
|
294
|
-
|
|
295
|
+
test 'should compute job_status for the paginated subset' do
|
|
296
|
+
get :hosts, params: { :id => @hosts_invocation.id, :page => 2, :per_page => 1 }
|
|
295
297
|
assert_response :success
|
|
296
298
|
result = ActiveSupport::JSON.decode(@response.body)
|
|
297
299
|
assert_equal 3, result['total']
|
|
298
300
|
assert_equal 1, result['results'].size
|
|
299
301
|
assert_equal(['success'], result['results'].map { |r| r['job_status'] })
|
|
300
302
|
end
|
|
303
|
+
|
|
304
|
+
test 'should not include task and permissions without include_permissions' do
|
|
305
|
+
get :hosts, params: { :id => @hosts_invocation.id }
|
|
306
|
+
assert_response :success
|
|
307
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
|
308
|
+
result['results'].each do |host|
|
|
309
|
+
assert_not host.key?('task'), 'Expected task to be absent'
|
|
310
|
+
assert_not host.key?('permissions'), 'Expected permissions to be absent'
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
test 'should include task and permissions with include_permissions=true' do
|
|
315
|
+
get :hosts, params: { :id => @hosts_invocation.id, :include_permissions => true }
|
|
316
|
+
assert_response :success
|
|
317
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
|
318
|
+
result['results'].each do |host|
|
|
319
|
+
assert host.key?('task'), 'Expected task to be present'
|
|
320
|
+
assert host.key?('permissions'), 'Expected permissions to be present'
|
|
321
|
+
permissions = host['permissions']
|
|
322
|
+
assert permissions.key?('view_foreman_tasks')
|
|
323
|
+
assert permissions.key?('cancel_job_invocations')
|
|
324
|
+
assert permissions.key?('execute_jobs')
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
test 'should handle hosts with nil task gracefully when include_permissions=true' do
|
|
329
|
+
invocation = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
|
|
330
|
+
invocation.job_category = invocation.pattern_template_invocations.first.template.job_category
|
|
331
|
+
invocation.save!
|
|
332
|
+
|
|
333
|
+
get :hosts, params: { :id => invocation.id, :include_permissions => true }
|
|
334
|
+
assert_response :success
|
|
335
|
+
result = ActiveSupport::JSON.decode(@response.body)
|
|
336
|
+
result['results'].each do |host|
|
|
337
|
+
assert host.key?('permissions'), 'Expected permissions to be present'
|
|
338
|
+
end
|
|
339
|
+
end
|
|
301
340
|
end
|
|
302
341
|
|
|
303
342
|
describe 'raw output' do
|
|
@@ -1,52 +1,73 @@
|
|
|
1
1
|
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
|
2
|
-
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
3
2
|
import { addToast } from 'foremanReact/components/ToastsList';
|
|
4
|
-
import { APIActions
|
|
5
|
-
import {
|
|
6
|
-
stopInterval,
|
|
7
|
-
withInterval,
|
|
8
|
-
} from 'foremanReact/redux/middlewares/IntervalMiddleware';
|
|
3
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
9
4
|
import {
|
|
10
5
|
CANCEL_JOB,
|
|
11
6
|
CANCEL_RECURRING_LOGIC,
|
|
12
7
|
CHANGE_ENABLED_RECURRING_LOGIC,
|
|
13
|
-
GET_TASK,
|
|
14
8
|
JOB_INVOCATION_KEY,
|
|
15
|
-
|
|
9
|
+
STATUS,
|
|
10
|
+
AUTO_REFRESH_INTERVAL_MS,
|
|
16
11
|
} from './JobInvocationConstants';
|
|
17
12
|
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}),
|
|
35
|
-
1000
|
|
36
|
-
);
|
|
13
|
+
export const isJobFinished = statusLabel =>
|
|
14
|
+
statusLabel === STATUS.FAILED ||
|
|
15
|
+
statusLabel === STATUS.SUCCEEDED ||
|
|
16
|
+
statusLabel === STATUS.CANCELLED;
|
|
17
|
+
|
|
18
|
+
const extractErrorMessage = response =>
|
|
19
|
+
// eslint-disable-next-line camelcase
|
|
20
|
+
response?.data?.error?.full_messages?.[0] ||
|
|
21
|
+
response?.data?.error?.message ||
|
|
22
|
+
__('Unknown error.');
|
|
23
|
+
|
|
24
|
+
const createPollState = cancel => ({ timeoutId: null, cancel });
|
|
25
|
+
|
|
26
|
+
export const getJobInvocation = (url, pollTimeoutRef) => dispatch => {
|
|
27
|
+
let cancelled = false;
|
|
28
|
+
let isFirstCall = true;
|
|
37
29
|
|
|
38
|
-
|
|
30
|
+
const poll = () => {
|
|
31
|
+
if (cancelled) return;
|
|
32
|
+
const params = { include_hosts: false };
|
|
33
|
+
if (isFirstCall) {
|
|
34
|
+
params.include_permissions = true;
|
|
35
|
+
isFirstCall = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dispatch(
|
|
39
|
+
APIActions.get({
|
|
40
|
+
key: JOB_INVOCATION_KEY,
|
|
41
|
+
params,
|
|
42
|
+
url,
|
|
43
|
+
handleSuccess: ({ data }) => {
|
|
44
|
+
if (cancelled) return;
|
|
45
|
+
// eslint-disable-next-line camelcase
|
|
46
|
+
pollTimeoutRef.current.timeoutId = isJobFinished(data?.status_label)
|
|
47
|
+
? null
|
|
48
|
+
: setTimeout(poll, AUTO_REFRESH_INTERVAL_MS);
|
|
49
|
+
},
|
|
50
|
+
handleError: () => {
|
|
51
|
+
if (cancelled) return;
|
|
52
|
+
pollTimeoutRef.current.timeoutId = null;
|
|
53
|
+
},
|
|
54
|
+
errorToast: ({ response }) => extractErrorMessage(response),
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
stopJobInvocationPolling(pollTimeoutRef);
|
|
60
|
+
pollTimeoutRef.current = createPollState(() => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
poll();
|
|
39
65
|
};
|
|
40
66
|
|
|
41
|
-
export const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
url,
|
|
46
|
-
key: UPDATE_JOB,
|
|
47
|
-
params: { include_hosts: false },
|
|
48
|
-
})
|
|
49
|
-
);
|
|
67
|
+
export const stopJobInvocationPolling = pollTimeoutRef => {
|
|
68
|
+
clearTimeout(pollTimeoutRef.current.timeoutId);
|
|
69
|
+
pollTimeoutRef.current.cancel();
|
|
70
|
+
pollTimeoutRef.current = createPollState(() => {});
|
|
50
71
|
};
|
|
51
72
|
|
|
52
73
|
export const cancelJob = (jobId, force) => dispatch => {
|
|
@@ -54,10 +75,6 @@ export const cancelJob = (jobId, force) => dispatch => {
|
|
|
54
75
|
force
|
|
55
76
|
? sprintf(__('Trying to abort the job %s.'), jobId)
|
|
56
77
|
: sprintf(__('Trying to cancel the job %s.'), jobId);
|
|
57
|
-
const errorToast = response =>
|
|
58
|
-
force
|
|
59
|
-
? sprintf(__(`Could not abort the job %s: ${response}`), jobId)
|
|
60
|
-
: sprintf(__(`Could not cancel the job %s: ${response}`), jobId);
|
|
61
78
|
const url = force
|
|
62
79
|
? `/job_invocations/${jobId}/cancel?force=true`
|
|
63
80
|
: `/job_invocations/${jobId}/cancel`;
|
|
@@ -67,12 +84,17 @@ export const cancelJob = (jobId, force) => dispatch => {
|
|
|
67
84
|
url,
|
|
68
85
|
key: CANCEL_JOB,
|
|
69
86
|
errorToast: ({ response }) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
force
|
|
88
|
+
? sprintf(
|
|
89
|
+
__('Could not abort the job %s: %s'),
|
|
90
|
+
jobId,
|
|
91
|
+
extractErrorMessage(response)
|
|
92
|
+
)
|
|
93
|
+
: sprintf(
|
|
94
|
+
__('Could not cancel the job %s: %s'),
|
|
95
|
+
jobId,
|
|
96
|
+
extractErrorMessage(response)
|
|
97
|
+
),
|
|
76
98
|
handleSuccess: () => {
|
|
77
99
|
dispatch(
|
|
78
100
|
addToast({
|
|
@@ -81,40 +103,16 @@ export const cancelJob = (jobId, force) => dispatch => {
|
|
|
81
103
|
message: infoToast(),
|
|
82
104
|
})
|
|
83
105
|
);
|
|
84
|
-
dispatch(updateJob(jobId));
|
|
85
106
|
},
|
|
86
107
|
})
|
|
87
108
|
);
|
|
88
109
|
};
|
|
89
110
|
|
|
90
|
-
export const
|
|
91
|
-
dispatch(
|
|
92
|
-
get({
|
|
93
|
-
key: GET_TASK,
|
|
94
|
-
url: `/foreman_tasks/api/tasks/${taskId}`,
|
|
95
|
-
})
|
|
96
|
-
);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export const enableRecurringLogic = (
|
|
100
|
-
recurrenceId,
|
|
101
|
-
enabled,
|
|
102
|
-
jobId
|
|
103
|
-
) => dispatch => {
|
|
111
|
+
export const enableRecurringLogic = (recurrenceId, enabled) => dispatch => {
|
|
104
112
|
const successToast = () =>
|
|
105
113
|
enabled
|
|
106
114
|
? sprintf(__('Recurring logic %s disabled successfully.'), recurrenceId)
|
|
107
115
|
: sprintf(__('Recurring logic %s enabled successfully.'), recurrenceId);
|
|
108
|
-
const errorToast = response =>
|
|
109
|
-
enabled
|
|
110
|
-
? sprintf(
|
|
111
|
-
__(`Could not disable recurring logic %s: ${response}`),
|
|
112
|
-
recurrenceId
|
|
113
|
-
)
|
|
114
|
-
: sprintf(
|
|
115
|
-
__(`Could not enable recurring logic %s: ${response}`),
|
|
116
|
-
recurrenceId
|
|
117
|
-
);
|
|
118
116
|
const url = `/foreman_tasks/api/recurring_logics/${recurrenceId}`;
|
|
119
117
|
dispatch(
|
|
120
118
|
APIActions.put({
|
|
@@ -123,25 +121,24 @@ export const enableRecurringLogic = (
|
|
|
123
121
|
params: { recurring_logic: { enabled: !enabled } },
|
|
124
122
|
successToast,
|
|
125
123
|
errorToast: ({ response }) =>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
enabled
|
|
125
|
+
? sprintf(
|
|
126
|
+
__('Could not disable recurring logic %s: %s'),
|
|
127
|
+
recurrenceId,
|
|
128
|
+
extractErrorMessage(response)
|
|
129
|
+
)
|
|
130
|
+
: sprintf(
|
|
131
|
+
__('Could not enable recurring logic %s: %s'),
|
|
132
|
+
recurrenceId,
|
|
133
|
+
extractErrorMessage(response)
|
|
134
|
+
),
|
|
133
135
|
})
|
|
134
136
|
);
|
|
135
137
|
};
|
|
136
138
|
|
|
137
|
-
export const cancelRecurringLogic =
|
|
139
|
+
export const cancelRecurringLogic = recurrenceId => dispatch => {
|
|
138
140
|
const successToast = () =>
|
|
139
141
|
sprintf(__('Recurring logic %s cancelled successfully.'), recurrenceId);
|
|
140
|
-
const errorToast = response =>
|
|
141
|
-
sprintf(
|
|
142
|
-
__(`Could not cancel recurring logic %s: ${response}`),
|
|
143
|
-
recurrenceId
|
|
144
|
-
);
|
|
145
142
|
const url = `/foreman_tasks/recurring_logics/${recurrenceId}/cancel`;
|
|
146
143
|
dispatch(
|
|
147
144
|
APIActions.post({
|
|
@@ -149,13 +146,11 @@ export const cancelRecurringLogic = (recurrenceId, jobId) => dispatch => {
|
|
|
149
146
|
key: CANCEL_RECURRING_LOGIC,
|
|
150
147
|
successToast,
|
|
151
148
|
errorToast: ({ response }) =>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
'Unknown error.'
|
|
149
|
+
sprintf(
|
|
150
|
+
__('Could not cancel recurring logic %s: %s'),
|
|
151
|
+
recurrenceId,
|
|
152
|
+
extractErrorMessage(response)
|
|
157
153
|
),
|
|
158
|
-
handleSuccess: () => dispatch(updateJob(jobId)),
|
|
159
154
|
})
|
|
160
155
|
);
|
|
161
156
|
};
|
|
@@ -6,9 +6,7 @@ import { useForemanHostDetailsPageUrl } from 'foremanReact/Root/Context/ForemanC
|
|
|
6
6
|
import JobStatusIcon from '../react_app/components/RecentJobsCard/JobStatusIcon';
|
|
7
7
|
|
|
8
8
|
export const JOB_INVOCATION_KEY = 'JOB_INVOCATION_KEY';
|
|
9
|
-
export const UPDATE_JOB = 'UPDATE_JOB';
|
|
10
9
|
export const CANCEL_JOB = 'CANCEL_JOB';
|
|
11
|
-
export const GET_TASK = 'GET_TASK';
|
|
12
10
|
export const GET_TEMPLATE_INVOCATIONS = 'GET_TEMPLATE_INVOCATIONS';
|
|
13
11
|
export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
|
|
14
12
|
export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
|
|
@@ -17,12 +15,15 @@ export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
|
|
|
17
15
|
export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
|
|
18
16
|
export const GET_TEMPLATE_INVOCATION = 'GET_TEMPLATE_INVOCATION';
|
|
19
17
|
export const DIRECT_OPEN_HOST_LIMIT = 3;
|
|
20
|
-
export const ALL_JOB_HOSTS = 'ALL_JOB_HOSTS';
|
|
21
18
|
export const AWAITING_STATUS_FILTER = '(job_invocation.result = N/A)';
|
|
22
19
|
|
|
20
|
+
export const AUTO_REFRESH_INTERVAL_MS = 5000;
|
|
21
|
+
export const DEFAULT_CHART_LEGEND_WIDTH = 270;
|
|
22
|
+
export const CLIPBOARD_COPIED_EXIT_DELAY_MS = 1500;
|
|
23
|
+
export const CLIPBOARD_DEFAULT_EXIT_DELAY_MS = 600;
|
|
24
|
+
|
|
23
25
|
export const showTemplateInvocationUrl = (hostID, jobID) =>
|
|
24
26
|
`/show_template_invocation_by_host/${hostID}/job_invocation/${jobID}`;
|
|
25
|
-
export const LIST_TEMPLATE_INVOCATIONS = 'LIST_TEMPLATE_INVOCATIONS';
|
|
26
27
|
|
|
27
28
|
export const templateInvocationPageUrl = (hostID, jobID) =>
|
|
28
29
|
`/job_invocations_detail/${jobID}/host_invocation/${hostID}`;
|
|
@@ -38,6 +39,7 @@ export const STATUS = {
|
|
|
38
39
|
SUCCEEDED: 'succeeded',
|
|
39
40
|
FAILED: 'failed',
|
|
40
41
|
CANCELLED: 'cancelled',
|
|
42
|
+
RUNNING: 'running',
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
export const STATUS_UPPERCASE = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.job-invocation-detail-flex {
|
|
2
2
|
$chart_size: 105px;
|
|
3
|
+
|
|
3
4
|
padding-top: 0px;
|
|
4
5
|
padding-left: 10px;
|
|
5
6
|
display: flex;
|
|
@@ -92,16 +93,17 @@ section.job-additional-info {
|
|
|
92
93
|
max-height: 25em;
|
|
93
94
|
}
|
|
94
95
|
}
|
|
96
|
+
|
|
95
97
|
div.invocation-output {
|
|
96
98
|
display: block;
|
|
97
99
|
padding: 9.5px;
|
|
98
100
|
margin: 0 0 10px;
|
|
99
101
|
font-size: 12px;
|
|
100
102
|
word-break: break-all;
|
|
101
|
-
|
|
103
|
+
overflow-wrap: break-word;
|
|
102
104
|
color: rgba(255, 255, 255, 1);
|
|
103
105
|
background-color: rgba(47, 47, 47, 1);
|
|
104
|
-
border: 1px solid #
|
|
106
|
+
border: 1px solid #000;
|
|
105
107
|
border-radius: 0px;
|
|
106
108
|
font-family: Menlo, Monaco, Consolas, monospace;
|
|
107
109
|
|
|
@@ -127,7 +129,7 @@ section.job-additional-info {
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
a {
|
|
130
|
-
color: #
|
|
132
|
+
color: #fff;
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
a.scroll-link{
|