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 }, ], },