diff --git a/app/controllers/api/v2/job_invocations_controller.rb b/app/controllers/api/v2/job_invocations_controller.rb
index a27b9464b..ea8404b8c 100644
--- a/app/controllers/api/v2/job_invocations_controller.rb
+++ b/app/controllers/api/v2/job_invocations_controller.rb
@@ -3,10 +3,11 @@ module V2
class JobInvocationsController < ::Api::V2::BaseController
include ::Api::Version2
include ::Foreman::Renderer
+ include RemoteExecutionHelper
before_action :find_optional_nested_object, :only => %w{output raw_output}
before_action :find_host, :only => %w{output raw_output}
- before_action :find_resource, :only => %w{show update destroy clone cancel rerun outputs}
+ before_action :find_resource, :only => %w{show update destroy clone cancel rerun outputs hosts}
wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])
@@ -20,14 +21,9 @@ def index
param :id, :identifier, :required => true
param :host_status, :bool, required: false, desc: N_('Show Job status for the hosts')
def show
- @hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
- @template_invocations = @job_invocation.template_invocations
- .where(host: @hosts)
- .includes(:input_values)
-
+ set_hosts_and_template_invocations
if params[:host_status] == 'true'
- template_invocations = @template_invocations.includes(:run_host_job_task).to_a
- @host_statuses = Hash[template_invocations.map { |ti| [ti.host_id, template_invocation_status(ti)] }]
+ set_statuses_and_smart_proxies
end
end
@@ -111,6 +107,19 @@ def output
render :json => host_output(@nested_obj, @host, :default => [], :since => params[:since])
end
+ api :GET, '/job_invocations/:id/hosts', N_('List hosts belonging to job invocation')
+ param_group :search_and_pagination, ::Api::V2::BaseController
+ add_scoped_search_description_for(JobInvocation)
+ param :id, :identifier, :required => true
+ def hosts
+ set_hosts_and_template_invocations
+ set_statuses_and_smart_proxies
+ @total = @job_invocation.targeting.hosts.size
+ @hosts = @hosts.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page])
+ @subtotal = @hosts.respond_to?(:total_entries) ? @hosts.total_entries : @hosts.sizes
+ render :hosts, :layout => 'api/v2/layouts/index_layout'
+ end
+
api :GET, '/job_invocations/:id/hosts/:host_id/raw', N_('Get raw output for a host')
param :id, :identifier, :required => true
param :host_id, :identifier, :required => true
@@ -187,7 +196,7 @@ def allowed_nested_id
def action_permission
case params[:action]
- when 'output', 'raw_output', 'outputs'
+ when 'output', 'raw_output', 'outputs', 'hosts'
:view
when 'cancel'
:cancel
@@ -256,15 +265,23 @@ def parent_scope
resource_class.where(nil)
end
- def template_invocation_status(template_invocation)
- task = template_invocation.try(:run_host_job_task)
- parent_task = @job_invocation.task
-
- return(parent_task.result == 'cancelled' ? 'cancelled' : 'N/A') if task.nil?
- return task.state if task.state == 'running' || task.state == 'planned'
- return 'error' if task.result == 'warning'
+ def set_hosts_and_template_invocations
+ @hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
+ @template_invocations = @job_invocation.template_invocations
+ .where(host: @hosts)
+ .includes(:input_values)
+ end
- task.result
+ def set_statuses_and_smart_proxies
+ template_invocations = @template_invocations.includes(:run_host_job_task).to_a
+ hosts = @hosts.to_a
+ @host_statuses = Hash[hosts.map do |host|
+ template_invocation = template_invocations.find { |ti| ti.host_id == host.id }
+ task = template_invocation.try(:run_host_job_task)
+ [host.id, template_invocation_status(task, @job_invocation.task)]
+ end]
+ @smart_proxy_id = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_id] }]
+ @smart_proxy_name = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_name] }]
end
end
end
diff --git a/app/helpers/remote_execution_helper.rb b/app/helpers/remote_execution_helper.rb
index 1af5b47f3..0ba0f4abb 100644
--- a/app/helpers/remote_execution_helper.rb
+++ b/app/helpers/remote_execution_helper.rb
@@ -18,9 +18,9 @@ def host_counter(label, count)
end
def template_invocation_status(task, parent_task)
- return(parent_task.result == 'cancelled' ? _('cancelled') : _('Awaiting start')) if task.nil?
+ return(parent_task.result == 'cancelled' ? 'cancelled' : 'N/A') if task.nil?
return task.state if task.state == 'running' || task.state == 'planned'
- return _('error') if task.result == 'warning'
+ return 'error' if task.result == 'warning'
task.result
end
diff --git a/app/views/api/v2/job_invocations/hosts.json.rabl b/app/views/api/v2/job_invocations/hosts.json.rabl
new file mode 100644
index 000000000..0467f9ce5
--- /dev/null
+++ b/app/views/api/v2/job_invocations/hosts.json.rabl
@@ -0,0 +1,15 @@
+collection @hosts
+
+attribute :name, :operatingsystem_id, :operatingsystem_name, :hostgroup_id, :hostgroup_name
+
+node :job_status do |host|
+ @host_statuses[host.id]
+end
+
+node :smart_proxy_id do |host|
+ @smart_proxy_id[host.id]
+end
+
+node :smart_proxy_name do |host|
+ @smart_proxy_name[host.id]
+end
diff --git a/config/routes.rb b/config/routes.rb
index 51d97f21b..3cf6759ac 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -63,6 +63,7 @@
get '/raw', :to => 'job_invocations#raw_output'
end
member do
+ get 'hosts'
post 'cancel'
post 'rerun'
get 'template_invocations', :to => 'template_invocations#template_invocations'
diff --git a/webpack/JobInvocationDetail/JobInvocationActions.js b/webpack/JobInvocationDetail/JobInvocationActions.js
index a70b80e6b..f80425065 100644
--- a/webpack/JobInvocationDetail/JobInvocationActions.js
+++ b/webpack/JobInvocationDetail/JobInvocationActions.js
@@ -15,7 +15,7 @@ import {
UPDATE_JOB,
} from './JobInvocationConstants';
-export const getData = url => dispatch => {
+export const getJobInvocation = url => dispatch => {
const fetchData = withInterval(
get({
key: JOB_INVOCATION_KEY,
diff --git a/webpack/JobInvocationDetail/JobInvocationConstants.js b/webpack/JobInvocationDetail/JobInvocationConstants.js
index 2673d3aaf..16d1f2b4d 100644
--- a/webpack/JobInvocationDetail/JobInvocationConstants.js
+++ b/webpack/JobInvocationDetail/JobInvocationConstants.js
@@ -1,14 +1,21 @@
+/* eslint-disable camelcase */
+import React from 'react';
import { foremanUrl } from 'foremanReact/common/helpers';
+import { translate as __ } from 'foremanReact/common/I18n';
+import { useForemanHostDetailsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
+import JobStatusIcon from '../react_app/components/RecentJobsCard/JobStatusIcon';
export const JOB_INVOCATION_KEY = 'JOB_INVOCATION_KEY';
export const CURRENT_PERMISSIONS = 'CURRENT_PERMISSIONS';
export const UPDATE_JOB = 'UPDATE_JOB';
export const CANCEL_JOB = 'CANCEL_JOB';
export const GET_TASK = 'GET_TASK';
+export const GET_TEMPLATE_INVOCATIONS = 'GET_TEMPLATE_INVOCATIONS';
export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
+export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
export const currentPermissionsUrl = foremanUrl(
'/api/v2/permissions/current_permissions'
);
@@ -20,6 +27,12 @@ export const STATUS = {
CANCELLED: 'cancelled',
};
+export const STATUS_UPPERCASE = {
+ RESOLVED: 'RESOLVED',
+ ERROR: 'ERROR',
+ PENDING: 'PENDING',
+};
+
export const DATE_OPTIONS = {
day: 'numeric',
month: 'short',
@@ -29,3 +42,74 @@ export const DATE_OPTIONS = {
hour12: false,
timeZoneName: 'short',
};
+
+const Columns = () => {
+ const getColumnsStatus = ({ hostJobStatus }) => {
+ switch (hostJobStatus) {
+ case 'success':
+ return { title: __('Succeeded'), status: 0 };
+ case 'error':
+ return { title: __('Failed'), status: 1 };
+ case 'planned':
+ return { title: __('Scheduled'), status: 2 };
+ case 'running':
+ return { title: __('Pending'), status: 3 };
+ case 'cancelled':
+ return { title: __('Cancelled'), status: 4 };
+ case 'N/A':
+ return { title: __('Awaiting start'), status: 5 };
+ default:
+ return { title: hostJobStatus, status: 6 };
+ }
+ };
+ const hostDetailsPageUrl = useForemanHostDetailsPageUrl();
+
+ return {
+ name: {
+ title: __('Name'),
+ wrapper: ({ name }) => (
+ {name}
+ ),
+ weight: 1,
+ },
+ groups: {
+ title: __('Host group'),
+ wrapper: ({ hostgroup_id, hostgroup_name }) => (
+ {hostgroup_name}
+ ),
+ weight: 2,
+ },
+ os: {
+ title: __('OS'),
+ wrapper: ({ operatingsystem_id, operatingsystem_name }) => (
+
+ {operatingsystem_name}
+
+ ),
+ weight: 3,
+ },
+ smart_proxy: {
+ title: __('Smart proxy'),
+ wrapper: ({ smart_proxy_name, smart_proxy_id }) => (
+ {smart_proxy_name}
+ ),
+ weight: 4,
+ },
+ status: {
+ title: __('Status'),
+ wrapper: ({ job_status }) => {
+ const { title, status } = getColumnsStatus({
+ hostJobStatus: job_status,
+ });
+ return (
+
+ {title || __('Unknown')}
+
+ );
+ },
+ weight: 5,
+ },
+ };
+};
+
+export default Columns;
diff --git a/webpack/JobInvocationDetail/JobInvocationDetail.scss b/webpack/JobInvocationDetail/JobInvocationDetail.scss
index 9482277a0..6cc58986e 100644
--- a/webpack/JobInvocationDetail/JobInvocationDetail.scss
+++ b/webpack/JobInvocationDetail/JobInvocationDetail.scss
@@ -38,4 +38,3 @@
height: $chart_size;
}
}
-
\ No newline at end of file
diff --git a/webpack/JobInvocationDetail/JobInvocationHostTable.js b/webpack/JobInvocationDetail/JobInvocationHostTable.js
new file mode 100644
index 000000000..0f6312313
--- /dev/null
+++ b/webpack/JobInvocationDetail/JobInvocationHostTable.js
@@ -0,0 +1,210 @@
+/* eslint-disable camelcase */
+import PropTypes from 'prop-types';
+import React, { useMemo, useEffect } from 'react';
+import { Icon } from 'patternfly-react';
+import { translate as __ } from 'foremanReact/common/I18n';
+import { FormattedMessage } from 'react-intl';
+import { Tr, Td } from '@patternfly/react-table';
+import {
+ Title,
+ EmptyState,
+ EmptyStateVariant,
+ EmptyStateBody,
+} from '@patternfly/react-core';
+import { foremanUrl } from 'foremanReact/common/helpers';
+import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
+import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
+import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
+import { useSetParamsAndApiAndSearch } from 'foremanReact/components/PF4/TableIndexPage/Table/TableIndexHooks';
+import {
+ useBulkSelect,
+ useUrlParams,
+} from 'foremanReact/components/PF4/TableIndexPage/Table/TableHooks';
+import Pagination from 'foremanReact/components/Pagination';
+import { getControllerSearchProps } from 'foremanReact/constants';
+import Columns, {
+ JOB_INVOCATION_HOSTS,
+ STATUS_UPPERCASE,
+} from './JobInvocationConstants';
+
+const JobInvocationHostTable = ({ id, targeting, finished, autoRefresh }) => {
+ const columns = Columns();
+ const columnNamesKeys = Object.keys(columns);
+ const apiOptions = { key: JOB_INVOCATION_HOSTS };
+ const {
+ searchParam: urlSearchQuery = '',
+ page: urlPage,
+ per_page: urlPerPage,
+ } = useUrlParams();
+ const defaultParams = { search: urlSearchQuery };
+ if (urlPage) defaultParams.page = Number(urlPage);
+ if (urlPerPage) defaultParams.per_page = Number(urlPerPage);
+ const { response, status, setAPIOptions } = useAPI(
+ 'get',
+ `/api/job_invocations/${id}/hosts`,
+ {
+ params: { ...defaultParams, key: JOB_INVOCATION_HOSTS },
+ }
+ );
+
+ const combinedResponse = {
+ response: {
+ search: urlSearchQuery,
+ can_create: false,
+ results: response?.results || [],
+ total: response?.total || 0,
+ per_page: response?.perPage,
+ page: response?.page,
+ subtotal: response?.subtotal || 0,
+ message: response?.message || 'error',
+ },
+ status,
+ setAPIOptions,
+ };
+
+ const { setParamsAndAPI, params } = useSetParamsAndApiAndSearch({
+ defaultParams,
+ apiOptions,
+ setAPIOptions: combinedResponse.setAPIOptions,
+ });
+
+ const { updateSearchQuery } = useBulkSelect({
+ initialSearchQuery: urlSearchQuery,
+ });
+
+ const controller = 'hosts';
+ const memoDefaultSearchProps = useMemo(
+ () => getControllerSearchProps(controller),
+ [controller]
+ );
+ memoDefaultSearchProps.autocomplete.url = foremanUrl(
+ `/${controller}/auto_complete_search`
+ );
+
+ useEffect(() => {
+ const intervalId = setInterval(() => {
+ if (!finished || autoRefresh) {
+ setAPIOptions(prevOptions => ({
+ ...prevOptions,
+ params: {
+ ...prevOptions.params,
+ },
+ }));
+ }
+ }, 5000);
+
+ return () => {
+ clearInterval(intervalId);
+ };
+ }, [finished, autoRefresh, setAPIOptions]);
+
+ const onPagination = newPagination => {
+ setParamsAndAPI({
+ ...params,
+ ...newPagination,
+ search: urlSearchQuery,
+ });
+ };
+
+ const bottomPagination = (
+
+ );
+
+ const customEmptyState = (
+
+
+
+
+
+
+
+ {__('No Results')}
+
+
+
+ {targeting?.targeting_type === 'dynamic_query' ? (
+
+ {__('view the hosts')}
+
+ ),
+ }}
+ />
+ ) : (
+ __('No hosts found')
+ )}
+
+
+
+ |
+
+ );
+
+ return (
+
+ {}}
+ errorMessage={
+ status === STATUS_UPPERCASE.ERROR && response?.message
+ ? response.message
+ : null
+ }
+ isPending={status === STATUS_UPPERCASE.PENDING}
+ isDeleteable={false}
+ bottomPagination={bottomPagination}
+ >
+ {response?.results?.map((result, rowIndex) => (
+
+ {columnNamesKeys.map(k => (
+ {columns[k].wrapper(result)} |
+ ))}
+
+ ))}
+
+
+ );
+};
+
+JobInvocationHostTable.propTypes = {
+ id: PropTypes.string.isRequired,
+ targeting: PropTypes.object.isRequired,
+ finished: PropTypes.bool.isRequired,
+ autoRefresh: PropTypes.bool.isRequired,
+};
+
+JobInvocationHostTable.defaultProps = {};
+
+export default JobInvocationHostTable;
diff --git a/webpack/JobInvocationDetail/JobInvocationSelectors.js b/webpack/JobInvocationDetail/JobInvocationSelectors.js
index cd2eb57ca..f5ba99d66 100644
--- a/webpack/JobInvocationDetail/JobInvocationSelectors.js
+++ b/webpack/JobInvocationDetail/JobInvocationSelectors.js
@@ -1,10 +1,10 @@
import { selectAPIResponse } from 'foremanReact/redux/API/APISelectors';
-import { JOB_INVOCATION_KEY } from './JobInvocationConstants';
+import { JOB_INVOCATION_KEY, GET_TASK } from './JobInvocationConstants';
export const selectItems = state =>
selectAPIResponse(state, JOB_INVOCATION_KEY);
-export const selectTask = state => selectAPIResponse(state, 'GET_TASK');
+export const selectTask = state => selectAPIResponse(state, GET_TASK);
export const selectTaskCancelable = state =>
selectTask(state).available_actions?.cancellable || false;
diff --git a/webpack/JobInvocationDetail/__tests__/MainInformation.test.js b/webpack/JobInvocationDetail/__tests__/MainInformation.test.js
index 83d6e045c..bfcc4d158 100644
--- a/webpack/JobInvocationDetail/__tests__/MainInformation.test.js
+++ b/webpack/JobInvocationDetail/__tests__/MainInformation.test.js
@@ -76,6 +76,10 @@ api.get.mockImplementation(({ handleSuccess, ...action }) => {
return { type: 'get', ...action };
});
+jest.mock('../JobInvocationHostTable.js', () => () => (
+ Mock Table
+));
+
const reportTemplateJobId = mockReportTemplatesResponse.results[0].id;
const mockStore = configureMockStore([thunk]);
@@ -207,7 +211,7 @@ describe('JobInvocationDetailPage', () => {
{ key: GET_REPORT_TEMPLATES, url: '/api/report_templates' },
{
key: JOB_INVOCATION_KEY,
- url: `/api/job_invocations/${jobId}`,
+ url: `/api/job_invocations/${jobId}?host_status=true`,
},
{
key: GET_REPORT_TEMPLATE_INPUTS,
diff --git a/webpack/JobInvocationDetail/__tests__/fixtures.js b/webpack/JobInvocationDetail/__tests__/fixtures.js
index c4f9e7846..d984a84f0 100644
--- a/webpack/JobInvocationDetail/__tests__/fixtures.js
+++ b/webpack/JobInvocationDetail/__tests__/fixtures.js
@@ -1,4 +1,7 @@
export const jobInvocationData = {
+ search: '',
+ per_page: 20,
+ page: 1,
id: 123,
description: 'Description',
job_category: 'Commands',
@@ -40,6 +43,9 @@ export const jobInvocationData = {
};
export const jobInvocationDataScheduled = {
+ search: '',
+ per_page: 20,
+ page: 1,
id: 456,
description: 'Description',
job_category: 'Commands',
@@ -62,6 +68,9 @@ export const jobInvocationDataScheduled = {
};
export const jobInvocationDataRecurring = {
+ search: '',
+ per_page: 20,
+ page: 1,
id: 789,
description: 'Description',
job_category: 'Commands',
diff --git a/webpack/JobInvocationDetail/index.js b/webpack/JobInvocationDetail/index.js
index 1605a46ae..2f57ed75e 100644
--- a/webpack/JobInvocationDetail/index.js
+++ b/webpack/JobInvocationDetail/index.js
@@ -1,12 +1,17 @@
import PropTypes from 'prop-types';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { Divider, Flex } from '@patternfly/react-core';
+import {
+ Divider,
+ Flex,
+ PageSection,
+ PageSectionVariants,
+} from '@patternfly/react-core';
import { translate as __, documentLocale } from 'foremanReact/common/I18n';
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
import { stopInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
-import { getData, getTask } from './JobInvocationActions';
+import { getJobInvocation, getTask } from './JobInvocationActions';
import {
CURRENT_PERMISSIONS,
DATE_OPTIONS,
@@ -19,6 +24,7 @@ import JobInvocationOverview from './JobInvocationOverview';
import { selectItems } from './JobInvocationSelectors';
import JobInvocationSystemStatusChart from './JobInvocationSystemStatusChart';
import JobInvocationToolbarButtons from './JobInvocationToolbarButtons';
+import JobInvocationHostTable from './JobInvocationHostTable';
const JobInvocationDetailPage = ({
match: {
@@ -32,6 +38,7 @@ const JobInvocationDetailPage = ({
status_label: statusLabel,
task,
start_at: startAt,
+ targeting,
} = items;
const finished =
statusLabel === STATUS.FAILED ||
@@ -57,7 +64,7 @@ const JobInvocationDetailPage = ({
}
useEffect(() => {
- dispatch(getData(`/api/job_invocations/${id}`));
+ dispatch(getJobInvocation(`/api/job_invocations/${id}?host_status=true`));
if (finished && !autoRefresh) {
dispatch(stopInterval(JOB_INVOCATION_KEY));
}
@@ -82,45 +89,60 @@ const JobInvocationDetailPage = ({
};
return (
-
- }
- searchable={false}
- >
-
+
+ }
+ searchable={false}
>
-
-
-
+
+
+
+
-
-
+
+
+ {items.id !== undefined && (
+
+ )}
+
+ >
);
};
diff --git a/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js b/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js
index a8f9748d1..533d49bd7 100644
--- a/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js
+++ b/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js
@@ -1,8 +1,7 @@
import React from 'react';
-import { translate as __ } from '../../../common/I18n';
const DefaultLoaderEmptyState = () => (
- {__('Not available')}
+ Not available
);
export default DefaultLoaderEmptyState;
diff --git a/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js b/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js
index 3fb1d3493..a5b819d67 100644
--- a/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js
+++ b/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js
@@ -3,29 +3,60 @@ import PropTypes from 'prop-types';
import {
CheckCircleIcon,
ExclamationCircleIcon,
+ BuildIcon,
+ RunningIcon,
+ ExclamationTriangleIcon,
QuestionCircleIcon,
} from '@patternfly/react-icons';
-import { JOB_SUCCESS_STATUS, JOB_ERROR_STATUS } from './constants';
+import {
+ JOB_SUCCESS_STATUS,
+ JOB_ERROR_STATUS,
+ JOB_PLANNED_STATUS,
+ JOB_RUNNING_STATUS,
+ JOB_CANCELLED_STATUS,
+ JOB_AWAITING_STATUS,
+} from './constants';
import './styles.scss';
const JobStatusIcon = ({ status, children, ...props }) => {
switch (status) {
case JOB_SUCCESS_STATUS:
return (
-
- {children}
+
+ {children}
);
case JOB_ERROR_STATUS:
return (
-
- {children}
+
+ {children}
+
+ );
+ case JOB_PLANNED_STATUS:
+ return (
+
+ {children}
+
+ );
+ case JOB_RUNNING_STATUS:
+ return (
+
+ {children}
+
+ );
+ case JOB_CANCELLED_STATUS:
+ return (
+
+ {' '}
+ {children}
);
+ case JOB_AWAITING_STATUS:
+ return {children};
default:
return (
-
- {children}
+
+ {children}
);
}
diff --git a/webpack/react_app/components/RecentJobsCard/constants.js b/webpack/react_app/components/RecentJobsCard/constants.js
index bd298c706..a5b9cde50 100644
--- a/webpack/react_app/components/RecentJobsCard/constants.js
+++ b/webpack/react_app/components/RecentJobsCard/constants.js
@@ -5,6 +5,10 @@ export const SCHEDULED_TAB = 2;
export const JOB_SUCCESS_STATUS = 0;
export const JOB_ERROR_STATUS = 1;
+export const JOB_PLANNED_STATUS = 2;
+export const JOB_RUNNING_STATUS = 3;
+export const JOB_CANCELLED_STATUS = 4;
+export const JOB_AWAITING_STATUS = 5;
export const JOB_BASE_URL = '/job_invocations?search=targeted_host_id+%3D+';
export const JOB_API_URL =
diff --git a/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap b/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap
index ec26aee19..5c47909ce 100644
--- a/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap
+++ b/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap
@@ -7,6 +7,6 @@ exports[`HostStatus renders 1`] = `
type="pf"
/>
- success
+ Succeeded
`;
diff --git a/webpack/react_app/components/TargetingHosts/components/HostStatus.js b/webpack/react_app/components/TargetingHosts/components/HostStatus.js
index 50f84a057..62b1f2cf1 100644
--- a/webpack/react_app/components/TargetingHosts/components/HostStatus.js
+++ b/webpack/react_app/components/TargetingHosts/components/HostStatus.js
@@ -8,38 +8,38 @@ const HostStatus = ({ status }) => {
case 'cancelled':
return (
- {status}
+ {__('Cancelled')}
);
case 'N/A':
return (
- {status}
+ {__('Awaiting start')}
);
case 'running':
return (
- {status}
+ {__('Pending')}
);
case 'planned':
return (
- {status}
+ {__('Scheduled')}
);
case 'warning':
case 'error':
return (
- {__('failed')}
+ {__('Failed')}
);
case 'success':
return (
- {status}
+ {__('Succeeded')}
);
default: