diff --git a/awx/ui/src/api/index.js b/awx/ui/src/api/index.js
index 5281ad861d..7a03643c05 100644
--- a/awx/ui/src/api/index.js
+++ b/awx/ui/src/api/index.js
@@ -44,6 +44,7 @@ import WorkflowApprovalTemplates from './models/WorkflowApprovalTemplates';
import WorkflowJobTemplateNodes from './models/WorkflowJobTemplateNodes';
import WorkflowJobTemplates from './models/WorkflowJobTemplates';
import WorkflowJobs from './models/WorkflowJobs';
+import HostMetrics from './models/HostMetrics';
const ActivityStreamAPI = new ActivityStream();
const AdHocCommandsAPI = new AdHocCommands();
@@ -91,6 +92,7 @@ const WorkflowApprovalTemplatesAPI = new WorkflowApprovalTemplates();
const WorkflowJobTemplateNodesAPI = new WorkflowJobTemplateNodes();
const WorkflowJobTemplatesAPI = new WorkflowJobTemplates();
const WorkflowJobsAPI = new WorkflowJobs();
+const HostMetricsAPI = new HostMetrics();
export {
ActivityStreamAPI,
@@ -139,4 +141,5 @@ export {
WorkflowJobTemplateNodesAPI,
WorkflowJobTemplatesAPI,
WorkflowJobsAPI,
+ HostMetricsAPI,
};
diff --git a/awx/ui/src/api/models/HostMetrics.js b/awx/ui/src/api/models/HostMetrics.js
new file mode 100644
index 0000000000..d8ca8f4c8c
--- /dev/null
+++ b/awx/ui/src/api/models/HostMetrics.js
@@ -0,0 +1,10 @@
+import Base from '../Base';
+
+class HostMetrics extends Base {
+ constructor(http) {
+ super(http);
+ this.baseUrl = 'api/v2/host_metrics/';
+ }
+}
+
+export default HostMetrics;
diff --git a/awx/ui/src/routeConfig.js b/awx/ui/src/routeConfig.js
index 602e804d2b..c738a352b3 100644
--- a/awx/ui/src/routeConfig.js
+++ b/awx/ui/src/routeConfig.js
@@ -23,6 +23,7 @@ import TopologyView from 'screens/TopologyView';
import Users from 'screens/User';
import WorkflowApprovals from 'screens/WorkflowApproval';
import { Jobs } from 'screens/Job';
+import HostMetrics from 'screens/HostMetrics';
function getRouteConfig(userProfile = {}) {
let routeConfig = [
@@ -55,6 +56,11 @@ function getRouteConfig(userProfile = {}) {
path: '/workflow_approvals',
screen: WorkflowApprovals,
},
+ {
+ title: Host Metrics,
+ path: '/host_metrics',
+ screen: HostMetrics,
+ },
],
},
{
diff --git a/awx/ui/src/screens/HostMetrics/HostMetrics.js b/awx/ui/src/screens/HostMetrics/HostMetrics.js
new file mode 100644
index 0000000000..0a397dfce3
--- /dev/null
+++ b/awx/ui/src/screens/HostMetrics/HostMetrics.js
@@ -0,0 +1,86 @@
+import React, {useCallback, useEffect, useState} from 'react';
+import {t} from "@lingui/macro";
+import ScreenHeader from 'components/ScreenHeader/ScreenHeader';
+import { HostMetricsAPI } from 'api';
+import useRequest from 'hooks/useRequest';
+import PaginatedTable, {
+ HeaderRow,
+ HeaderCell
+} from 'components/PaginatedTable';
+import DataListToolbar from 'components/DataListToolbar';
+import { getQSConfig, parseQueryString } from 'util/qs';
+import {Card, PageSection} from "@patternfly/react-core";
+import { useLocation } from 'react-router-dom';
+import HostMetricsListItem from "./HostMetricsListItem";
+
+function HostMetrics() {
+
+ const location = useLocation();
+
+ const [breadcrumbConfig] = useState({
+ '/host_metrics': t`Host Metrics`,
+ });
+ const QS_CONFIG = getQSConfig('host_metrics', {
+ page: 1,
+ page_size: 20,
+ order_by: 'hostname',
+ });
+ const {
+ result: { count, results },
+ isLoading,
+ error,
+ request: readHostMetrics,
+ } = useRequest(
+ useCallback(async () => {
+ const params = parseQueryString(QS_CONFIG, location.search);
+ const list = await HostMetricsAPI.read(params);
+ return {
+ count: list.data.count,
+ results: list.data.results
+ };
+ }, [location.search]),
+ { results: [], count: 0 }
+ );
+
+ useEffect(() => {
+ readHostMetrics();
+ }, [readHostMetrics]);
+
+ return(
+ <>
+
+
+
+ ()}
+ qsConfig={QS_CONFIG}
+ toolbarSearchColumns={[{name: t`Hostname`, key: 'hostname', isDefault: true}]}
+ toolbarSearchableKeys={[]}
+ toolbarRelatedSearchableKeys={[]}
+ renderToolbar={(props) => }
+ headerRow={
+
+ {t`Hostname`}
+ {t`First automated`}
+ {t`Last automated`}
+ {t`Automation`}
+ {t`Inventories`}
+ {t`Deleted`}
+
+}
+ />
+
+
+ >
+ );
+}
+
+export default HostMetrics;
diff --git a/awx/ui/src/screens/HostMetrics/HostMetricsListItem.js b/awx/ui/src/screens/HostMetrics/HostMetricsListItem.js
new file mode 100644
index 0000000000..fe9d06a25c
--- /dev/null
+++ b/awx/ui/src/screens/HostMetrics/HostMetricsListItem.js
@@ -0,0 +1,27 @@
+import 'styled-components/macro';
+import React from 'react';
+import { Tr, Td } from '@patternfly/react-table';
+import { formatDateString } from 'util/dates';
+import { HostMetrics } from 'types';
+import {t} from "@lingui/macro";
+
+function HostMetricsListItem({ item }) {
+
+ return (
+
+ |
+ {item.hostname} |
+ {formatDateString(item.first_automation)} |
+ {formatDateString(item.last_automation)} |
+ {item.automated_counter} |
+ {item.used_in_inventories || 0} |
+ {item.deleted_counter} | <
+ /Tr>
+ );
+}
+
+HostMetricsListItem.propTypes = {
+ item: HostMetrics.isRequired,
+};
+
+export default HostMetricsListItem;
diff --git a/awx/ui/src/screens/HostMetrics/index.js b/awx/ui/src/screens/HostMetrics/index.js
new file mode 100644
index 0000000000..bb2945686c
--- /dev/null
+++ b/awx/ui/src/screens/HostMetrics/index.js
@@ -0,0 +1 @@
+export { default } from './HostMetrics';
diff --git a/awx/ui/src/types.js b/awx/ui/src/types.js
index 57fd32029d..677f4edae8 100644
--- a/awx/ui/src/types.js
+++ b/awx/ui/src/types.js
@@ -439,3 +439,12 @@ export const Toast = shape({
hasTimeout: bool,
message: string,
});
+
+export const HostMetrics = shape({
+ hostname: string.isRequired,
+ first_automation: string.isRequired,
+ last_automation: string.isRequired,
+ automated_counter: number.isRequired,
+ used_in_inventories: number,
+ deleted_counter: number,
+});