foreman_remote_execution 14.1.4 → 15.0.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 +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
|
+
}
|