diff --git a/src/layouts/admin-menu.jsx b/src/layouts/admin-menu.jsx
index 739eabe4..a6faadeb 100644
--- a/src/layouts/admin-menu.jsx
+++ b/src/layouts/admin-menu.jsx
@@ -601,6 +601,14 @@ const renderMenu = (t) => {
children: [],
hasBreadcrumb: true,
},
+ {
+ path: '/monitor-center/physical-node-admin',
+ name: t('Physical Node'),
+ key: 'monitorPhysicalNodeAdmin',
+ level: 1,
+ children: [],
+ hasBreadcrumb: true,
+ },
],
},
{
diff --git a/src/pages/monitor/containers/PhysicalNode/Charts.jsx b/src/pages/monitor/containers/PhysicalNode/Charts.jsx
new file mode 100644
index 00000000..b0be9aec
--- /dev/null
+++ b/src/pages/monitor/containers/PhysicalNode/Charts.jsx
@@ -0,0 +1,355 @@
+// Copyright 2021 99cloud
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React, { Component } from 'react';
+import { observer } from 'mobx-react';
+import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
+import ChartCard from 'components/PrometheusChart/ChartCard';
+import { ChartType } from 'components/PrometheusChart/utils/utils';
+import { Col, Progress, Row } from 'antd';
+import { merge, get } from 'lodash';
+import BaseCard from 'components/PrometheusChart/BaseCard';
+import { getSuitableValue } from 'resources/monitoring';
+import moment from 'moment';
+import { computePercentage, formatSize, formatUsedTime } from 'utils/index';
+import styles from './index.less';
+
+@observer
+class Charts extends Component {
+ constructor(props) {
+ super(props);
+ this.store = props.store;
+ }
+
+ renderTopCards() {
+ const baseConfig = {
+ span: 12,
+ constructorParams: {
+ requestType: 'current',
+ formatDataFn: handleResponses,
+ },
+ renderContent: (store) => (
+
{store.data}
+ ),
+ };
+ const chartLists = [
+ {
+ title: t('CPU Cores'),
+ span: 5,
+ constructorParams: {
+ metricKey: 'physicalNode.cpuCores',
+ },
+ renderContent: (store) => (
+
+ {get(store.data, 'length', 0)}
+
+ ),
+ },
+ {
+ title: t('Total Ram'),
+ span: 5,
+ constructorParams: {
+ metricKey: 'physicalNode.totalMem',
+ },
+ renderContent: (store) => (
+
+ {getSuitableValue(get(store.data[0], 'y', 0), 'memory')}
+
+ ),
+ },
+ {
+ title: t('System Running Time'),
+ span: 5,
+ constructorParams: {
+ metricKey: 'physicalNode.systemRunningTime',
+ },
+ renderContent: (store) => (
+
+ {formatUsedTime(
+ (moment().unix() -
+ parseInt(get(store.data[0], 'y', moment().unix()), 10)) *
+ 1000
+ )}
+
+ ),
+ },
+ {
+ title: t('File System Free Space'),
+ span: 9,
+ constructorParams: {
+ metricKey: 'physicalNode.fileSystemFreeSpace',
+ formatDataFn: (...rest) => {
+ const [data, typeKey, deviceKey] = rest;
+ const [avail, size] = data;
+ const { data: { result } = { result: [] } } = avail;
+ const temp = [];
+ result.forEach((item, index) => {
+ temp.push({
+ mountpoint:
+ get(item, `metric.${deviceKey}`) +
+ get(item, `metric.${typeKey}`),
+ avail: parseFloat(get(item, 'value[1]', 0)),
+ total: parseFloat(
+ get(size, `data.result[${index}].value[1]`, 0)
+ ),
+ });
+ });
+ return temp;
+ },
+ typeKey: 'mountpoint',
+ deviceKey: 'device',
+ },
+ renderContent: (store) => (
+
+ {(store.data || []).map((item, index) => {
+ const percentage = computePercentage(item.avail, item.total);
+ const percentageColor = percentage > 80 ? '#FAAD14' : '#1890FF';
+ return (
+
0 ? 16 : 0 }}
+ >
+
+
{item.mountpoint}
+
+ {`${formatSize(parseInt(item.avail, 10))} / ${formatSize(
+ parseInt(item.total, 10)
+ )}`}
+
+
+
+
+ );
+ })}
+
+ ),
+ },
+ ];
+ return (
+
+ {chartLists.map((chartProps) => {
+ const config = merge({}, baseConfig, chartProps);
+ const { span, ...rest } = config;
+ return (
+
+
+
+ );
+ })}
+
+ );
+ }
+
+ renderChartCards() {
+ const baseConfig = {
+ span: 12,
+ constructorParams: {
+ requestType: 'range',
+ formatDataFn: handleResponses,
+ },
+ chartProps: {
+ height: 250,
+ scale: {
+ y: {
+ nice: true,
+ },
+ },
+ },
+ };
+ const chartLists = [
+ {
+ title: t('CPU Usage(%)'),
+ constructorParams: {
+ metricKey: 'physicalNode.cpuUsage',
+ typeKey: 'mode',
+ },
+ chartProps: {
+ chartType: ChartType.MULTILINE,
+ },
+ },
+ {
+ title: t('Memory Usage'),
+ constructorParams: {
+ metricKey: 'physicalNode.memUsage',
+ modifyKeys: [t('Used'), t('Free')],
+ },
+ chartProps: {
+ scale: {
+ y: {
+ formatter: (d) => getSuitableValue(d, 'memory', 0),
+ },
+ },
+ chartType: ChartType.MULTILINE,
+ },
+ },
+ {
+ title: t('DISK IOPS'),
+ constructorParams: {
+ metricKey: 'physicalNode.diskIOPS',
+ modifyKeys: [t('read'), t('write')],
+ deviceKey: 'device',
+ },
+ chartProps: {
+ chartType: ChartType.MULTILINEDEVICES,
+ },
+ },
+ {
+ title: t('DISK Usage(%)'),
+ constructorParams: {
+ metricKey: 'physicalNode.diskUsage',
+ typeKey: 'hostname',
+ deviceKey: 'device',
+ },
+ chartProps: {
+ scale: {
+ y: {
+ alias: t('DISK Usage(%)'),
+ },
+ },
+ chartType: ChartType.ONELINEDEVICES,
+ },
+ },
+ {
+ title: t('System Load'),
+ span: 24,
+ constructorParams: {
+ metricKey: 'physicalNode.systemLoad',
+ typeKey: '__name__',
+ },
+ chartProps: {
+ chartType: ChartType.MULTILINE,
+ },
+ },
+ {
+ title: t('Network Traffic'),
+ span: 12,
+ constructorParams: {
+ metricKey: 'physicalNode.networkTraffic',
+ modifyKeys: [t('receive'), t('transmit')],
+ deviceKey: 'device',
+ },
+ chartProps: {
+ chartType: ChartType.MULTILINEDEVICES,
+ scale: {
+ y: {
+ formatter: (d) => getSuitableValue(d, 'traffic', 0),
+ },
+ },
+ },
+ },
+ {
+ title: t('TCP Connections'),
+ span: 12,
+ constructorParams: {
+ metricKey: 'physicalNode.tcpConnections',
+ },
+ chartProps: {
+ scale: {
+ y: {
+ alias: t('TCP Connections'),
+ },
+ },
+ chartType: ChartType.ONELINE,
+ },
+ },
+ {
+ title: t('Network Errors'),
+ span: 12,
+ constructorParams: {
+ metricKey: 'physicalNode.networkErrors',
+ typeKey: '__name__',
+ deviceKey: 'device',
+ },
+ chartProps: {
+ scale: {
+ y: {
+ alias: t('Network Errors'),
+ },
+ },
+ chartType: ChartType.ONELINE,
+ },
+ },
+ {
+ title: t('Network Dropped Packets'),
+ span: 12,
+ constructorParams: {
+ metricKey: 'physicalNode.networkDroppedPackets',
+ modifyKeys: [t('receive'), t('transmit')],
+ deviceKey: 'device',
+ },
+ chartProps: {
+ scale: {
+ y: {
+ alias: t('Network Dropped Packets'),
+ },
+ },
+ chartType: ChartType.MULTILINEDEVICES,
+ },
+ },
+ ];
+ return (
+
+ {chartLists.map((chartProps) => {
+ const config = merge({}, baseConfig, chartProps);
+ const { span, ...rest } = config;
+ return (
+
+
+
+ );
+ })}
+
+ );
+ }
+
+ render() {
+ if (this.store.isLoading) {
+ return null;
+ }
+ return (
+
+ {this.renderTopCards()}
+ {this.renderChartCards()}
+
+ );
+ }
+}
+
+export default Charts;
diff --git a/src/pages/monitor/containers/PhysicalNode/index.jsx b/src/pages/monitor/containers/PhysicalNode/index.jsx
new file mode 100644
index 00000000..4c2a67f7
--- /dev/null
+++ b/src/pages/monitor/containers/PhysicalNode/index.jsx
@@ -0,0 +1,28 @@
+// Copyright 2021 99cloud
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from 'react';
+import { observer } from 'mobx-react';
+import BaseContent from 'components/PrometheusChart/component/BaseContent';
+import Charts from './Charts';
+
+const PhysicalNode = () => {
+ function renderChartCards(store) {
+ return ;
+ }
+
+ return ;
+};
+
+export default observer(PhysicalNode);
diff --git a/src/pages/monitor/containers/PhysicalNode/index.less b/src/pages/monitor/containers/PhysicalNode/index.less
new file mode 100644
index 00000000..1b0cf28b
--- /dev/null
+++ b/src/pages/monitor/containers/PhysicalNode/index.less
@@ -0,0 +1,108 @@
+.header {
+ overflow: auto;
+ padding: 20px;
+
+ .range {
+ :global {
+ .ant-radio-button-wrapper {
+ color: rgba(0, 0, 0, .65);
+ }
+
+ .ant-radio-button-wrapper-checked {
+ color: #0068FF;
+ }
+ }
+ }
+
+ .download {
+ float: right;
+
+ :global {
+ .ant-btn-icon-only {
+ border-radius: 4px;
+ }
+ }
+ }
+}
+
+.myCardRow {
+ .top {
+ .content {
+ font-size: 24px;
+ text-align: center;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ :global {
+ .ant-card-bordered {
+ display: flex;
+ flex-direction: column;
+
+ .ant-card-body {
+ flex-grow: 1;
+ overflow: hidden;
+ padding-top: 0;
+ }
+
+ }
+ }
+ }
+
+ :global {
+ .ant-card-bordered {
+ box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.09);
+
+ .ant-card-head {
+ border-bottom: none;
+ }
+ }
+ }
+}
+
+
+.outer {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ overflow: hidden;
+ font-size: 12px;
+
+ .inner {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+
+ }
+
+ .inner::-webkit-scrollbar {
+ display: none;
+ }
+}
+
+.topContent{
+ font-size: 24px;
+ font-weight: 500;
+ height: 100px;
+ display: flex;
+ justify-content: center;
+ align-items:center;
+}
+
+.tabs {
+ :global {
+ .ant-tabs-tab {
+ margin-right: 20px;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .ant-tabs-nav::before {
+ border-bottom: none;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pages/monitor/routes/index.js b/src/pages/monitor/routes/index.js
index f83d4b74..10feacb5 100644
--- a/src/pages/monitor/routes/index.js
+++ b/src/pages/monitor/routes/index.js
@@ -14,6 +14,7 @@
import BaseLayout from 'layouts/Basic';
import E404 from 'pages/base/containers/404';
+import PhysicalNode from '../containers/PhysicalNode';
import Overview from '../containers/Overview';
const PATH = '/monitor-center';
@@ -23,6 +24,11 @@ export default [
component: BaseLayout,
routes: [
{ path: `${PATH}/overview-admin`, component: Overview, exact: true },
+ {
+ path: `${PATH}/physical-node-admin`,
+ component: PhysicalNode,
+ exact: true,
+ },
{ path: '*', component: E404 },
],
},