foreman_remote_execution 14.0.0 → 14.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +77 -77
- data/app/assets/javascripts/foreman_remote_execution/locale/en/foreman_remote_execution.js +2 -1667
- data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +7 -7
- data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +33 -33
- data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +131 -131
- data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +131 -131
- data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +38 -38
- data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +16 -16
- data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +33 -33
- data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +7 -7
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +134 -134
- data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +6 -6
- data/app/controllers/api/v2/job_invocations_controller.rb +34 -17
- data/app/helpers/job_invocations_helper.rb +1 -1
- data/app/helpers/remote_execution_helper.rb +2 -2
- data/app/lib/actions/remote_execution/proxy_action.rb +10 -5
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
- data/app/lib/actions/remote_execution/template_invocation_progress_logging.rb +2 -3
- data/app/models/concerns/foreman_remote_execution/errors_flattener.rb +2 -2
- data/app/views/api/v2/job_invocations/hosts.json.rabl +15 -0
- data/app/views/template_invocations/show.html.erb +1 -1
- data/app/views/templates/script/package_action.erb +1 -1
- data/config/routes.rb +1 -0
- data/db/migrate/20240312133027_extend_template_invocation_events.rb +19 -0
- data/db/migrate/20240522093413_migrate_smart_proxy_ids_to_template_invocations.rb +3 -0
- data/lib/foreman_remote_execution/engine.rb +0 -4
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/locale/Makefile +12 -2
- data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/de/foreman_remote_execution.po +81 -80
- data/locale/en/foreman_remote_execution.po +0 -1675
- data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/en_GB/foreman_remote_execution.po +7 -7
- data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/es/foreman_remote_execution.po +33 -33
- data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/fr/foreman_remote_execution.po +132 -132
- data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ja/foreman_remote_execution.po +131 -131
- data/locale/ka/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ka/foreman_remote_execution.po +38 -38
- data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ko/foreman_remote_execution.po +16 -16
- data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/pt_BR/foreman_remote_execution.po +33 -33
- data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/ru/foreman_remote_execution.po +7 -7
- data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_CN/foreman_remote_execution.po +136 -134
- data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
- data/locale/zh_TW/foreman_remote_execution.po +6 -6
- data/webpack/JobInvocationDetail/JobInvocationActions.js +1 -1
- data/webpack/JobInvocationDetail/JobInvocationConstants.js +84 -0
- data/webpack/JobInvocationDetail/JobInvocationDetail.scss +0 -1
- data/webpack/JobInvocationDetail/JobInvocationHostTable.js +210 -0
- data/webpack/JobInvocationDetail/JobInvocationSelectors.js +2 -2
- data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +5 -1
- data/webpack/JobInvocationDetail/__tests__/fixtures.js +9 -0
- data/webpack/JobInvocationDetail/index.js +56 -34
- data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js +1 -2
- data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +38 -7
- data/webpack/react_app/components/RecentJobsCard/constants.js +4 -0
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +1 -1
- data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +6 -6
- metadata +6 -95
- data/.babelrc.js +0 -3
- data/.eslintignore +0 -3
- data/.eslintrc +0 -13
- data/.github/workflows/js_ci.yml +0 -32
- data/.github/workflows/release.yml +0 -16
- data/.github/workflows/ruby_ci.yml +0 -19
- data/.gitignore +0 -18
- data/.packit.yaml +0 -45
- data/.prettierrc +0 -4
- data/.rubocop.yml +0 -105
- data/.rubocop_todo.yml +0 -516
- data/.tx/config +0 -10
- data/Gemfile +0 -5
- data/app/mailers/.gitkeep +0 -0
- data/app/views/dashboard/.gitkeep +0 -0
- data/foreman_remote_execution.gemspec +0 -33
- data/jsconfig.json +0 -8
- data/locale/action_names.rb +0 -2
- data/test/benchmark/run_hosts_job_benchmark.rb +0 -70
- data/test/benchmark/targeting_benchmark.rb +0 -31
- data/test/factories/foreman_remote_execution_factories.rb +0 -147
- data/test/functional/api/v2/foreign_input_sets_controller_test.rb +0 -58
- data/test/functional/api/v2/job_invocations_controller_test.rb +0 -446
- data/test/functional/api/v2/job_templates_controller_test.rb +0 -110
- data/test/functional/api/v2/registration_controller_test.rb +0 -73
- data/test/functional/api/v2/remote_execution_features_controller_test.rb +0 -34
- data/test/functional/api/v2/template_invocations_controller_test.rb +0 -33
- data/test/functional/cockpit_controller_test.rb +0 -16
- data/test/functional/job_invocations_controller_test.rb +0 -132
- data/test/functional/job_templates_controller_test.rb +0 -31
- data/test/functional/ui_job_wizard_controller_test.rb +0 -16
- data/test/graphql/mutations/job_invocations/create_test.rb +0 -58
- data/test/graphql/queries/job_invocation_query_test.rb +0 -31
- data/test/graphql/queries/job_invocations_query_test.rb +0 -35
- data/test/helpers/remote_execution_helper_test.rb +0 -46
- data/test/support/remote_execution_helper.rb +0 -5
- data/test/test_plugin_helper.rb +0 -9
- data/test/unit/actions/run_host_job_test.rb +0 -115
- data/test/unit/actions/run_hosts_job_test.rb +0 -214
- data/test/unit/api_params_test.rb +0 -25
- data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +0 -29
- data/test/unit/concerns/host_extensions_test.rb +0 -219
- data/test/unit/concerns/nic_extensions_test.rb +0 -9
- data/test/unit/execution_task_status_mapper_test.rb +0 -92
- data/test/unit/input_template_renderer_test.rb +0 -503
- data/test/unit/job_invocation_composer_test.rb +0 -974
- data/test/unit/job_invocation_report_template_test.rb +0 -60
- data/test/unit/job_invocation_test.rb +0 -232
- data/test/unit/job_template_effective_user_test.rb +0 -37
- data/test/unit/job_template_test.rb +0 -316
- data/test/unit/remote_execution_feature_test.rb +0 -86
- data/test/unit/remote_execution_provider_test.rb +0 -298
- data/test/unit/renderer_scope_input_test.rb +0 -49
- data/test/unit/targeting_test.rb +0 -206
- data/test/unit/template_invocation_input_value_test.rb +0 -38
Binary file
|
@@ -5,11 +5,11 @@
|
|
5
5
|
# Translators:
|
6
6
|
msgid ""
|
7
7
|
msgstr ""
|
8
|
-
"Project-Id-Version: foreman_remote_execution
|
8
|
+
"Project-Id-Version: foreman_remote_execution 14.0.0\n"
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
10
10
|
"PO-Revision-Date: 2016-02-15 13:54+0000\n"
|
11
11
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
12
|
-
"Language-Team: Chinese (Taiwan) (http://
|
12
|
+
"Language-Team: Chinese (Taiwan) (http://app.transifex.com/foreman/foreman/lang"
|
13
13
|
"uage/zh_TW/)\n"
|
14
14
|
"MIME-Version: 1.0\n"
|
15
15
|
"Content-Type: text/plain; charset=UTF-8\n"
|
@@ -86,7 +86,7 @@ msgid "Abort Job"
|
|
86
86
|
msgstr ""
|
87
87
|
|
88
88
|
msgid "Access denied"
|
89
|
-
msgstr ""
|
89
|
+
msgstr "拒絕存取"
|
90
90
|
|
91
91
|
msgid "Actions"
|
92
92
|
msgstr "動作"
|
@@ -137,7 +137,7 @@ msgid "Awaiting start"
|
|
137
137
|
msgstr ""
|
138
138
|
|
139
139
|
msgid "Back"
|
140
|
-
msgstr ""
|
140
|
+
msgstr "上一步"
|
141
141
|
|
142
142
|
msgid "Back to Job"
|
143
143
|
msgstr "回到工作"
|
@@ -776,7 +776,7 @@ msgid "New UI"
|
|
776
776
|
msgstr ""
|
777
777
|
|
778
778
|
msgid "Next"
|
779
|
-
msgstr ""
|
779
|
+
msgstr "下一步"
|
780
780
|
|
781
781
|
msgid "No (override)"
|
782
782
|
msgstr ""
|
@@ -1555,7 +1555,7 @@ msgid "You are not allowed to see the currently assigned template. Saving the fo
|
|
1555
1555
|
msgstr "您不能看到目前指定的範本。現在儲存表單會取消指定範本。"
|
1556
1556
|
|
1557
1557
|
msgid "You are not authorized to perform this action."
|
1558
|
-
msgstr ""
|
1558
|
+
msgstr "您未經許可執行這項動作。"
|
1559
1559
|
|
1560
1560
|
msgid "You have %s results to display. Showing first %s results"
|
1561
1561
|
msgstr ""
|
@@ -1,14 +1,21 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
2
|
+
import React from 'react';
|
1
3
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import { useForemanHostDetailsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
|
6
|
+
import JobStatusIcon from '../react_app/components/RecentJobsCard/JobStatusIcon';
|
2
7
|
|
3
8
|
export const JOB_INVOCATION_KEY = 'JOB_INVOCATION_KEY';
|
4
9
|
export const CURRENT_PERMISSIONS = 'CURRENT_PERMISSIONS';
|
5
10
|
export const UPDATE_JOB = 'UPDATE_JOB';
|
6
11
|
export const CANCEL_JOB = 'CANCEL_JOB';
|
7
12
|
export const GET_TASK = 'GET_TASK';
|
13
|
+
export const GET_TEMPLATE_INVOCATIONS = 'GET_TEMPLATE_INVOCATIONS';
|
8
14
|
export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
|
9
15
|
export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
|
10
16
|
export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
|
11
17
|
export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
|
18
|
+
export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
|
12
19
|
export const currentPermissionsUrl = foremanUrl(
|
13
20
|
'/api/v2/permissions/current_permissions'
|
14
21
|
);
|
@@ -20,6 +27,12 @@ export const STATUS = {
|
|
20
27
|
CANCELLED: 'cancelled',
|
21
28
|
};
|
22
29
|
|
30
|
+
export const STATUS_UPPERCASE = {
|
31
|
+
RESOLVED: 'RESOLVED',
|
32
|
+
ERROR: 'ERROR',
|
33
|
+
PENDING: 'PENDING',
|
34
|
+
};
|
35
|
+
|
23
36
|
export const DATE_OPTIONS = {
|
24
37
|
day: 'numeric',
|
25
38
|
month: 'short',
|
@@ -29,3 +42,74 @@ export const DATE_OPTIONS = {
|
|
29
42
|
hour12: false,
|
30
43
|
timeZoneName: 'short',
|
31
44
|
};
|
45
|
+
|
46
|
+
const Columns = () => {
|
47
|
+
const getColumnsStatus = ({ hostJobStatus }) => {
|
48
|
+
switch (hostJobStatus) {
|
49
|
+
case 'success':
|
50
|
+
return { title: __('Succeeded'), status: 0 };
|
51
|
+
case 'error':
|
52
|
+
return { title: __('Failed'), status: 1 };
|
53
|
+
case 'planned':
|
54
|
+
return { title: __('Scheduled'), status: 2 };
|
55
|
+
case 'running':
|
56
|
+
return { title: __('Pending'), status: 3 };
|
57
|
+
case 'cancelled':
|
58
|
+
return { title: __('Cancelled'), status: 4 };
|
59
|
+
case 'N/A':
|
60
|
+
return { title: __('Awaiting start'), status: 5 };
|
61
|
+
default:
|
62
|
+
return { title: hostJobStatus, status: 6 };
|
63
|
+
}
|
64
|
+
};
|
65
|
+
const hostDetailsPageUrl = useForemanHostDetailsPageUrl();
|
66
|
+
|
67
|
+
return {
|
68
|
+
name: {
|
69
|
+
title: __('Name'),
|
70
|
+
wrapper: ({ name }) => (
|
71
|
+
<a href={`${hostDetailsPageUrl}${name}`}>{name}</a>
|
72
|
+
),
|
73
|
+
weight: 1,
|
74
|
+
},
|
75
|
+
groups: {
|
76
|
+
title: __('Host group'),
|
77
|
+
wrapper: ({ hostgroup_id, hostgroup_name }) => (
|
78
|
+
<a href={`/hostgroups/${hostgroup_id}/edit`}>{hostgroup_name}</a>
|
79
|
+
),
|
80
|
+
weight: 2,
|
81
|
+
},
|
82
|
+
os: {
|
83
|
+
title: __('OS'),
|
84
|
+
wrapper: ({ operatingsystem_id, operatingsystem_name }) => (
|
85
|
+
<a href={`/operatingsystems/${operatingsystem_id}/edit`}>
|
86
|
+
{operatingsystem_name}
|
87
|
+
</a>
|
88
|
+
),
|
89
|
+
weight: 3,
|
90
|
+
},
|
91
|
+
smart_proxy: {
|
92
|
+
title: __('Smart proxy'),
|
93
|
+
wrapper: ({ smart_proxy_name, smart_proxy_id }) => (
|
94
|
+
<a href={`/smart_proxies/${smart_proxy_id}`}>{smart_proxy_name}</a>
|
95
|
+
),
|
96
|
+
weight: 4,
|
97
|
+
},
|
98
|
+
status: {
|
99
|
+
title: __('Status'),
|
100
|
+
wrapper: ({ job_status }) => {
|
101
|
+
const { title, status } = getColumnsStatus({
|
102
|
+
hostJobStatus: job_status,
|
103
|
+
});
|
104
|
+
return (
|
105
|
+
<JobStatusIcon status={status}>
|
106
|
+
{title || __('Unknown')}
|
107
|
+
</JobStatusIcon>
|
108
|
+
);
|
109
|
+
},
|
110
|
+
weight: 5,
|
111
|
+
},
|
112
|
+
};
|
113
|
+
};
|
114
|
+
|
115
|
+
export default Columns;
|
@@ -0,0 +1,210 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import React, { useMemo, useEffect } from 'react';
|
4
|
+
import { Icon } from 'patternfly-react';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { FormattedMessage } from 'react-intl';
|
7
|
+
import { Tr, Td } from '@patternfly/react-table';
|
8
|
+
import {
|
9
|
+
Title,
|
10
|
+
EmptyState,
|
11
|
+
EmptyStateVariant,
|
12
|
+
EmptyStateBody,
|
13
|
+
} from '@patternfly/react-core';
|
14
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
15
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
16
|
+
import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
|
17
|
+
import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
|
18
|
+
import { useSetParamsAndApiAndSearch } from 'foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks';
|
19
|
+
import {
|
20
|
+
useBulkSelect,
|
21
|
+
useUrlParams,
|
22
|
+
} from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
|
23
|
+
import Pagination from 'foremanReact/components/Pagination';
|
24
|
+
import { getControllerSearchProps } from 'foremanReact/constants';
|
25
|
+
import Columns, {
|
26
|
+
JOB_INVOCATION_HOSTS,
|
27
|
+
STATUS_UPPERCASE,
|
28
|
+
} from './JobInvocationConstants';
|
29
|
+
|
30
|
+
const JobInvocationHostTable = ({ id, targeting, finished, autoRefresh }) => {
|
31
|
+
const columns = Columns();
|
32
|
+
const columnNamesKeys = Object.keys(columns);
|
33
|
+
const apiOptions = { key: JOB_INVOCATION_HOSTS };
|
34
|
+
const {
|
35
|
+
searchParam: urlSearchQuery = '',
|
36
|
+
page: urlPage,
|
37
|
+
per_page: urlPerPage,
|
38
|
+
} = useUrlParams();
|
39
|
+
const defaultParams = { search: urlSearchQuery };
|
40
|
+
if (urlPage) defaultParams.page = Number(urlPage);
|
41
|
+
if (urlPerPage) defaultParams.per_page = Number(urlPerPage);
|
42
|
+
const { response, status, setAPIOptions } = useAPI(
|
43
|
+
'get',
|
44
|
+
`/api/job_invocations/${id}/hosts`,
|
45
|
+
{
|
46
|
+
params: { ...defaultParams, key: JOB_INVOCATION_HOSTS },
|
47
|
+
}
|
48
|
+
);
|
49
|
+
|
50
|
+
const combinedResponse = {
|
51
|
+
response: {
|
52
|
+
search: urlSearchQuery,
|
53
|
+
can_create: false,
|
54
|
+
results: response?.results || [],
|
55
|
+
total: response?.total || 0,
|
56
|
+
per_page: response?.perPage,
|
57
|
+
page: response?.page,
|
58
|
+
subtotal: response?.subtotal || 0,
|
59
|
+
message: response?.message || 'error',
|
60
|
+
},
|
61
|
+
status,
|
62
|
+
setAPIOptions,
|
63
|
+
};
|
64
|
+
|
65
|
+
const { setParamsAndAPI, params } = useSetParamsAndApiAndSearch({
|
66
|
+
defaultParams,
|
67
|
+
apiOptions,
|
68
|
+
setAPIOptions: combinedResponse.setAPIOptions,
|
69
|
+
});
|
70
|
+
|
71
|
+
const { updateSearchQuery } = useBulkSelect({
|
72
|
+
initialSearchQuery: urlSearchQuery,
|
73
|
+
});
|
74
|
+
|
75
|
+
const controller = 'hosts';
|
76
|
+
const memoDefaultSearchProps = useMemo(
|
77
|
+
() => getControllerSearchProps(controller),
|
78
|
+
[controller]
|
79
|
+
);
|
80
|
+
memoDefaultSearchProps.autocomplete.url = foremanUrl(
|
81
|
+
`/${controller}/auto_complete_search`
|
82
|
+
);
|
83
|
+
|
84
|
+
useEffect(() => {
|
85
|
+
const intervalId = setInterval(() => {
|
86
|
+
if (!finished || autoRefresh) {
|
87
|
+
setAPIOptions(prevOptions => ({
|
88
|
+
...prevOptions,
|
89
|
+
params: {
|
90
|
+
...prevOptions.params,
|
91
|
+
},
|
92
|
+
}));
|
93
|
+
}
|
94
|
+
}, 5000);
|
95
|
+
|
96
|
+
return () => {
|
97
|
+
clearInterval(intervalId);
|
98
|
+
};
|
99
|
+
}, [finished, autoRefresh, setAPIOptions]);
|
100
|
+
|
101
|
+
const onPagination = newPagination => {
|
102
|
+
setParamsAndAPI({
|
103
|
+
...params,
|
104
|
+
...newPagination,
|
105
|
+
search: urlSearchQuery,
|
106
|
+
});
|
107
|
+
};
|
108
|
+
|
109
|
+
const bottomPagination = (
|
110
|
+
<Pagination
|
111
|
+
ouiaId="table-hosts-bottom-pagination"
|
112
|
+
key="table-bottom-pagination"
|
113
|
+
page={params.page}
|
114
|
+
perPage={params.perPage}
|
115
|
+
itemCount={response?.subtotal}
|
116
|
+
onChange={onPagination}
|
117
|
+
/>
|
118
|
+
);
|
119
|
+
|
120
|
+
const customEmptyState = (
|
121
|
+
<Tr ouiaId="table-empty">
|
122
|
+
<Td colSpan={100}>
|
123
|
+
<EmptyState variant={EmptyStateVariant.xl}>
|
124
|
+
<span className="empty-state-icon">
|
125
|
+
<Icon name="add-circle-o" type="pf" size="2x" />
|
126
|
+
</span>
|
127
|
+
<Title ouiaId="empty-state-header" headingLevel="h5" size="4xl">
|
128
|
+
{__('No Results')}
|
129
|
+
</Title>
|
130
|
+
<EmptyStateBody>
|
131
|
+
<div className="empty-state-description">
|
132
|
+
{targeting?.targeting_type === 'dynamic_query' ? (
|
133
|
+
<FormattedMessage
|
134
|
+
id="view-dynamic-hosts"
|
135
|
+
defaultMessage={__(
|
136
|
+
'The dynamic query is still being processed. You can {viewTheHosts} targeted by the query.'
|
137
|
+
)}
|
138
|
+
values={{
|
139
|
+
viewTheHosts: (
|
140
|
+
<a href={`/new/hosts?search=${targeting?.search_query}`}>
|
141
|
+
{__('view the hosts')}
|
142
|
+
</a>
|
143
|
+
),
|
144
|
+
}}
|
145
|
+
/>
|
146
|
+
) : (
|
147
|
+
__('No hosts found')
|
148
|
+
)}
|
149
|
+
</div>
|
150
|
+
</EmptyStateBody>
|
151
|
+
</EmptyState>
|
152
|
+
</Td>
|
153
|
+
</Tr>
|
154
|
+
);
|
155
|
+
|
156
|
+
return (
|
157
|
+
<TableIndexPage
|
158
|
+
apiUrl=""
|
159
|
+
apiOptions={apiOptions}
|
160
|
+
customSearchProps={memoDefaultSearchProps}
|
161
|
+
controller="hosts"
|
162
|
+
creatable={false}
|
163
|
+
replacementResponse={combinedResponse}
|
164
|
+
updateSearchQuery={updateSearchQuery}
|
165
|
+
>
|
166
|
+
<Table
|
167
|
+
ouiaId="job-invocation-hosts-table"
|
168
|
+
columns={columns}
|
169
|
+
customEmptyState={
|
170
|
+
status === STATUS_UPPERCASE.RESOLVED && !response?.results?.length
|
171
|
+
? customEmptyState
|
172
|
+
: null
|
173
|
+
}
|
174
|
+
params={params}
|
175
|
+
setParams={setParamsAndAPI}
|
176
|
+
itemCount={response?.subtotal}
|
177
|
+
results={response?.results}
|
178
|
+
url=""
|
179
|
+
refreshData={() => {}}
|
180
|
+
errorMessage={
|
181
|
+
status === STATUS_UPPERCASE.ERROR && response?.message
|
182
|
+
? response.message
|
183
|
+
: null
|
184
|
+
}
|
185
|
+
isPending={status === STATUS_UPPERCASE.PENDING}
|
186
|
+
isDeleteable={false}
|
187
|
+
bottomPagination={bottomPagination}
|
188
|
+
>
|
189
|
+
{response?.results?.map((result, rowIndex) => (
|
190
|
+
<Tr key={rowIndex} ouiaId={`table-row-${rowIndex}`}>
|
191
|
+
{columnNamesKeys.map(k => (
|
192
|
+
<Td key={k}>{columns[k].wrapper(result)}</Td>
|
193
|
+
))}
|
194
|
+
</Tr>
|
195
|
+
))}
|
196
|
+
</Table>
|
197
|
+
</TableIndexPage>
|
198
|
+
);
|
199
|
+
};
|
200
|
+
|
201
|
+
JobInvocationHostTable.propTypes = {
|
202
|
+
id: PropTypes.string.isRequired,
|
203
|
+
targeting: PropTypes.object.isRequired,
|
204
|
+
finished: PropTypes.bool.isRequired,
|
205
|
+
autoRefresh: PropTypes.bool.isRequired,
|
206
|
+
};
|
207
|
+
|
208
|
+
JobInvocationHostTable.defaultProps = {};
|
209
|
+
|
210
|
+
export default JobInvocationHostTable;
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
|
2
|
-
import { JOB_INVOCATION_KEY } from './JobInvocationConstants';
|
2
|
+
import { JOB_INVOCATION_KEY, GET_TASK } from './JobInvocationConstants';
|
3
3
|
|
4
4
|
export const selectItems = state =>
|
5
5
|
selectAPIResponse(state, JOB_INVOCATION_KEY);
|
6
6
|
|
7
|
-
export const selectTask = state => selectAPIResponse(state,
|
7
|
+
export const selectTask = state => selectAPIResponse(state, GET_TASK);
|
8
8
|
|
9
9
|
export const selectTaskCancelable = state =>
|
10
10
|
selectTask(state).available_actions?.cancellable || false;
|
@@ -76,6 +76,10 @@ api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
|
76
76
|
return { type: 'get', ...action };
|
77
77
|
});
|
78
78
|
|
79
|
+
jest.mock('../JobInvocationHostTable.js', () => () => (
|
80
|
+
<div data-testid="mock-table">Mock Table</div>
|
81
|
+
));
|
82
|
+
|
79
83
|
const reportTemplateJobId = mockReportTemplatesResponse.results[0].id;
|
80
84
|
|
81
85
|
const mockStore = configureMockStore([thunk]);
|
@@ -207,7 +211,7 @@ describe('JobInvocationDetailPage', () => {
|
|
207
211
|
{ key: GET_REPORT_TEMPLATES, url: '/api/report_templates' },
|
208
212
|
{
|
209
213
|
key: JOB_INVOCATION_KEY,
|
210
|
-
url: `/api/job_invocations/${jobId}`,
|
214
|
+
url: `/api/job_invocations/${jobId}?host_status=true`,
|
211
215
|
},
|
212
216
|
{
|
213
217
|
key: GET_REPORT_TEMPLATE_INPUTS,
|
@@ -1,4 +1,7 @@
|
|
1
1
|
export const jobInvocationData = {
|
2
|
+
search: '',
|
3
|
+
per_page: 20,
|
4
|
+
page: 1,
|
2
5
|
id: 123,
|
3
6
|
description: 'Description',
|
4
7
|
job_category: 'Commands',
|
@@ -40,6 +43,9 @@ export const jobInvocationData = {
|
|
40
43
|
};
|
41
44
|
|
42
45
|
export const jobInvocationDataScheduled = {
|
46
|
+
search: '',
|
47
|
+
per_page: 20,
|
48
|
+
page: 1,
|
43
49
|
id: 456,
|
44
50
|
description: 'Description',
|
45
51
|
job_category: 'Commands',
|
@@ -62,6 +68,9 @@ export const jobInvocationDataScheduled = {
|
|
62
68
|
};
|
63
69
|
|
64
70
|
export const jobInvocationDataRecurring = {
|
71
|
+
search: '',
|
72
|
+
per_page: 20,
|
73
|
+
page: 1,
|
65
74
|
id: 789,
|
66
75
|
description: 'Description',
|
67
76
|
job_category: 'Commands',
|
@@ -1,12 +1,17 @@
|
|
1
1
|
import PropTypes from 'prop-types';
|
2
2
|
import React, { useEffect } from 'react';
|
3
3
|
import { useDispatch, useSelector } from 'react-redux';
|
4
|
-
import {
|
4
|
+
import {
|
5
|
+
Divider,
|
6
|
+
Flex,
|
7
|
+
PageSection,
|
8
|
+
PageSectionVariants,
|
9
|
+
} from '@patternfly/react-core';
|
5
10
|
import { translate as __, documentLocale } from 'foremanReact/common/I18n';
|
6
11
|
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
7
12
|
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
8
13
|
import { stopInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
|
9
|
-
import {
|
14
|
+
import { getJobInvocation, getTask } from './JobInvocationActions';
|
10
15
|
import {
|
11
16
|
CURRENT_PERMISSIONS,
|
12
17
|
DATE_OPTIONS,
|
@@ -19,6 +24,7 @@ import JobInvocationOverview from './JobInvocationOverview';
|
|
19
24
|
import { selectItems } from './JobInvocationSelectors';
|
20
25
|
import JobInvocationSystemStatusChart from './JobInvocationSystemStatusChart';
|
21
26
|
import JobInvocationToolbarButtons from './JobInvocationToolbarButtons';
|
27
|
+
import JobInvocationHostTable from './JobInvocationHostTable';
|
22
28
|
|
23
29
|
const JobInvocationDetailPage = ({
|
24
30
|
match: {
|
@@ -32,6 +38,7 @@ const JobInvocationDetailPage = ({
|
|
32
38
|
status_label: statusLabel,
|
33
39
|
task,
|
34
40
|
start_at: startAt,
|
41
|
+
targeting,
|
35
42
|
} = items;
|
36
43
|
const finished =
|
37
44
|
statusLabel === STATUS.FAILED ||
|
@@ -57,7 +64,7 @@ const JobInvocationDetailPage = ({
|
|
57
64
|
}
|
58
65
|
|
59
66
|
useEffect(() => {
|
60
|
-
dispatch(
|
67
|
+
dispatch(getJobInvocation(`/api/job_invocations/${id}?host_status=true`));
|
61
68
|
if (finished && !autoRefresh) {
|
62
69
|
dispatch(stopInterval(JOB_INVOCATION_KEY));
|
63
70
|
}
|
@@ -82,45 +89,60 @@ const JobInvocationDetailPage = ({
|
|
82
89
|
};
|
83
90
|
|
84
91
|
return (
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
<Flex
|
99
|
-
className="job-invocation-detail-flex"
|
100
|
-
alignItems={{ default: 'alignItemsFlexStart' }}
|
92
|
+
<>
|
93
|
+
<PageLayout
|
94
|
+
header={description}
|
95
|
+
breadcrumbOptions={breadcrumbOptions}
|
96
|
+
toolbarButtons={
|
97
|
+
<JobInvocationToolbarButtons
|
98
|
+
jobId={id}
|
99
|
+
data={items}
|
100
|
+
currentPermissions={response.results}
|
101
|
+
permissionsStatus={status}
|
102
|
+
/>
|
103
|
+
}
|
104
|
+
searchable={false}
|
101
105
|
>
|
102
|
-
<JobInvocationSystemStatusChart
|
103
|
-
data={items}
|
104
|
-
isAlreadyStarted={isAlreadyStarted}
|
105
|
-
formattedStartDate={formattedStartDate}
|
106
|
-
/>
|
107
|
-
<Divider
|
108
|
-
orientation={{
|
109
|
-
default: 'vertical',
|
110
|
-
}}
|
111
|
-
/>
|
112
106
|
<Flex
|
113
|
-
className="job-
|
114
|
-
alignItems={{ default: '
|
107
|
+
className="job-invocation-detail-flex"
|
108
|
+
alignItems={{ default: 'alignItemsFlexStart' }}
|
115
109
|
>
|
116
|
-
<
|
110
|
+
<JobInvocationSystemStatusChart
|
117
111
|
data={items}
|
118
112
|
isAlreadyStarted={isAlreadyStarted}
|
119
113
|
formattedStartDate={formattedStartDate}
|
120
114
|
/>
|
115
|
+
<Divider
|
116
|
+
orientation={{
|
117
|
+
default: 'vertical',
|
118
|
+
}}
|
119
|
+
/>
|
120
|
+
<Flex
|
121
|
+
className="job-overview"
|
122
|
+
alignItems={{ default: 'alignItemsCenter' }}
|
123
|
+
>
|
124
|
+
<JobInvocationOverview
|
125
|
+
data={items}
|
126
|
+
isAlreadyStarted={isAlreadyStarted}
|
127
|
+
formattedStartDate={formattedStartDate}
|
128
|
+
/>
|
129
|
+
</Flex>
|
121
130
|
</Flex>
|
122
|
-
</
|
123
|
-
|
131
|
+
</PageLayout>
|
132
|
+
<PageSection
|
133
|
+
variant={PageSectionVariants.light}
|
134
|
+
className="table-section"
|
135
|
+
>
|
136
|
+
{items.id !== undefined && (
|
137
|
+
<JobInvocationHostTable
|
138
|
+
id={id}
|
139
|
+
targeting={targeting}
|
140
|
+
finished={finished}
|
141
|
+
autoRefresh={autoRefresh}
|
142
|
+
/>
|
143
|
+
)}
|
144
|
+
</PageSection>
|
145
|
+
</>
|
124
146
|
);
|
125
147
|
};
|
126
148
|
|
data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { translate as __ } from '../../../common/I18n';
|
3
2
|
|
4
3
|
const DefaultLoaderEmptyState = () => (
|
5
|
-
<span className="disabled-text">
|
4
|
+
<span className="disabled-text">Not available</span>
|
6
5
|
);
|
7
6
|
|
8
7
|
export default DefaultLoaderEmptyState;
|
@@ -3,29 +3,60 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import {
|
4
4
|
CheckCircleIcon,
|
5
5
|
ExclamationCircleIcon,
|
6
|
+
BuildIcon,
|
7
|
+
RunningIcon,
|
8
|
+
ExclamationTriangleIcon,
|
6
9
|
QuestionCircleIcon,
|
7
10
|
} from '@patternfly/react-icons';
|
8
|
-
import {
|
11
|
+
import {
|
12
|
+
JOB_SUCCESS_STATUS,
|
13
|
+
JOB_ERROR_STATUS,
|
14
|
+
JOB_PLANNED_STATUS,
|
15
|
+
JOB_RUNNING_STATUS,
|
16
|
+
JOB_CANCELLED_STATUS,
|
17
|
+
JOB_AWAITING_STATUS,
|
18
|
+
} from './constants';
|
9
19
|
import './styles.scss';
|
10
20
|
|
11
21
|
const JobStatusIcon = ({ status, children, ...props }) => {
|
12
22
|
switch (status) {
|
13
23
|
case JOB_SUCCESS_STATUS:
|
14
24
|
return (
|
15
|
-
<span
|
16
|
-
<CheckCircleIcon {...props} /> {children}
|
25
|
+
<span>
|
26
|
+
<CheckCircleIcon className="job-success" {...props} /> {children}
|
17
27
|
</span>
|
18
28
|
);
|
19
29
|
case JOB_ERROR_STATUS:
|
20
30
|
return (
|
21
|
-
<span
|
22
|
-
<ExclamationCircleIcon {...props} /> {children}
|
31
|
+
<span>
|
32
|
+
<ExclamationCircleIcon className="job-error" {...props} /> {children}
|
33
|
+
</span>
|
34
|
+
);
|
35
|
+
case JOB_PLANNED_STATUS:
|
36
|
+
return (
|
37
|
+
<span>
|
38
|
+
<BuildIcon className="job-planned" {...props} /> {children}
|
39
|
+
</span>
|
40
|
+
);
|
41
|
+
case JOB_RUNNING_STATUS:
|
42
|
+
return (
|
43
|
+
<span>
|
44
|
+
<RunningIcon className="job-running" {...props} /> {children}
|
45
|
+
</span>
|
46
|
+
);
|
47
|
+
case JOB_CANCELLED_STATUS:
|
48
|
+
return (
|
49
|
+
<span>
|
50
|
+
<ExclamationTriangleIcon className="job-cancelled" {...props} />{' '}
|
51
|
+
{children}
|
23
52
|
</span>
|
24
53
|
);
|
54
|
+
case JOB_AWAITING_STATUS:
|
55
|
+
return <span className="job-awaiting_start">{children}</span>;
|
25
56
|
default:
|
26
57
|
return (
|
27
|
-
<span
|
28
|
-
<QuestionCircleIcon {...props} /> {children}
|
58
|
+
<span>
|
59
|
+
<QuestionCircleIcon className="job-unknown" {...props} /> {children}
|
29
60
|
</span>
|
30
61
|
);
|
31
62
|
}
|
@@ -5,6 +5,10 @@ export const SCHEDULED_TAB = 2;
|
|
5
5
|
|
6
6
|
export const JOB_SUCCESS_STATUS = 0;
|
7
7
|
export const JOB_ERROR_STATUS = 1;
|
8
|
+
export const JOB_PLANNED_STATUS = 2;
|
9
|
+
export const JOB_RUNNING_STATUS = 3;
|
10
|
+
export const JOB_CANCELLED_STATUS = 4;
|
11
|
+
export const JOB_AWAITING_STATUS = 5;
|
8
12
|
|
9
13
|
export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
|
10
14
|
export const JOB_API_URL =
|