foreman_host_reports 0.0.3

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +545 -0
  4. data/app/controllers/api/v2/host_reports_controller.rb +98 -0
  5. data/app/controllers/concerns/foreman_host_reports/controller/parameters/host_report.rb +25 -0
  6. data/app/controllers/host_reports_controller.rb +46 -0
  7. data/app/models/concerns/foreman_host_reports/host_extensions.rb +9 -0
  8. data/app/models/concerns/foreman_host_reports/smart_proxy_extensions.rb +9 -0
  9. data/app/models/host_report.rb +85 -0
  10. data/app/models/report_keyword.rb +11 -0
  11. data/app/views/api/v2/host_reports/base.json.rabl +5 -0
  12. data/app/views/api/v2/host_reports/create.json.rabl +5 -0
  13. data/app/views/api/v2/host_reports/index.json.rabl +5 -0
  14. data/app/views/api/v2/host_reports/main.json.rabl +15 -0
  15. data/app/views/api/v2/host_reports/show.json.rabl +11 -0
  16. data/config/routes.rb +33 -0
  17. data/db/migrate/20210112183526_add_host_reports.rb +36 -0
  18. data/db/migrate/20210616133601_create_report_keywords.rb +12 -0
  19. data/lib/foreman_host_reports/engine.rb +68 -0
  20. data/lib/foreman_host_reports/version.rb +3 -0
  21. data/lib/foreman_host_reports.rb +4 -0
  22. data/lib/tasks/foreman_host_reports_tasks.rake +50 -0
  23. data/locale/Makefile +60 -0
  24. data/locale/en/foreman_host_reports.po +19 -0
  25. data/locale/foreman_host_reports.pot +19 -0
  26. data/locale/gemspec.rb +2 -0
  27. data/package.json +45 -0
  28. data/test/controllers/api/v2/host_reports_controller_test.rb +278 -0
  29. data/test/factories/foreman_host_reports_factories.rb +21 -0
  30. data/test/snapshots/foreman-web.json +918 -0
  31. data/test/test_plugin_helper.rb +11 -0
  32. data/test/unit/foreman_host_reports_test.rb +9 -0
  33. data/webpack/global_index.js +4 -0
  34. data/webpack/global_test_setup.js +11 -0
  35. data/webpack/index.js +0 -0
  36. data/webpack/src/Router/HostReports/Components/HostReportDeleteModal.js +50 -0
  37. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.js +53 -0
  38. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/FormatCell.scss +4 -0
  39. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/formatCellFormatter.js +6 -0
  40. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/hostReportsToShowFormatter.js +13 -0
  41. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/index.js +4 -0
  42. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/reportToShowFormatter.js +13 -0
  43. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/statusFormatter.js +9 -0
  44. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/HostReportsToShowCell.js +32 -0
  45. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/ReportToShowCell.js +27 -0
  46. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.js +77 -0
  47. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.scss +9 -0
  48. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/formatImages.js +7 -0
  49. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/ansible.png +0 -0
  50. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/images/puppet.png +0 -0
  51. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTable.js +85 -0
  52. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTableSchema.js +65 -0
  53. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/index.js +28 -0
  54. data/webpack/src/Router/HostReports/IndexPage/IndexPage.js +88 -0
  55. data/webpack/src/Router/HostReports/IndexPage/IndexPageActions.js +55 -0
  56. data/webpack/src/Router/HostReports/IndexPage/IndexPageHelpers.js +50 -0
  57. data/webpack/src/Router/HostReports/IndexPage/IndexPageSelectors.js +86 -0
  58. data/webpack/src/Router/HostReports/IndexPage/constants.js +14 -0
  59. data/webpack/src/Router/HostReports/IndexPage/index.js +98 -0
  60. data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/HostReportMetrics.scss +3 -0
  61. data/webpack/src/Router/HostReports/ShowPage/Components/HostReportMetrics/index.js +100 -0
  62. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Ansible.js +77 -0
  63. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Puppet.js +79 -0
  64. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/helpers.js +17 -0
  65. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/index.js +29 -0
  66. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogsFilter/index.js +109 -0
  67. data/webpack/src/Router/HostReports/ShowPage/ShowPage.js +160 -0
  68. data/webpack/src/Router/HostReports/ShowPage/ShowPageSelectors.js +51 -0
  69. data/webpack/src/Router/HostReports/ShowPage/index.js +75 -0
  70. data/webpack/src/Router/HostReports/constants.js +15 -0
  71. data/webpack/src/Router/routes.js +23 -0
  72. data/webpack/test_setup.js +17 -0
  73. metadata +133 -0
@@ -0,0 +1,55 @@
1
+ import history from 'foremanReact/history';
2
+ import { get } from 'foremanReact/redux/API';
3
+ import { stringifyParams, getParams } from 'foremanReact/common/urlHelpers';
4
+
5
+ import { buildQuery } from './IndexPageHelpers';
6
+ import {
7
+ HOST_REPORTS_API_PATH,
8
+ HOST_REPORTS_PATH,
9
+ HOST_REPORTS_API_REQUEST_KEY,
10
+ } from './constants';
11
+
12
+ export const initializeHostReports = () => dispatch => {
13
+ const params = getParams();
14
+ dispatch(fetchHostReports(params));
15
+ if (!history.action === 'POP') {
16
+ history.replace({
17
+ pathname: HOST_REPORTS_PATH,
18
+ search: stringifyParams(params),
19
+ });
20
+ }
21
+ };
22
+
23
+ export const fetchHostReports = (
24
+ { page, perPage, searchQuery, sort },
25
+ url = HOST_REPORTS_API_PATH
26
+ ) => async dispatch => {
27
+ const sortString =
28
+ sort && Object.keys(sort).length > 0 ? `${sort.by} ${sort.order}` : '';
29
+
30
+ return dispatch(
31
+ get({
32
+ key: HOST_REPORTS_API_REQUEST_KEY,
33
+ url,
34
+ params: {
35
+ page,
36
+ per_page: perPage,
37
+ search: searchQuery,
38
+ order: sortString,
39
+ },
40
+ })
41
+ );
42
+ };
43
+
44
+ export const fetchAndPush = (params = {}) => (dispatch, getState) => {
45
+ const query = buildQuery(params, getState());
46
+ dispatch(fetchHostReports(query));
47
+ history.push({
48
+ pathname: HOST_REPORTS_PATH,
49
+ search: stringifyParams(query),
50
+ });
51
+ };
52
+
53
+ export const reloadWithSearch = query => dispatch => {
54
+ dispatch(fetchAndPush({ searchQuery: query, page: 1 }));
55
+ };
@@ -0,0 +1,50 @@
1
+ import URI from 'urijs';
2
+ import { snakeCase } from 'lodash';
3
+ import { compose } from 'redux';
4
+
5
+ import { foremanUrl } from 'foremanReact/common/helpers';
6
+
7
+ import {
8
+ selectSort,
9
+ selectPage,
10
+ selectPerPage,
11
+ selectSearch,
12
+ } from './IndexPageSelectors';
13
+
14
+ import { HOST_REPORTS_API_PATH } from './constants';
15
+
16
+ export const buildQuery = (query, state) => {
17
+ const querySort = pickSort(query, state);
18
+
19
+ return {
20
+ page: query.page || selectPage(state),
21
+ perPage: query.perPage || selectPerPage(state),
22
+ searchQuery:
23
+ query.searchQuery === undefined ? selectSearch(state) : query.searchQuery,
24
+ ...(querySort && { sort: querySort }),
25
+ };
26
+ };
27
+
28
+ export const pickSort = (query, state) =>
29
+ checkSort(query.sort)
30
+ ? transformSort(query.sort)
31
+ : checkSort(compose(transformSort, selectSort)(state));
32
+
33
+ const checkSort = sort => (sort && sort.by && sort.order ? sort : undefined);
34
+
35
+ const transformSort = sort => ({ ...sort, by: snakeCase(sort.by) });
36
+
37
+ export const getExportUrl = (path, query) => {
38
+ let url = new URI(path);
39
+ url = url.pathname(`${url.pathname()}/export`);
40
+ url.addSearch(query);
41
+ return url.toString();
42
+ };
43
+
44
+ export const hostReportsIndexUrl = hostId => {
45
+ if (!hostId) return foremanUrl(HOST_REPORTS_API_PATH);
46
+
47
+ return foremanUrl(
48
+ `/api/v2/hosts/${hostId}/host_reports?include_permissions=true`
49
+ );
50
+ };
@@ -0,0 +1,86 @@
1
+ import { camelCase, isEmpty } from 'lodash';
2
+ import Immutable from 'seamless-immutable';
3
+ import { STATUS } from 'foremanReact/constants';
4
+ import { deepPropsToCamelCase } from 'foremanReact/common/helpers';
5
+ import {
6
+ selectAPIStatus,
7
+ selectAPIResponse,
8
+ selectAPIErrorMessage,
9
+ } from 'foremanReact/redux/API/APISelectors';
10
+
11
+ import { HOST_REPORTS_API_REQUEST_KEY } from './constants';
12
+
13
+ export const emptyResponse = {
14
+ results: [],
15
+ page: 0,
16
+ perPage: 0,
17
+ search: '',
18
+ sort: {},
19
+ canCreate: false,
20
+ subtotal: 0,
21
+ };
22
+
23
+ const selectHostReportsPageResponse = state => {
24
+ const response = deepPropsToCamelCase(
25
+ selectAPIResponse(state, HOST_REPORTS_API_REQUEST_KEY)
26
+ );
27
+ if (isEmpty(response)) {
28
+ return Immutable(emptyResponse);
29
+ }
30
+ return response;
31
+ };
32
+
33
+ export const selectIsLoading = state => {
34
+ const status = selectHostReportsPageStatus(state);
35
+ return !status || status === STATUS.PENDING;
36
+ };
37
+
38
+ const selectHostReportsPageStatus = state =>
39
+ selectAPIStatus(state, HOST_REPORTS_API_REQUEST_KEY);
40
+
41
+ export const selectHasError = state =>
42
+ selectHostReportsPageStatus(state) === STATUS.ERROR;
43
+
44
+ export const selectHostReports = state => {
45
+ if (selectHasError(state)) {
46
+ return [];
47
+ }
48
+ return selectHostReportsPageResponse(state).results;
49
+ };
50
+
51
+ export const selectHasData = state => {
52
+ const status = selectHostReportsPageStatus(state);
53
+ const results = selectHostReports(state);
54
+
55
+ return status === STATUS.RESOLVED && results && results.length > 0;
56
+ };
57
+
58
+ export const selectPage = state =>
59
+ selectHostReportsPageResponse(state).page || 1;
60
+ export const selectPerPage = state =>
61
+ selectHostReportsPageResponse(state).perPage || 20;
62
+ export const selectSearch = state =>
63
+ selectHostReportsPageResponse(state).search;
64
+
65
+ export const selectSort = state => {
66
+ const sort = selectHostReportsPageResponse(state).sort || Immutable({});
67
+ if (sort.by && sort.order) {
68
+ return { ...sort, by: camelCase(sort.by) };
69
+ }
70
+ return sort;
71
+ };
72
+
73
+ export const selectSubtotal = state =>
74
+ selectHostReportsPageResponse(state).subtotal || 0;
75
+
76
+ export const selectErrorMessage = state => {
77
+ if (!selectHasError(state)) return { message: '', details: '' };
78
+ const error = selectHostReportsPageResponse(state).response?.data?.error;
79
+
80
+ if (error) return error;
81
+
82
+ return {
83
+ message: selectAPIErrorMessage(state, HOST_REPORTS_API_REQUEST_KEY),
84
+ details: '',
85
+ };
86
+ };
@@ -0,0 +1,14 @@
1
+ import { getControllerSearchProps } from 'foremanReact/constants';
2
+
3
+ export const HOST_REPORTS_SEARCH_PROPS = getControllerSearchProps(
4
+ 'host_reports'
5
+ );
6
+
7
+ export const HOST_REPORTS_PATH = '/host_reports';
8
+ export const HOST_REPORTS_API_PATH =
9
+ '/api/v2/host_reports?include_permissions=true';
10
+ export const HOST_REPORTS_API_PLAIN_PATH = '/api/v2/host_reports';
11
+
12
+ export const HOST_REPORTS_API_REQUEST_KEY = 'HOST_REPORTS_API';
13
+
14
+ export const HOST_REPORT_DELETE_MODAL_ID = 'hostReportDeleteModal';
@@ -0,0 +1,98 @@
1
+ import React, { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useSelector, useDispatch } from 'react-redux';
4
+ import { isEmpty } from 'lodash';
5
+
6
+ import { get } from 'foremanReact/redux/API';
7
+
8
+ import Loading from 'foremanReact/components/Loading';
9
+ import DefaultEmptyState from 'foremanReact/components/common/EmptyState';
10
+ import { WelcomeConfigReports } from 'foremanReact/components/ConfigReports/Welcome';
11
+
12
+ import { HOST_REPORTS_API_REQUEST_KEY } from './constants';
13
+
14
+ import HostReportsIndexPage from './IndexPage';
15
+
16
+ import {
17
+ selectHostReports,
18
+ selectPage,
19
+ selectPerPage,
20
+ selectSearch,
21
+ selectSort,
22
+ selectHasData,
23
+ selectHasError,
24
+ selectIsLoading,
25
+ selectSubtotal,
26
+ selectErrorMessage,
27
+ } from './IndexPageSelectors';
28
+
29
+ import { reloadWithSearch, fetchAndPush } from './IndexPageActions';
30
+
31
+ import { hostReportsIndexUrl } from './IndexPageHelpers';
32
+
33
+ const ConnectedHostReportsIndexPage = ({ history, match }) => {
34
+ const dispatch = useDispatch();
35
+
36
+ const reports = useSelector(selectHostReports);
37
+ const page = useSelector(selectPage);
38
+ const perPage = useSelector(selectPerPage);
39
+ const search = useSelector(selectSearch);
40
+ const sort = useSelector(selectSort);
41
+ const isLoading = useSelector(selectIsLoading);
42
+ const hasData = useSelector(selectHasData);
43
+ const hasError = useSelector(selectHasError);
44
+ const itemCount = useSelector(selectSubtotal);
45
+ const error = useSelector(selectErrorMessage);
46
+
47
+ const { hostId } = match.params;
48
+
49
+ useEffect(() => {
50
+ dispatch(
51
+ get({
52
+ key: HOST_REPORTS_API_REQUEST_KEY,
53
+ url: hostReportsIndexUrl(hostId),
54
+ })
55
+ );
56
+ }, [dispatch, hostId]);
57
+
58
+ if (isLoading && !hasError) return <Loading />;
59
+
60
+ if (!isLoading && hasError) {
61
+ return (
62
+ <DefaultEmptyState
63
+ icon="error-circle-o"
64
+ header={error.message}
65
+ description={error.details || ''}
66
+ documentation={null}
67
+ />
68
+ );
69
+ }
70
+
71
+ if (!isLoading && !hasError && !hasData && isEmpty(search)) {
72
+ return <WelcomeConfigReports />;
73
+ }
74
+
75
+ return (
76
+ <HostReportsIndexPage
77
+ fetchAndPush={params => dispatch(fetchAndPush(params))}
78
+ search={search}
79
+ isLoading={isLoading}
80
+ hasData={hasData}
81
+ reports={reports}
82
+ page={page}
83
+ perPage={perPage}
84
+ sort={sort}
85
+ itemCount={itemCount}
86
+ reloadWithSearch={query => dispatch(reloadWithSearch(query))}
87
+ history={history}
88
+ hostId={hostId}
89
+ />
90
+ );
91
+ };
92
+
93
+ ConnectedHostReportsIndexPage.propTypes = {
94
+ history: PropTypes.object.isRequired,
95
+ match: PropTypes.object.isRequired,
96
+ };
97
+
98
+ export default ConnectedHostReportsIndexPage;
@@ -0,0 +1,3 @@
1
+ .bar-chart-medium-width {
2
+ min-width: 450px;
3
+ }
@@ -0,0 +1,100 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { Grid, GridItem } from '@patternfly/react-core';
5
+
6
+ import { cloneDeep, remove } from 'lodash';
7
+
8
+ import ChartBox from 'foremanReact/components/ChartBox/ChartBox';
9
+
10
+ import { translate as __ } from 'foremanReact/common/I18n';
11
+ import { STATUS } from 'foremanReact/constants';
12
+
13
+ import './HostReportMetrics.scss';
14
+
15
+ const HostReportMetrics = ({
16
+ metrics: {
17
+ time: { values: timeValues },
18
+ resources: { values: resValues },
19
+ },
20
+ }) => {
21
+ const clonedTimeValues = cloneDeep(timeValues).filter(v => v[2] >= 0.001);
22
+ const totalTime = remove(clonedTimeValues, val => val[0] === 'total');
23
+ const metricsChartData = clonedTimeValues
24
+ .map(v => [v[1], parseFloat(v[2].toFixed(4))])
25
+ .sort((a, b) => b[1] - a[1]);
26
+
27
+ const clonedStatuses = cloneDeep(resValues);
28
+ const totalStatuses = remove(clonedStatuses, val => val[0] === 'total');
29
+ const statuses = clonedStatuses
30
+ .map(v => [v[1], v[2]])
31
+ .sort((a, b) => b[1] - a[1]);
32
+
33
+ const createRow = ([name, value], i) => (
34
+ <tr key={i}>
35
+ <td className="break-me">{name}</td>
36
+ <td>{value}</td>
37
+ </tr>
38
+ );
39
+
40
+ const chartBoxProps = {
41
+ className: 'report-chart',
42
+ noDataMsg: __('No data available'),
43
+ status: STATUS.RESOLVED,
44
+ config: 'medium',
45
+ };
46
+
47
+ return (
48
+ <Grid hasGutter>
49
+ <GridItem span={4}>
50
+ <table className="table table-bordered table-striped table-hover report-chart">
51
+ <tbody>{statuses.map((label, v) => createRow(label, v))}</tbody>
52
+ {totalStatuses.length ? (
53
+ <tfoot>
54
+ <tr>
55
+ <th>{__('Total')}</th>
56
+ <th>{totalStatuses[0][2]}</th>
57
+ </tr>
58
+ </tfoot>
59
+ ) : null}
60
+ </table>
61
+ </GridItem>
62
+ <GridItem span={4}>
63
+ <ChartBox
64
+ {...chartBoxProps}
65
+ type="donut"
66
+ chart={{ data: metricsChartData }}
67
+ title={__('Report Metrics')}
68
+ />
69
+ </GridItem>
70
+ <GridItem span={4}>
71
+ <table className="table table-bordered table-striped table-hover report-chart">
72
+ <tbody>
73
+ {metricsChartData.map((label, t) => createRow(label, t))}
74
+ </tbody>
75
+ {totalTime.length ? (
76
+ <tfoot>
77
+ <tr>
78
+ <th>{__('Total')}</th>
79
+ <th>{parseFloat(totalTime[0][2].toFixed(4))}</th>
80
+ </tr>
81
+ </tfoot>
82
+ ) : null}
83
+ </table>
84
+ </GridItem>
85
+ </Grid>
86
+ );
87
+ };
88
+
89
+ HostReportMetrics.propTypes = {
90
+ metrics: PropTypes.shape({
91
+ time: PropTypes.shape({
92
+ values: PropTypes.array,
93
+ }),
94
+ resources: PropTypes.shape({
95
+ values: PropTypes.array,
96
+ }),
97
+ }).isRequired,
98
+ };
99
+
100
+ export default HostReportMetrics;
@@ -0,0 +1,77 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { Alert, AlertActionCloseButton } from '@patternfly/react-core';
5
+
6
+ import { translate as __ } from 'foremanReact/common/I18n';
7
+
8
+ import { msgLevelClasses } from './helpers';
9
+
10
+ const AnsibleLogs = ({ logs, checkMode }) => {
11
+ const [alertVisibility, setAlertVisibility] = useState(true);
12
+ return (
13
+ <>
14
+ {checkMode && alertVisibility ? (
15
+ <Alert
16
+ variant="info"
17
+ isInline
18
+ title={__('Ansible check mode')}
19
+ actionClose={
20
+ <AlertActionCloseButton onClose={() => setAlertVisibility(false)} />
21
+ }
22
+ >
23
+ {__('Notice that ansible roles run in check mode.')}
24
+ </Alert>
25
+ ) : null}
26
+ <table
27
+ id="report_log"
28
+ className="table table-bordered table-striped table-hover"
29
+ >
30
+ <thead>
31
+ <tr>
32
+ <th className="col col-md"> {__('Level')} </th>
33
+ <th className="col col-md-3"> {__('Task')} </th>
34
+ <th className="col col-md-9"> {__('Message')} </th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ {logs.map((log, idx) => (
39
+ <tr key={`tr-${idx + 1}`}>
40
+ <td>
41
+ <span className={msgLevelClasses(log.level)}>{log.level}</span>
42
+ </td>
43
+ <td className="break-me">{log.task.name}</td>
44
+ {Array.isArray(log.friendlyMessage) ? (
45
+ <td>
46
+ <ul>
47
+ {log.friendlyMessage.map((msg, i) => (
48
+ <li key={`li-${i + 1}`}>{msg}</li>
49
+ ))}
50
+ </ul>
51
+ </td>
52
+ ) : (
53
+ <td className="break-me">{log.friendlyMessage}</td>
54
+ )}
55
+ </tr>
56
+ ))}
57
+ {logs.length === 0 ? (
58
+ <tr key="tr-0">
59
+ <td colSpan="3">{__('Nothing to show')}</td>
60
+ </tr>
61
+ ) : null}
62
+ </tbody>
63
+ </table>
64
+ </>
65
+ );
66
+ };
67
+
68
+ AnsibleLogs.propTypes = {
69
+ logs: PropTypes.array.isRequired,
70
+ checkMode: PropTypes.bool,
71
+ };
72
+
73
+ AnsibleLogs.defaultProps = {
74
+ checkMode: false,
75
+ };
76
+
77
+ export default AnsibleLogs;
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { useDispatch } from 'react-redux';
3
+ import PropTypes from 'prop-types';
4
+
5
+ import { sprintf, translate as __ } from 'foremanReact/common/I18n';
6
+ import * as diffModalActions from 'foremanReact/components/ConfigReports/DiffModal/DiffModalActions';
7
+ import DiffModal from 'foremanReact/components/ConfigReports/DiffModal';
8
+
9
+ import { msgLevelClasses } from './helpers';
10
+
11
+ const PuppetLogs = ({ logs, environment }) => {
12
+ const dispatch = useDispatch();
13
+ const showDiff = (e, diff, title) => {
14
+ e.preventDefault();
15
+ dispatch(diffModalActions.createDiff(diff, title));
16
+ };
17
+
18
+ return (
19
+ <>
20
+ <DiffModal />
21
+ {environment ? (
22
+ <p className="ra">
23
+ {sprintf(__('Puppet Environment: %s'), environment)}
24
+ </p>
25
+ ) : null}
26
+ <table
27
+ id="report_log"
28
+ className="table table-bordered table-striped table-hover"
29
+ >
30
+ <thead>
31
+ <tr>
32
+ <th className="col col-md"> {__('Level')} </th>
33
+ <th className="col col-md-3"> {__('Resource')} </th>
34
+ <th className="col col-md-9"> {__('Message')} </th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ {logs.map((log, i) => (
39
+ <tr key={`tr-${i + 1}`}>
40
+ <td>
41
+ <span className={msgLevelClasses(log[0])}>{log[0]}</span>
42
+ </td>
43
+ <td className="break-me">{log[1]}</td>
44
+ {log[2].startsWith('\n---') ? (
45
+ <td className="break-me">
46
+ <a
47
+ onClick={e =>
48
+ showDiff(e, log[2], /File\[(.*?)\]/.exec(log[1])[1])
49
+ }
50
+ >
51
+ {__('Show Diff')}
52
+ </a>
53
+ </td>
54
+ ) : (
55
+ <td className="break-me">{log[2]}</td>
56
+ )}
57
+ </tr>
58
+ ))}
59
+ {logs.length === 0 ? (
60
+ <tr key="tr-0">
61
+ <td colSpan="3">{__('Nothing to show')}</td>
62
+ </tr>
63
+ ) : null}
64
+ </tbody>
65
+ </table>
66
+ </>
67
+ );
68
+ };
69
+
70
+ PuppetLogs.propTypes = {
71
+ logs: PropTypes.array.isRequired,
72
+ environment: PropTypes.string,
73
+ };
74
+
75
+ PuppetLogs.defaultProps = {
76
+ environment: null,
77
+ };
78
+
79
+ export default PuppetLogs;
@@ -0,0 +1,17 @@
1
+ export const msgLevelClasses = level => {
2
+ let tag;
3
+ switch (level) {
4
+ case 'notice':
5
+ tag = 'info';
6
+ break;
7
+ case 'warning':
8
+ tag = 'warning';
9
+ break;
10
+ case 'err':
11
+ tag = 'danger';
12
+ break;
13
+ default:
14
+ tag = 'default';
15
+ }
16
+ return `label label-${tag} result-filter-tag`;
17
+ };
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import PuppetLogs from './Puppet';
5
+ import AnsibleLogs from './Ansible';
6
+
7
+ const ReportLogs = ({ format, logs, meta }) => {
8
+ switch (format) {
9
+ case 'puppet':
10
+ return <PuppetLogs logs={logs} environment={meta.environment} />;
11
+ case 'ansible':
12
+ return <AnsibleLogs logs={logs} checkMode={meta.checkMode} />;
13
+ default:
14
+ return <></>;
15
+ }
16
+ };
17
+
18
+ ReportLogs.propTypes = {
19
+ format: PropTypes.string.isRequired,
20
+ logs: PropTypes.array,
21
+ meta: PropTypes.object,
22
+ };
23
+
24
+ ReportLogs.defaultProps = {
25
+ logs: [],
26
+ meta: {},
27
+ };
28
+
29
+ export default ReportLogs;