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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +8 -0
  3. data/app/controllers/template_invocations_controller.rb +57 -0
  4. data/app/controllers/ui_job_wizard_controller.rb +6 -3
  5. data/app/helpers/remote_execution_helper.rb +5 -6
  6. data/app/views/api/v2/job_invocations/base.json.rabl +1 -1
  7. data/app/views/api/v2/job_invocations/hosts.json.rabl +1 -1
  8. data/app/views/api/v2/job_invocations/main.json.rabl +1 -1
  9. data/app/views/api/v2/job_invocations/show.json.rabl +18 -0
  10. data/app/views/templates/script/convert2rhel_analyze.erb +4 -4
  11. data/config/routes.rb +2 -0
  12. data/lib/foreman_remote_execution/engine.rb +3 -3
  13. data/lib/foreman_remote_execution/version.rb +1 -1
  14. data/webpack/JobInvocationDetail/JobAdditionInfo.js +214 -0
  15. data/webpack/JobInvocationDetail/JobInvocationConstants.js +40 -2
  16. data/webpack/JobInvocationDetail/JobInvocationDetail.scss +70 -0
  17. data/webpack/JobInvocationDetail/JobInvocationHostTable.js +177 -80
  18. data/webpack/JobInvocationDetail/JobInvocationHostTableToolbar.js +63 -0
  19. data/webpack/JobInvocationDetail/JobInvocationSelectors.js +8 -1
  20. data/webpack/JobInvocationDetail/JobInvocationSystemStatusChart.js +61 -10
  21. data/webpack/JobInvocationDetail/OpenAlInvocations.js +111 -0
  22. data/webpack/JobInvocationDetail/TemplateInvocation.js +202 -0
  23. data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputCodeBlock.js +124 -0
  24. data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputToggleGroup.js +156 -0
  25. data/webpack/JobInvocationDetail/TemplateInvocationComponents/PreviewTemplate.js +50 -0
  26. data/webpack/JobInvocationDetail/TemplateInvocationComponents/TemplateActionButtons.js +224 -0
  27. data/webpack/JobInvocationDetail/TemplateInvocationPage.js +53 -0
  28. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +1 -1
  29. data/webpack/JobInvocationDetail/__tests__/OpenAlInvocations.test.js +110 -0
  30. data/webpack/JobInvocationDetail/__tests__/OutputCodeBlock.test.js +69 -0
  31. data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +131 -0
  32. data/webpack/JobInvocationDetail/__tests__/fixtures.js +130 -0
  33. data/webpack/JobInvocationDetail/index.js +18 -3
  34. data/webpack/JobWizard/JobWizard.js +38 -16
  35. data/webpack/JobWizard/{StartsBeforeErrorAlert.js → StartsErrorAlert.js} +16 -1
  36. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +1 -1
  37. data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +1 -1
  38. data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +5 -3
  39. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +1 -1
  40. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +3 -3
  41. data/webpack/JobWizard/steps/form/DateTimePicker.js +13 -0
  42. data/webpack/JobWizard/steps/form/Formatter.js +1 -0
  43. data/webpack/JobWizard/steps/form/ResourceSelect.js +34 -9
  44. data/webpack/Routes/routes.js +6 -0
  45. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +1 -0
  46. data/webpack/react_app/components/RegistrationExtension/RexPull.js +27 -2
  47. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +1 -1
  48. metadata +15 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a300266f1fb00313f0bc59f37573802bbcf230fca5e9867bcee11bdc8279a93
4
- data.tar.gz: bc52a77d346cd77c834fd134a872fab19c8b12fc98c30f27deba4a7a64a776b0
3
+ metadata.gz: f4dc9acfc2bf412af59bead68426f726248f020964fa433156bb341eefcee3ec
4
+ data.tar.gz: cf88f8f262a83fb638e855043be9f5f5d9bd7f3c1b365131b1906ace271eb288
5
5
  SHA512:
6
- metadata.gz: ba0a20114b7d30cf6cc7fe236b34794ab98163cc9bbd6d78a02e3573c996b2fb0eaccdf96af081cfc96afb0c8a95af8ad9ec906d2b8e103806ebe6d1d74bc413
7
- data.tar.gz: fe6aa4f48b414729aa0678589b4dc55eab153b5c6505edb21700601d7bb993b4cae2465ccb33eeb24becb12c7b68be45d7a88bea67507b8917b2ad23acf738ba
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
- resource_list = resource_type.constantize.authorized("view_#{resource_type.underscore.pluralize}").all.map { |r| {:name => r.to_s, :id => r.id } }.select { |v| v[:name] =~ /#{params[:name]}/ }
59
- render :json => { :results =>
60
- resource_list.sort_by { |r| r[:name] }.take(100), :subtotal => resource_list.count}
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
- if Setting[:lab_features]
103
- buttons << link_to(_('New UI'), new_job_invocation_detail_path(:id => job_invocation.id),
104
- class: 'btn btn-default',
105
- title: _('Switch to the new job invocation detail UI'))
106
- end
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: true
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: "true\nfalse"
16
- default: "true"
15
+ options: "yes\r\nno"
16
+ default: "no"
17
17
  hidden_value: false
18
18
  %>
19
19
  <%-
20
- els = input('ELS') == "true" ? "--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.13'
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'
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '14.1.4'.freeze
2
+ VERSION = '15.0.0'.freeze
3
3
  end
@@ -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
- groups: {
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
+ }