foreman_remote_execution 14.1.4 → 15.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/job_invocations_controller.rb +8 -0
- data/app/controllers/template_invocations_controller.rb +57 -0
- data/app/controllers/ui_job_wizard_controller.rb +6 -3
- data/app/helpers/remote_execution_helper.rb +5 -6
- data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
- data/app/views/api/v2/job_invocations/hosts.json.rabl +1 -1
- data/app/views/api/v2/job_invocations/main.json.rabl +1 -1
- data/app/views/api/v2/job_invocations/show.json.rabl +18 -0
- data/app/views/templates/script/convert2rhel_analyze.erb +4 -4
- data/config/routes.rb +2 -0
- data/lib/foreman_remote_execution/engine.rb +3 -3
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/webpack/JobInvocationDetail/JobAdditionInfo.js +214 -0
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +40 -2
- data/webpack/JobInvocationDetail/JobInvocationDetail.scss +70 -0
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +177 -80
- data/webpack/JobInvocationDetail/JobInvocationHostTableToolbar.js +63 -0
- data/webpack/JobInvocationDetail/JobInvocationSelectors.js +8 -1
- data/webpack/JobInvocationDetail/JobInvocationSystemStatusChart.js +61 -10
- data/webpack/JobInvocationDetail/OpenAlInvocations.js +111 -0
- data/webpack/JobInvocationDetail/TemplateInvocation.js +202 -0
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputCodeBlock.js +124 -0
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputToggleGroup.js +156 -0
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/PreviewTemplate.js +50 -0
- data/webpack/JobInvocationDetail/TemplateInvocationComponents/TemplateActionButtons.js +224 -0
- data/webpack/JobInvocationDetail/TemplateInvocationPage.js +53 -0
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +1 -1
- data/webpack/JobInvocationDetail/__tests__/OpenAlInvocations.test.js +110 -0
- data/webpack/JobInvocationDetail/__tests__/OutputCodeBlock.test.js +69 -0
- data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +131 -0
- data/webpack/JobInvocationDetail/__tests__/fixtures.js +130 -0
- data/webpack/JobInvocationDetail/index.js +18 -3
- data/webpack/JobWizard/JobWizard.js +38 -16
- data/webpack/JobWizard/{StartsBeforeErrorAlert.js → StartsErrorAlert.js} +16 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +1 -1
- data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +1 -1
- data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +5 -3
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +1 -1
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +3 -3
- data/webpack/JobWizard/steps/form/DateTimePicker.js +13 -0
- data/webpack/JobWizard/steps/form/Formatter.js +1 -0
- data/webpack/JobWizard/steps/form/ResourceSelect.js +34 -9
- data/webpack/Routes/routes.js +6 -0
- data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +1 -0
- data/webpack/react_app/components/RegistrationExtension/RexPull.js +27 -2
- data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +1 -1
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4dc9acfc2bf412af59bead68426f726248f020964fa433156bb341eefcee3ec
|
4
|
+
data.tar.gz: cf88f8f262a83fb638e855043be9f5f5d9bd7f3c1b365131b1906ace271eb288
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a50e49596517959f17ba39dd99b4729942051991ae3488f6a56e28505464dd422fd952ad1a98e3c5174d6b36c8a0658b827a69f4b8833c40707119414c39aca4
|
7
|
+
data.tar.gz: a50742f65d872373f2b1bc1a7aee03323adc42c6a667a18d12b40b0895c3ffc711658d3a300dab05163688f999f7d7c7b2546ea3219107ba8a0b2897c02f4923
|
@@ -22,6 +22,8 @@ module Api
|
|
22
22
|
param :host_status, :bool, required: false, desc: N_('Show Job status for the hosts')
|
23
23
|
def show
|
24
24
|
set_hosts_and_template_invocations
|
25
|
+
@job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
|
26
|
+
@job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
|
25
27
|
if params[:host_status] == 'true'
|
26
28
|
set_statuses_and_smart_proxies
|
27
29
|
end
|
@@ -266,7 +268,13 @@ module Api
|
|
266
268
|
end
|
267
269
|
|
268
270
|
def set_hosts_and_template_invocations
|
271
|
+
@pattern_template_invocations = @job_invocation.pattern_template_invocations.includes(:input_values)
|
269
272
|
@hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
|
273
|
+
|
274
|
+
unless params[:search].nil?
|
275
|
+
@hosts = @hosts.joins(:template_invocations)
|
276
|
+
.where(:template_invocations => { :job_invocation_id => @job_invocation.id})
|
277
|
+
end
|
270
278
|
@template_invocations = @job_invocation.template_invocations
|
271
279
|
.where(host: @hosts)
|
272
280
|
.includes(:input_values)
|
@@ -1,5 +1,10 @@
|
|
1
1
|
class TemplateInvocationsController < ApplicationController
|
2
2
|
include Foreman::Controller::AutoCompleteSearch
|
3
|
+
include RemoteExecutionHelper
|
4
|
+
include JobInvocationsHelper
|
5
|
+
|
6
|
+
before_action :find_job_invocation, :only => %w{show_template_invocation_by_host}
|
7
|
+
before_action :find_host, :only => %w{show_template_invocation_by_host}
|
3
8
|
|
4
9
|
def controller_permission
|
5
10
|
'job_invocations'
|
@@ -17,4 +22,56 @@ class TemplateInvocationsController < ApplicationController
|
|
17
22
|
@line_sets = @line_sets.drop_while { |o| o['timestamp'].to_f <= @since } if @since
|
18
23
|
@line_counter = params[:line_counter].to_i
|
19
24
|
end
|
25
|
+
|
26
|
+
def show_template_invocation_by_host
|
27
|
+
@template_invocation = @job_invocation.template_invocations.find { |template_inv| template_inv.host_id == @host.id }
|
28
|
+
if @template_invocation.nil?
|
29
|
+
render :json => { :error => _('Template invocation not found') }, :status => :not_found
|
30
|
+
end
|
31
|
+
@template_invocation_task = @template_invocation.run_host_job_task
|
32
|
+
|
33
|
+
lines = normalize_line_sets(@template_invocation_task.main_action.live_output)
|
34
|
+
transformed_input_values = @template_invocation.input_values.joins(:template_input).map do |input_value|
|
35
|
+
{
|
36
|
+
name: input_value&.template_input&.name,
|
37
|
+
value: input_safe_value(input_value),
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
auto_refresh = @job_invocation.task.try(:pending?)
|
42
|
+
finished = @job_invocation.status_label == 'failed' || @job_invocation.status_label == 'succeeded' || @job_invocation.status_label == 'cancelled'
|
43
|
+
render :json => { :output => lines, :preview => template_invocation_preview(@template_invocation, @host), :input_values => transformed_input_values, :job_invocation_description => @job_invocation.description, :task_id => @template_invocation_task.id, :task_cancellable => @template_invocation_task.cancellable?, :host_name => @host.name, :permissions => {
|
44
|
+
:view_foreman_tasks => User.current.allowed_to?(:view_foreman_tasks),
|
45
|
+
:cancel_job_invocations => User.current.allowed_to?(:cancel_job_invocations),
|
46
|
+
:execute_jobs => User.current.allowed_to?(:create_job_invocations) && (!@host.infrastructure_host? || User.current.can?(:execute_jobs_on_infrastructure_hosts)),
|
47
|
+
|
48
|
+
},
|
49
|
+
:auto_refresh => auto_refresh, :finished => finished}, status: :ok
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def find_job_invocation
|
55
|
+
@job_invocation = JobInvocation.find(params[:id])
|
56
|
+
rescue ActiveRecord::RecordNotFound
|
57
|
+
render :json => { :error => { :message => format(_("Job with id '%{id}' was not found"), :id => params['id']) } }, :status => :not_found
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_host
|
61
|
+
@host = Host.find(params[:host_id])
|
62
|
+
rescue ActiveRecord::RecordNotFound
|
63
|
+
render :json => { :error => { :message => format(_("Host with id '%{id}' was not found"), :id => params['host_id']) } }, :status => :not_found
|
64
|
+
end
|
65
|
+
|
66
|
+
def template_invocation_preview(template_invocation, host)
|
67
|
+
renderer = InputTemplateRenderer.new(template_invocation.template, host, template_invocation)
|
68
|
+
output = load_template_from_task(template_invocation, host) || renderer.preview
|
69
|
+
if output
|
70
|
+
{:plain => output}
|
71
|
+
else
|
72
|
+
{status: :bad_request,
|
73
|
+
plain: renderer.error_message }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
20
77
|
end
|
@@ -55,9 +55,12 @@ class UIJobWizardController < Api::V2::BaseController
|
|
55
55
|
|
56
56
|
def resources
|
57
57
|
resource_type = params[:resource]
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
attribute_name = resource_type.constantize.attribute_name
|
59
|
+
resource_list = resource_type.constantize.authorized("view_#{resource_type.underscore.pluralize}")
|
60
|
+
.search_for("#{attribute_name} ~ \"#{params[:name]}\"")
|
61
|
+
.reorder(attribute_name)
|
62
|
+
.limit(Setting[:entries_per_page] + 1).pluck(attribute_name, :id).map { |name, id| { name: name, id: id } }
|
63
|
+
render :json => { :results => resource_list, :subtotal => resource_list.count }
|
61
64
|
end
|
62
65
|
|
63
66
|
def job_invocation
|
@@ -99,12 +99,11 @@ module RemoteExecutionHelper
|
|
99
99
|
:disabled => !task.cancellable?,
|
100
100
|
:method => :post)
|
101
101
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
return buttons
|
102
|
+
buttons << link_to(_('New UI'), new_job_invocation_detail_path(:id => job_invocation.id),
|
103
|
+
class: 'btn btn-default',
|
104
|
+
title: _('Switch to the new job invocation detail UI'))
|
105
|
+
|
106
|
+
buttons
|
108
107
|
end
|
109
108
|
|
110
109
|
def template_invocation_task_buttons(task, invocation)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
object @job_invocation
|
2
2
|
|
3
|
-
attributes :id, :description, :job_category, :targeting_id, :status, :start_at, :status_label, :ssh_user, :time_to_pickup
|
3
|
+
attributes :id, :description, :job_category, :targeting_id, :status, :start_at, :status_label, :ssh_user, :time_to_pickup, :concurrency_level, :execution_timeout_interval
|
4
4
|
|
5
5
|
node do |invocation|
|
6
6
|
pattern_template = invocation.pattern_template_invocations.first
|
@@ -1,6 +1,6 @@
|
|
1
1
|
collection @hosts
|
2
2
|
|
3
|
-
attribute :name, :operatingsystem_id, :operatingsystem_name, :hostgroup_id, :hostgroup_name
|
3
|
+
attribute :name, :operatingsystem_id, :operatingsystem_name, :hostgroup_id, :hostgroup_name, :id
|
4
4
|
|
5
5
|
node :job_status do |host|
|
6
6
|
@host_statuses[host.id]
|
@@ -16,7 +16,7 @@ node do |invocation|
|
|
16
16
|
end
|
17
17
|
|
18
18
|
child :targeting do
|
19
|
-
attributes :bookmark_id, :search_query, :targeting_type, :user_id, :status, :status_label,
|
19
|
+
attributes :bookmark_id, :bookmark_name, :search_query, :targeting_type, :user_id, :status, :status_label,
|
20
20
|
:randomized_ordering
|
21
21
|
|
22
22
|
child @hosts => :hosts do
|
@@ -1,3 +1,21 @@
|
|
1
1
|
object @job_invocation
|
2
2
|
|
3
|
+
child @pattern_template_invocations => :pattern_template_invocations do
|
4
|
+
attributes :template_id, :template_name, :host_id
|
5
|
+
child :input_values do
|
6
|
+
attributes :template_input_name, :template_input_id
|
7
|
+
node :value do |iv|
|
8
|
+
iv.template_input.respond_to?(:hidden_value) && iv.template_input.hidden_value? ? '*' * 5 : iv.value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
3
13
|
extends 'api/v2/job_invocations/main'
|
14
|
+
|
15
|
+
node :job_organization do
|
16
|
+
@job_organization
|
17
|
+
end
|
18
|
+
|
19
|
+
node :job_location do
|
20
|
+
@job_location
|
21
|
+
end
|
@@ -7,17 +7,17 @@ provider_type: script
|
|
7
7
|
kind: job_template
|
8
8
|
template_inputs:
|
9
9
|
- name: ELS
|
10
|
-
required:
|
10
|
+
required: false
|
11
11
|
input_type: user
|
12
12
|
description: Use an Extended Lifecycle Support (ELS) add-on subscription
|
13
13
|
advanced: false
|
14
14
|
value_type: plain
|
15
|
-
options: "
|
16
|
-
default: "
|
15
|
+
options: "yes\r\nno"
|
16
|
+
default: "no"
|
17
17
|
hidden_value: false
|
18
18
|
%>
|
19
19
|
<%-
|
20
|
-
els = input('ELS') == "
|
20
|
+
els = input('ELS') == "yes" ? "--els" : ""
|
21
21
|
-%>
|
22
22
|
<% if @host.operatingsystem.family == 'Redhat' -%>
|
23
23
|
if ! [ $(id -u) -eq 0 ]; then
|
data/config/routes.rb
CHANGED
@@ -23,6 +23,8 @@ Rails.application.routes.draw do
|
|
23
23
|
match 'old/job_invocations/new', to: 'job_invocations#new', via: [:get], as: 'form_new_job_invocation'
|
24
24
|
match 'old/job_invocations/:id/rerun', to: 'job_invocations#rerun', via: [:get, :post], as: 'form_rerun_job_invocation'
|
25
25
|
match 'experimental/job_invocations_detail/:id', to: 'react#index', :via => [:get], as: 'new_job_invocation_detail'
|
26
|
+
match 'job_invocations_detail/:id/host_invocation/:host_id', to: 'react#index', :via => [:get], as: 'new_job_invocation_detail_by_host'
|
27
|
+
get 'show_template_invocation_by_host/:host_id/job_invocation/:id', to: 'template_invocations#show_template_invocation_by_host'
|
26
28
|
|
27
29
|
resources :job_invocations, :only => [:create, :show, :index] do
|
28
30
|
collection do
|
@@ -35,7 +35,7 @@ module ForemanRemoteExecution
|
|
35
35
|
initializer 'foreman_remote_execution.register_plugin', before: :finisher_hook do |app|
|
36
36
|
app.reloader.to_prepare do
|
37
37
|
Foreman::Plugin.register :foreman_remote_execution do
|
38
|
-
requires_foreman '>= 3.
|
38
|
+
requires_foreman '>= 3.14'
|
39
39
|
register_global_js_file 'global'
|
40
40
|
register_gettext
|
41
41
|
|
@@ -170,9 +170,9 @@ module ForemanRemoteExecution
|
|
170
170
|
permission :lock_job_templates, { :job_templates => [:lock, :unlock] }, :resource_type => 'JobTemplate'
|
171
171
|
permission :create_job_invocations, { :job_invocations => [:new, :create, :legacy_create, :refresh, :rerun, :preview_hosts],
|
172
172
|
'api/v2/job_invocations' => [:create, :rerun] }, :resource_type => 'JobInvocation'
|
173
|
-
permission :view_job_invocations, { :job_invocations => [:index, :chart, :show, :auto_complete_search, :preview_job_invocations_per_host], :template_invocations => [:show],
|
173
|
+
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],
|
174
174
|
'api/v2/job_invocations' => [:index, :show, :output, :raw_output, :outputs, :hosts] }, :resource_type => 'JobInvocation'
|
175
|
-
permission :view_template_invocations, { :template_invocations => [:show],
|
175
|
+
permission :view_template_invocations, { :template_invocations => [:show, :template_invocation_preview, :show_template_invocation_by_host],
|
176
176
|
'api/v2/template_invocations' => [:template_invocations], :ui_job_wizard => [:job_invocation] }, :resource_type => 'TemplateInvocation'
|
177
177
|
permission :create_template_invocations, {}, :resource_type => 'TemplateInvocation'
|
178
178
|
permission :execute_jobs_on_infrastructure_hosts, {}, :resource_type => 'JobInvocation'
|
@@ -0,0 +1,214 @@
|
|
1
|
+
/* eslint-disable max-lines */
|
2
|
+
/* eslint-disable camelcase */
|
3
|
+
import React, { useState } from 'react';
|
4
|
+
import PropTypes from 'prop-types';
|
5
|
+
import {
|
6
|
+
ExpandableSection,
|
7
|
+
DataList,
|
8
|
+
DataListCell,
|
9
|
+
DataListItemCells,
|
10
|
+
DataListItem,
|
11
|
+
DataListItemRow,
|
12
|
+
} from '@patternfly/react-core';
|
13
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
14
|
+
import { TARGETING_TYPES } from './JobInvocationConstants';
|
15
|
+
|
16
|
+
const ItemsParser = ({ items }) => (
|
17
|
+
<>
|
18
|
+
{items.map(
|
19
|
+
({ title, value, wrappedValue }, index) =>
|
20
|
+
value && (
|
21
|
+
<DataListItem key={index}>
|
22
|
+
<DataListItemRow>
|
23
|
+
<DataListItemCells
|
24
|
+
dataListCells={[
|
25
|
+
<DataListCell width={1} key={0}>
|
26
|
+
{title}
|
27
|
+
</DataListCell>,
|
28
|
+
<DataListCell width={4} key={1}>
|
29
|
+
{wrappedValue || value}
|
30
|
+
</DataListCell>,
|
31
|
+
]}
|
32
|
+
/>
|
33
|
+
</DataListItemRow>
|
34
|
+
</DataListItem>
|
35
|
+
)
|
36
|
+
)}
|
37
|
+
</>
|
38
|
+
);
|
39
|
+
const Schedule = ({ data }) => {
|
40
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
41
|
+
const {
|
42
|
+
concurrency_level,
|
43
|
+
scheduling,
|
44
|
+
time_to_pickup,
|
45
|
+
execution_timeout_interval,
|
46
|
+
} = data;
|
47
|
+
if (
|
48
|
+
!concurrency_level &&
|
49
|
+
!scheduling &&
|
50
|
+
!time_to_pickup &&
|
51
|
+
!execution_timeout_interval
|
52
|
+
)
|
53
|
+
return null;
|
54
|
+
const items = [
|
55
|
+
{ title: __('Concurrency level limited to'), value: concurrency_level },
|
56
|
+
{ title: __('Scheduled to start before'), value: scheduling?.start_before },
|
57
|
+
{ title: __('Scheduled to start at'), value: scheduling?.start_at },
|
58
|
+
{
|
59
|
+
title: __('Timeout to kill after'),
|
60
|
+
value: execution_timeout_interval,
|
61
|
+
wrappedValue: `${execution_timeout_interval} ${__('seconds')}`,
|
62
|
+
},
|
63
|
+
{
|
64
|
+
title: __('Time to pickup'),
|
65
|
+
value: time_to_pickup,
|
66
|
+
wrappedValue: `${time_to_pickup} ${__('seconds')}`,
|
67
|
+
},
|
68
|
+
];
|
69
|
+
return (
|
70
|
+
<ExpandableSection
|
71
|
+
toggleText={__('Schedule')}
|
72
|
+
onToggle={setIsExpanded}
|
73
|
+
isExpanded={isExpanded}
|
74
|
+
>
|
75
|
+
<DataList isCompact>
|
76
|
+
<ItemsParser items={items} />
|
77
|
+
</DataList>
|
78
|
+
</ExpandableSection>
|
79
|
+
);
|
80
|
+
};
|
81
|
+
const Recurring = ({ data }) => {
|
82
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
83
|
+
const { recurrence } = data;
|
84
|
+
|
85
|
+
if (!recurrence) return null;
|
86
|
+
const items = [
|
87
|
+
{
|
88
|
+
title: __('ID'),
|
89
|
+
value: recurrence.id,
|
90
|
+
wrappedValue: (
|
91
|
+
<a href={`/foreman_tasks/recurring_logics/${recurrence.id}`}>
|
92
|
+
{recurrence.id}
|
93
|
+
</a>
|
94
|
+
),
|
95
|
+
},
|
96
|
+
{ title: __('Cron line'), value: recurrence.cron_line },
|
97
|
+
// TODO { title:__('Action') , value: {format_task_input(recurring_logic.tasks.last)} },
|
98
|
+
{ title: __('Last occurrence'), value: recurrence.last_occurrence },
|
99
|
+
{ title: __('Next occurrence'), value: recurrence.next_occurrence },
|
100
|
+
{ title: __('Current iteration'), value: recurrence.iteration },
|
101
|
+
{ title: __('Iteration limit'), value: recurrence.max_iteration },
|
102
|
+
{ title: __('Repeat until'), value: recurrence.end_time },
|
103
|
+
// TODO { title:__('State') , value: {recurring_logic_state(recurring_logic)} },
|
104
|
+
{ title: __('Purpose'), value: recurrence.purpose },
|
105
|
+
{ title: __('Task count'), value: recurrence.task_count },
|
106
|
+
];
|
107
|
+
return (
|
108
|
+
<ExpandableSection
|
109
|
+
toggleText={__('Recurring logic')}
|
110
|
+
onToggle={setIsExpanded}
|
111
|
+
isExpanded={isExpanded}
|
112
|
+
>
|
113
|
+
<DataList isCompact>
|
114
|
+
<ItemsParser items={items} />
|
115
|
+
</DataList>
|
116
|
+
</ExpandableSection>
|
117
|
+
);
|
118
|
+
};
|
119
|
+
const TargetHosts = ({ data }) => {
|
120
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
121
|
+
const {
|
122
|
+
targeting,
|
123
|
+
job_location: location,
|
124
|
+
job_organization: organization,
|
125
|
+
} = data;
|
126
|
+
|
127
|
+
const targetingSelectioning = targeting.bookmark_name
|
128
|
+
? `${__('Bookmark')} ${targeting.bookmark_name}`
|
129
|
+
: __('Manual selection');
|
130
|
+
const items = [
|
131
|
+
{
|
132
|
+
title: __('Organization'),
|
133
|
+
value: true,
|
134
|
+
wrappedValue: organization || __('Any organization'),
|
135
|
+
},
|
136
|
+
{
|
137
|
+
title: __('Location'),
|
138
|
+
value: true,
|
139
|
+
wrappedValue: location || __('Any location'),
|
140
|
+
},
|
141
|
+
{
|
142
|
+
title: __('Execution order'),
|
143
|
+
value: true,
|
144
|
+
wrappedValue: targeting.randomized_ordering
|
145
|
+
? __('Randomized')
|
146
|
+
: __('Alphabetical'),
|
147
|
+
},
|
148
|
+
];
|
149
|
+
return (
|
150
|
+
<ExpandableSection
|
151
|
+
toggleText={__('Target Hosts')}
|
152
|
+
onToggle={setIsExpanded}
|
153
|
+
isExpanded={isExpanded}
|
154
|
+
>
|
155
|
+
<span>{targetingSelectioning}</span>{' '}
|
156
|
+
<span>
|
157
|
+
{__('using ')}
|
158
|
+
<b>{TARGETING_TYPES[targeting.targeting_type].toLowerCase()}</b>
|
159
|
+
</span>
|
160
|
+
<pre>{targeting.search_query}</pre>
|
161
|
+
<DataList isCompact>
|
162
|
+
<ItemsParser items={items} />
|
163
|
+
</DataList>
|
164
|
+
</ExpandableSection>
|
165
|
+
);
|
166
|
+
};
|
167
|
+
const Inputs = ({ data }) => {
|
168
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
169
|
+
const inputs =
|
170
|
+
data?.pattern_template_invocations?.[0]?.template_invocation_input_values;
|
171
|
+
|
172
|
+
if (!inputs) return null;
|
173
|
+
return (
|
174
|
+
<ExpandableSection
|
175
|
+
toggleText={__('User Inputs')}
|
176
|
+
onToggle={setIsExpanded}
|
177
|
+
isExpanded={isExpanded}
|
178
|
+
>
|
179
|
+
<DataList isCompact>
|
180
|
+
<ItemsParser
|
181
|
+
items={inputs.map(({ template_input_name: title, value }) => ({
|
182
|
+
title,
|
183
|
+
value: true,
|
184
|
+
wrappedValue: value,
|
185
|
+
}))}
|
186
|
+
/>
|
187
|
+
</DataList>
|
188
|
+
</ExpandableSection>
|
189
|
+
);
|
190
|
+
};
|
191
|
+
export const JobAdditionInfo = ({ data }) => (
|
192
|
+
<>
|
193
|
+
<Recurring data={data} />
|
194
|
+
<TargetHosts data={data} />
|
195
|
+
<Inputs data={data} />
|
196
|
+
<Schedule data={data} />
|
197
|
+
</>
|
198
|
+
);
|
199
|
+
|
200
|
+
JobAdditionInfo.propTypes = {
|
201
|
+
data: PropTypes.shape({
|
202
|
+
recurrence: PropTypes.object,
|
203
|
+
targeting: PropTypes.object,
|
204
|
+
}).isRequired,
|
205
|
+
};
|
206
|
+
|
207
|
+
Recurring.propTypes = JobAdditionInfo.propTypes;
|
208
|
+
TargetHosts.propTypes = JobAdditionInfo.propTypes;
|
209
|
+
Inputs.propTypes = JobAdditionInfo.propTypes;
|
210
|
+
Schedule.propTypes = JobAdditionInfo.propTypes;
|
211
|
+
|
212
|
+
ItemsParser.propTypes = {
|
213
|
+
items: PropTypes.array.isRequired,
|
214
|
+
};
|
@@ -19,6 +19,15 @@ export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
|
|
19
19
|
export const currentPermissionsUrl = foremanUrl(
|
20
20
|
'/api/v2/permissions/current_permissions'
|
21
21
|
);
|
22
|
+
export const GET_TEMPLATE_INVOCATION = 'GET_TEMPLATE_INVOCATION';
|
23
|
+
export const showTemplateInvocationUrl = (hostID, jobID) =>
|
24
|
+
`/show_template_invocation_by_host/${hostID}/job_invocation/${jobID}`;
|
25
|
+
|
26
|
+
export const templateInvocationPageUrl = (hostID, jobID) =>
|
27
|
+
`/job_invocations_detail/${jobID}/host_invocation/${hostID}`;
|
28
|
+
|
29
|
+
export const jobInvocationDetailsUrl = id =>
|
30
|
+
`/experimental/job_invocations_detail/${id}`;
|
22
31
|
|
23
32
|
export const STATUS = {
|
24
33
|
PENDING: 'pending',
|
@@ -33,6 +42,14 @@ export const STATUS_UPPERCASE = {
|
|
33
42
|
PENDING: 'PENDING',
|
34
43
|
};
|
35
44
|
|
45
|
+
export const STATUS_TITLES = {
|
46
|
+
ALL_STATUSES: { id: 'all_statuses', title: __('All statuses') },
|
47
|
+
SUCCESS: { id: 'success', title: __('Succeeded') },
|
48
|
+
FAILED: { id: 'failed', title: __('Failed') },
|
49
|
+
PENDING: { id: 'pending', title: __('In Progress') },
|
50
|
+
CANCELLED: { id: 'cancelled', title: __('Cancelled') },
|
51
|
+
};
|
52
|
+
|
36
53
|
export const DATE_OPTIONS = {
|
37
54
|
day: 'numeric',
|
38
55
|
month: 'short',
|
@@ -52,7 +69,7 @@ const Columns = () => {
|
|
52
69
|
return { title: __('Failed'), status: 1 };
|
53
70
|
case 'planned':
|
54
71
|
return { title: __('Scheduled'), status: 2 };
|
55
|
-
case 'running':
|
72
|
+
case 'running' || 'pending':
|
56
73
|
return { title: __('Pending'), status: 3 };
|
57
74
|
case 'cancelled':
|
58
75
|
return { title: __('Cancelled'), status: 4 };
|
@@ -65,18 +82,25 @@ const Columns = () => {
|
|
65
82
|
const hostDetailsPageUrl = useForemanHostDetailsPageUrl();
|
66
83
|
|
67
84
|
return {
|
85
|
+
expand: {
|
86
|
+
title: '',
|
87
|
+
weight: 0,
|
88
|
+
wrapper: () => null,
|
89
|
+
},
|
68
90
|
name: {
|
69
91
|
title: __('Name'),
|
70
92
|
wrapper: ({ name }) => (
|
71
93
|
<a href={`${hostDetailsPageUrl}${name}`}>{name}</a>
|
72
94
|
),
|
95
|
+
isSorted: true,
|
73
96
|
weight: 1,
|
74
97
|
},
|
75
|
-
|
98
|
+
hostgroup: {
|
76
99
|
title: __('Host group'),
|
77
100
|
wrapper: ({ hostgroup_id, hostgroup_name }) => (
|
78
101
|
<a href={`/hostgroups/${hostgroup_id}/edit`}>{hostgroup_name}</a>
|
79
102
|
),
|
103
|
+
isSorted: true,
|
80
104
|
weight: 2,
|
81
105
|
},
|
82
106
|
os: {
|
@@ -86,6 +110,7 @@ const Columns = () => {
|
|
86
110
|
{operatingsystem_name}
|
87
111
|
</a>
|
88
112
|
),
|
113
|
+
isSorted: true,
|
89
114
|
weight: 3,
|
90
115
|
},
|
91
116
|
smart_proxy: {
|
@@ -93,6 +118,7 @@ const Columns = () => {
|
|
93
118
|
wrapper: ({ smart_proxy_name, smart_proxy_id }) => (
|
94
119
|
<a href={`/smart_proxies/${smart_proxy_id}`}>{smart_proxy_name}</a>
|
95
120
|
),
|
121
|
+
isSorted: true,
|
96
122
|
weight: 4,
|
97
123
|
},
|
98
124
|
status: {
|
@@ -109,7 +135,19 @@ const Columns = () => {
|
|
109
135
|
},
|
110
136
|
weight: 5,
|
111
137
|
},
|
138
|
+
actions: {
|
139
|
+
title: '',
|
140
|
+
weight: 6,
|
141
|
+
wrapper: () => null,
|
142
|
+
},
|
112
143
|
};
|
113
144
|
};
|
114
145
|
|
115
146
|
export default Columns;
|
147
|
+
|
148
|
+
const STATIC_TYPE = 'static_query';
|
149
|
+
const DYNAMIC_TYPE = 'dynamic_query';
|
150
|
+
export const TARGETING_TYPES = {
|
151
|
+
[STATIC_TYPE]: __('Static Query'),
|
152
|
+
[DYNAMIC_TYPE]: __('Dynamic Query'),
|
153
|
+
};
|
@@ -38,3 +38,73 @@
|
|
38
38
|
height: $chart_size;
|
39
39
|
}
|
40
40
|
}
|
41
|
+
.job-additional-info {
|
42
|
+
padding: 0;
|
43
|
+
margin-bottom: -10px;
|
44
|
+
}
|
45
|
+
.job-details-table-section {
|
46
|
+
section:nth-child(1) {
|
47
|
+
padding: 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
.template-invocation {
|
52
|
+
&.output-in-table-view {
|
53
|
+
div.invocation-output {
|
54
|
+
overflow: auto;
|
55
|
+
max-height: 25em;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
div.invocation-output {
|
59
|
+
display: block;
|
60
|
+
padding: 9.5px;
|
61
|
+
margin: 0 0 10px;
|
62
|
+
font-size: 12px;
|
63
|
+
word-break: break-all;
|
64
|
+
word-wrap: break-word;
|
65
|
+
color: rgba(255, 255, 255, 1);
|
66
|
+
background-color: rgba(47, 47, 47, 1);
|
67
|
+
border: 1px solid #000000;
|
68
|
+
border-radius: 0px;
|
69
|
+
font-family: Menlo, Monaco, Consolas, monospace;
|
70
|
+
|
71
|
+
div.printable {
|
72
|
+
min-height: 50px;
|
73
|
+
}
|
74
|
+
|
75
|
+
div.line.stderr,
|
76
|
+
div.line.error,
|
77
|
+
div.line.debug {
|
78
|
+
color: red;
|
79
|
+
}
|
80
|
+
|
81
|
+
div.line span.counter {
|
82
|
+
float: left;
|
83
|
+
clear: left;
|
84
|
+
}
|
85
|
+
|
86
|
+
div.line div.content {
|
87
|
+
position: relative;
|
88
|
+
margin-left: 50px;
|
89
|
+
white-space: pre-wrap;
|
90
|
+
}
|
91
|
+
|
92
|
+
a {
|
93
|
+
color: #ffffff;
|
94
|
+
}
|
95
|
+
|
96
|
+
a.scroll-link{
|
97
|
+
position: relative;
|
98
|
+
bottom: 10px;
|
99
|
+
float: right;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
.template-invocation-preview {
|
104
|
+
margin-top: 10px;
|
105
|
+
}
|
106
|
+
|
107
|
+
.pf-c-toggle-group {
|
108
|
+
margin-bottom: 10px;
|
109
|
+
}
|
110
|
+
}
|