feature: Add monitor overview
add monitor overview Change-Id: I1e66ffde80ae2c162c1a68411cdd2cfa98ec8931
This commit is contained in:
parent
986891f430
commit
a210d80f5c
@ -18,7 +18,7 @@ import {
|
||||
DatabaseOutlined,
|
||||
CreditCardOutlined,
|
||||
GlobalOutlined,
|
||||
// ToolOutlined,
|
||||
MonitorOutlined,
|
||||
SettingOutlined,
|
||||
HomeOutlined,
|
||||
AppstoreOutlined,
|
||||
@ -587,6 +587,22 @@ const renderMenu = (t) => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/monitor-center',
|
||||
name: t('Monitor Center'),
|
||||
key: '/monitorCenterAdmin',
|
||||
icon: <MonitorOutlined />,
|
||||
children: [
|
||||
{
|
||||
path: '/monitor-center/overview-admin',
|
||||
name: t('Monitor Overview'),
|
||||
key: 'monitorOverviewAdmin',
|
||||
level: 1,
|
||||
children: [],
|
||||
hasBreadcrumb: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/configuration-admin',
|
||||
name: t('Global Setting'),
|
||||
|
@ -41,6 +41,9 @@ const Heat = lazy(() =>
|
||||
const UserCenter = lazy(() =>
|
||||
import(/* webpackChunkName: "user-center" */ 'pages/user-center/App')
|
||||
);
|
||||
const MonitorCenter = lazy(() =>
|
||||
import(/* webpackChunkName: "monitor-center" */ 'pages/monitor/App')
|
||||
);
|
||||
const E404 = lazy(() =>
|
||||
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
|
||||
);
|
||||
@ -77,6 +80,10 @@ export default [
|
||||
path: `/heat`,
|
||||
component: Heat,
|
||||
},
|
||||
{
|
||||
path: `/monitor-center`,
|
||||
component: MonitorCenter,
|
||||
},
|
||||
{
|
||||
path: `/user`,
|
||||
component: UserCenter,
|
||||
|
21
src/pages/monitor/App.jsx
Normal file
21
src/pages/monitor/App.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
// 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 renderRoutes from 'utils/RouterConfig';
|
||||
|
||||
import routes from './routes';
|
||||
|
||||
const App = (props) => renderRoutes(routes, props);
|
||||
|
||||
export default App;
|
@ -0,0 +1,55 @@
|
||||
// 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 { Empty, Row } from 'antd';
|
||||
import { Chart, Line, Tooltip } from 'bizcharts';
|
||||
import { observer } from 'mobx-react';
|
||||
import styles from '../../../index.less';
|
||||
|
||||
export default observer(({ data }) => {
|
||||
return (
|
||||
<div className={styles.card}>
|
||||
<Row justify="space-between">
|
||||
<span>{t('Last week alarm trend')}</span>
|
||||
<span>{t('time / 24h')}</span>
|
||||
</Row>
|
||||
<Row
|
||||
justify="center"
|
||||
align="middle"
|
||||
style={{ height: 272, paddingTop: 10 }}
|
||||
>
|
||||
{data.length === 0 ? <Empty /> : <Charts data={data} />}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
function Charts({ data }) {
|
||||
return (
|
||||
<Chart
|
||||
padding={[10, 20, 50, 50]}
|
||||
autoFit
|
||||
data={data}
|
||||
scale={{
|
||||
count: {
|
||||
nice: true,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Line position="date*count" />
|
||||
<Tooltip showCrosshairs lock />
|
||||
</Chart>
|
||||
);
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
// 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, { useState, useEffect } from 'react';
|
||||
import { Col, Row, Spin } from 'antd';
|
||||
import styles from 'pages/monitor/containers/Overview/index.less';
|
||||
import FetchPrometheusStore from 'components/PrometheusChart/store/FetchPrometheusStore';
|
||||
import { handleResponse } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import moment from 'moment';
|
||||
import AlertChart from './components/AlertChart';
|
||||
|
||||
const STEP = 15;
|
||||
|
||||
const Index = function () {
|
||||
const store = new FetchPrometheusStore({
|
||||
requestType: 'range',
|
||||
metricKey: 'monitorOverview.alertInfo',
|
||||
formatDataFn: (responses, typeKey, deviceKey, modifyKeys) => {
|
||||
const ret = [];
|
||||
responses.forEach((response, idx) => {
|
||||
ret.push(handleResponse(response, typeKey, deviceKey, modifyKeys[idx]));
|
||||
});
|
||||
return ret;
|
||||
},
|
||||
modifyKeys: ['cpu', 'memory'],
|
||||
});
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [cpuCount, setCpuCount] = useState(0);
|
||||
const [memCount, setMemCount] = useState(0);
|
||||
const [weekData, setWeekData] = useState(build7DaysData());
|
||||
|
||||
useEffect(() => {
|
||||
const end = moment();
|
||||
const start = moment().startOf('day');
|
||||
setIsLoading(true);
|
||||
store
|
||||
.fetchData({
|
||||
interval: STEP,
|
||||
currentRange: [start, end],
|
||||
})
|
||||
.then((d) => {
|
||||
const [cpuData, memoryData] = d;
|
||||
const newCpuCount = cpuData.reduce(
|
||||
(pre, cur, idx) =>
|
||||
idx > 0 && cur.x - cpuData[idx - 1].x > STEP ? pre + 1 : pre,
|
||||
0
|
||||
);
|
||||
const newMemoryCount = memoryData.reduce(
|
||||
(pre, cur, idx) =>
|
||||
idx > 0 && cur.x - memoryData[idx - 1].x > STEP ? pre + 1 : pre,
|
||||
0
|
||||
);
|
||||
setCpuCount(newCpuCount);
|
||||
setMemCount(newMemoryCount);
|
||||
weekData[6].count = newCpuCount + newMemoryCount;
|
||||
setWeekData([...weekData]);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return isLoading ? (
|
||||
<Spin />
|
||||
) : (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col flex="1 1">
|
||||
<div className={styles.card}>
|
||||
<Row style={{ height: '100%' }}>
|
||||
<Col span={12} className={styles.alertCardLine}>
|
||||
<div className={styles.number}>{cpuCount}</div>
|
||||
<div>{t('Today CPU usage > 80% alert')}</div>
|
||||
</Col>
|
||||
<Col span={12} className={styles.alertCardLine}>
|
||||
<div className={styles.number}>{memCount}</div>
|
||||
<div>{t('Today Memory usage > 80% alert')}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
<Col flex="0 1 440px">
|
||||
<AlertChart data={weekData} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
function build7DaysData() {
|
||||
const today = moment().startOf('day');
|
||||
const ret = [];
|
||||
for (let index = 6; index >= 0; index--) {
|
||||
ret.push({
|
||||
date: today.clone().subtract(index, 'day').format('MM-DD'),
|
||||
count: 0,
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default Index;
|
@ -0,0 +1,171 @@
|
||||
// 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 { Col, Progress, Row } from 'antd';
|
||||
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import { get, merge } from 'lodash';
|
||||
import { getSuitableValue } from 'resources/monitoring';
|
||||
import { computePercentage } from 'utils/index';
|
||||
import BaseCard from 'components/PrometheusChart/BaseCard';
|
||||
import styles from '../../index.less';
|
||||
|
||||
const ClusterCard = (props) => {
|
||||
const { store: propStore } = props;
|
||||
const baseConfig = {
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
requestType: 'current',
|
||||
formatDataFn: handleResponses,
|
||||
},
|
||||
visibleHeight: 55,
|
||||
renderContent: (store) => (
|
||||
<div className={styles.topContent}>{store.data}</div>
|
||||
),
|
||||
};
|
||||
const chartLists = [
|
||||
{
|
||||
title: t('Physical CPU Usage'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.physicalCPUUsage',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const { data } = store;
|
||||
const used = get(data[0], 'y', 0);
|
||||
const total = get(data[1], 'y', 0);
|
||||
return (
|
||||
<div className={styles.topContent} style={{ height: 55 }}>
|
||||
<div>
|
||||
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 28, fontWeight: 600 }}>
|
||||
{computePercentage(used, total)}
|
||||
</span>
|
||||
%
|
||||
</Row>
|
||||
<Row
|
||||
style={{
|
||||
alignItems: 'baseline',
|
||||
justifyContent: 'center',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{`${used} / ${total}`}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('Total Ram'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.physicalMemoryUsage',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const { data } = store;
|
||||
const usedValue = get(data[0], 'y', 0);
|
||||
const totalValue = get(data[1], 'y', 0);
|
||||
const used = getSuitableValue(usedValue, 'memory');
|
||||
const total = getSuitableValue(totalValue, 'memory');
|
||||
return (
|
||||
<div className={styles.topContent} style={{ height: 55 }}>
|
||||
<div>
|
||||
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 28, fontWeight: 600 }}>
|
||||
{computePercentage(usedValue, totalValue)}
|
||||
</span>
|
||||
%
|
||||
</Row>
|
||||
<Row
|
||||
style={{
|
||||
alignItems: 'baseline',
|
||||
justifyContent: 'center',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{`${used} / ${total}`}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('Physical Storage Usage'),
|
||||
span: 24,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.physicalStorageUsage',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const { data } = store;
|
||||
const usedValue = get(data[0], 'y', 0);
|
||||
const totalValue = get(data[1], 'y', 0);
|
||||
|
||||
const used = getSuitableValue(usedValue, 'disk');
|
||||
const total = getSuitableValue(totalValue, 'disk');
|
||||
const progressPercentage = computePercentage(usedValue, totalValue);
|
||||
return (
|
||||
<div className={styles.topContent} style={{ height: 55 }}>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<Row style={{ justifyContent: 'flex-end', height: '50%' }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 12,
|
||||
marginRight: 32,
|
||||
}}
|
||||
>
|
||||
{`${t('Used')} ${used} / ${t('Total')} ${total}`}
|
||||
</span>
|
||||
</Row>
|
||||
<Row style={{ height: '50%' }}>
|
||||
<Progress
|
||||
style={{ width: '95%' }}
|
||||
percent={progressPercentage}
|
||||
strokeColor={progressPercentage > 80 ? '#FAAD14' : '#1890FF'}
|
||||
showInfo={progressPercentage !== 100}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
{chartLists.map((chartProps) => {
|
||||
const config = merge({}, baseConfig, chartProps);
|
||||
const { span, ...rest } = config;
|
||||
return (
|
||||
<Col span={span} key={chartProps.constructorParams.metricKey}>
|
||||
<BaseCard
|
||||
{...rest}
|
||||
currentRange={propStore.currentRange}
|
||||
interval={propStore.interval}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClusterCard;
|
@ -0,0 +1,68 @@
|
||||
// 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 styles from 'pages/monitor/containers/Overview/index.less';
|
||||
import { observer } from 'mobx-react';
|
||||
import CircleChart from 'components/PrometheusChart/CircleWithRightLegend';
|
||||
import { get } from 'lodash';
|
||||
import BaseCard from 'components/PrometheusChart/BaseCard';
|
||||
|
||||
const ClusterChart = observer((props) => {
|
||||
const { store: propStore } = props;
|
||||
|
||||
const config = {
|
||||
title: t('Compute Node status'),
|
||||
constructorParams: {
|
||||
requestType: 'current',
|
||||
formatDataFn: (responses) => {
|
||||
const status = [
|
||||
{
|
||||
type: 'up',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
type: 'down',
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
const result = get(responses[0], 'data.result', []);
|
||||
result.forEach((sta) => {
|
||||
const idx = sta.metric.services_state === 'up' ? 0 : 1;
|
||||
status[idx].value += parseInt(sta.value[1], 10);
|
||||
});
|
||||
return status;
|
||||
},
|
||||
metricKey: 'monitorOverview.computeNodeStatus',
|
||||
},
|
||||
renderContent: (store) => (
|
||||
<div className={styles.topContent}>
|
||||
<div style={{ height: 218 }}>
|
||||
<CircleChart data={store.data} />
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
visibleHeight: 230,
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseCard
|
||||
{...config}
|
||||
currentRange={propStore.currentRange}
|
||||
interval={propStore.interval}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default ClusterChart;
|
@ -0,0 +1,155 @@
|
||||
// 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 { Col, Row } from 'antd';
|
||||
import { computePercentage } from 'utils/index';
|
||||
import {
|
||||
cephStatusColorMap,
|
||||
cephStatusMap,
|
||||
getSuitableValue,
|
||||
} from 'resources/monitoring';
|
||||
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import { get, merge } from 'lodash';
|
||||
import BaseCard from 'components/PrometheusChart/BaseCard';
|
||||
import styles from '../../index.less';
|
||||
|
||||
const StorageClusterCard = (props) => {
|
||||
const { store: propStore } = props;
|
||||
const baseConfig = {
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
requestType: 'current',
|
||||
formatDataFn: handleResponses,
|
||||
},
|
||||
visibleHeight: 65,
|
||||
renderContent: (store) => (
|
||||
<div className={styles.topContent}>{JSON.stringify(store.data)}</div>
|
||||
),
|
||||
};
|
||||
const chartLists = [
|
||||
{
|
||||
title: t('Storage Cluster Status'),
|
||||
span: 24,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.cephHealthStatus',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const data = get(store.data, 'y', 0);
|
||||
return (
|
||||
<div
|
||||
className={styles.topContent}
|
||||
style={{
|
||||
fontSize: 28,
|
||||
fontWeight: 600,
|
||||
color: cephStatusColorMap[data],
|
||||
height: 65,
|
||||
}}
|
||||
>
|
||||
{cephStatusMap[data]}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('Storage Cluster Usage'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.cephStorageUsage',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const { data } = store;
|
||||
const usedValue = get(data[0], 'y', 0);
|
||||
const totalValue = get(data[1], 'y', 0);
|
||||
const used = getSuitableValue(usedValue, 'disk');
|
||||
const total = getSuitableValue(totalValue, 'disk');
|
||||
return (
|
||||
<div className={styles.topContent} style={{ height: 55 }}>
|
||||
<div>
|
||||
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 28, fontWeight: 600 }}>
|
||||
{computePercentage(usedValue, totalValue)}
|
||||
</span>
|
||||
%
|
||||
</Row>
|
||||
<Row
|
||||
style={{
|
||||
alignItems: 'baseline',
|
||||
justifyContent: 'center',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{`${used} / ${total}`}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('Disk allocation (GB)'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.cephStorageAllocate',
|
||||
},
|
||||
renderContent: (store) => {
|
||||
const { data } = store;
|
||||
const totalValue = parseFloat(get(data[1], 'y', 0).toFixed(2));
|
||||
const usedValue = parseFloat(
|
||||
(totalValue - get(data[0], 'y', 0)).toFixed(2)
|
||||
);
|
||||
return (
|
||||
<div className={styles.topContent} style={{ height: 55 }}>
|
||||
<div>
|
||||
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: 28, fontWeight: 600 }}>
|
||||
{computePercentage(usedValue, totalValue)}
|
||||
</span>
|
||||
%
|
||||
</Row>
|
||||
<Row
|
||||
style={{
|
||||
alignItems: 'baseline',
|
||||
justifyContent: 'center',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
{`${usedValue} GB / ${totalValue} GB`}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
{chartLists.map((chartProps) => {
|
||||
const config = merge({}, baseConfig, chartProps);
|
||||
const { span, ...rest } = config;
|
||||
return (
|
||||
<Col span={span} key={chartProps.constructorParams.metricKey}>
|
||||
<BaseCard
|
||||
{...rest}
|
||||
currentRange={propStore.currentRange}
|
||||
interval={propStore.interval}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default StorageClusterCard;
|
@ -0,0 +1,57 @@
|
||||
// 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 ChartCard from 'components/PrometheusChart/ChartCard';
|
||||
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import { ChartType } from 'components/PrometheusChart/utils/utils';
|
||||
|
||||
const StorageClusterChart = observer((props) => {
|
||||
const { store: propStore } = props;
|
||||
|
||||
const config = {
|
||||
title: t('Storage Cluster IOPS'),
|
||||
constructorParams: {
|
||||
requestType: 'range',
|
||||
metricKey: 'monitorOverview.cephStorageClusterIOPS',
|
||||
formatDataFn: handleResponses,
|
||||
modifyKeys: [t('read'), t('write')],
|
||||
},
|
||||
chartProps: {
|
||||
chartType: ChartType.MULTILINE,
|
||||
height: 250,
|
||||
scale: {
|
||||
y: {
|
||||
nice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
visibleHeight: 230,
|
||||
};
|
||||
|
||||
return (
|
||||
<ChartCard
|
||||
{...config}
|
||||
currentRange={propStore.currentRange}
|
||||
interval={propStore.interval}
|
||||
BaseContentConfig={{
|
||||
...props.BaseContentConfig,
|
||||
renderTimeRangeSelect: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default StorageClusterChart;
|
@ -0,0 +1,187 @@
|
||||
// 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 { Col, Progress, Row, Tabs } from 'antd';
|
||||
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
|
||||
import { get, merge } from 'lodash';
|
||||
import BaseCard from 'components/PrometheusChart/BaseCard';
|
||||
import { Chart, Interval } from 'bizcharts';
|
||||
import { getSuitableValue } from 'resources/monitoring';
|
||||
import styles from '../../index.less';
|
||||
|
||||
const renderTopProgress = (store) => {
|
||||
const { data } = store;
|
||||
return (
|
||||
<Row style={{ height: '100%' }}>
|
||||
{data.map((d) => {
|
||||
const percentage = get(d, 'y', 0);
|
||||
const percentageColor = percentage > 80 ? '#FAAD14' : '#1890FF';
|
||||
return (
|
||||
<Col span={24} key={d.type}>
|
||||
<div>{d.type}</div>
|
||||
<Progress
|
||||
strokeColor={percentageColor}
|
||||
percent={percentage}
|
||||
style={{ marginBottom: 4 }}
|
||||
showInfo={percentage !== 100}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTopColumnExtra = (store) => {
|
||||
const { modifyKeys } = store;
|
||||
return (
|
||||
<Tabs
|
||||
className={styles.tabs}
|
||||
defaultActiveKey={modifyKeys[0]}
|
||||
onChange={(key) => store.handleDeviceChange(key)}
|
||||
>
|
||||
{modifyKeys.map((k) => (
|
||||
<Tabs.TabPane tab={k} key={k} />
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTopColumnChart = (store, scale = { y: {} }) => {
|
||||
const { data, modifyKeys } = store;
|
||||
return (
|
||||
<div>
|
||||
<Chart
|
||||
autoFit
|
||||
data={data.filter((d) => d.type === (store.device || modifyKeys[0]))}
|
||||
height={198}
|
||||
scale={{
|
||||
y: {
|
||||
nice: true,
|
||||
...scale.y,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Interval position="x*y" size={20} />
|
||||
</Chart>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TopCard = (props) => {
|
||||
const { store: propStore } = props;
|
||||
const baseConfig = {
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
requestType: 'current',
|
||||
formatDataFn: handleResponses,
|
||||
},
|
||||
visibleHeight: 200,
|
||||
renderContent: (store) => (
|
||||
<div className={styles.topContent}>{store.data}</div>
|
||||
),
|
||||
};
|
||||
const chartLists = [
|
||||
{
|
||||
title: t('Host CPU Usage'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.topHostCPUUsage',
|
||||
typeKey: 'instance',
|
||||
},
|
||||
renderContent: renderTopProgress,
|
||||
},
|
||||
{
|
||||
title: t('Host Disk Average IOPS'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.topHostDiskIOPS',
|
||||
formatDataFn: (reps, tk, dk, mk) => {
|
||||
const data = [];
|
||||
reps.forEach((ret, resIdx) => {
|
||||
(ret.data.result || []).forEach((d) => {
|
||||
data.push({
|
||||
x: d.metric.instance,
|
||||
y: parseFloat(get(d, 'value[1]', 0)),
|
||||
type: mk[resIdx],
|
||||
});
|
||||
});
|
||||
});
|
||||
return data;
|
||||
},
|
||||
modifyKeys: [t('read'), t('write')],
|
||||
},
|
||||
extra: renderTopColumnExtra,
|
||||
renderContent: renderTopColumnChart,
|
||||
},
|
||||
{
|
||||
title: t('Host Memory Usage'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.topHostMemoryUsage',
|
||||
typeKey: 'instance',
|
||||
},
|
||||
renderContent: renderTopProgress,
|
||||
},
|
||||
{
|
||||
title: t('Host Average Network IO'),
|
||||
span: 12,
|
||||
constructorParams: {
|
||||
metricKey: 'monitorOverview.topHostInterface',
|
||||
formatDataFn: (reps, tk, dk, mk) => {
|
||||
const data = [];
|
||||
reps.forEach((ret, resIdx) => {
|
||||
(ret.data.result || []).forEach((d) => {
|
||||
data.push({
|
||||
x: d.metric.instance,
|
||||
y: parseFloat(get(d, 'value[1]', 0)),
|
||||
type: mk[resIdx],
|
||||
});
|
||||
});
|
||||
});
|
||||
return data;
|
||||
},
|
||||
modifyKeys: [t('receive'), t('transmit')],
|
||||
},
|
||||
extra: renderTopColumnExtra,
|
||||
renderContent: (store) =>
|
||||
renderTopColumnChart(store, {
|
||||
y: {
|
||||
formatter: (d) => getSuitableValue(d, 'traffic', 0),
|
||||
},
|
||||
}),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
{chartLists.map((chartProps) => {
|
||||
const config = merge({}, baseConfig, chartProps);
|
||||
const { span, title, ...rest } = config;
|
||||
return (
|
||||
<Col span={span} key={chartProps.constructorParams.metricKey}>
|
||||
<BaseCard
|
||||
{...rest}
|
||||
title={`${title} TOP5`}
|
||||
currentRange={propStore.currentRange}
|
||||
interval={propStore.interval}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopCard;
|
72
src/pages/monitor/containers/Overview/index.jsx
Normal file
72
src/pages/monitor/containers/Overview/index.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
// 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 BaseContent from 'components/PrometheusChart/component/BaseContent';
|
||||
import { Col, Row } from 'antd';
|
||||
import styles from './index.less';
|
||||
import AlertInfo from './components/AlertInfo';
|
||||
import ClusterCard from './components/ClusterMonitor/ClusterCard';
|
||||
import ClusterChart from './components/ClusterMonitor/ClusterChart';
|
||||
import TopCard from './components/Tops/TopCard';
|
||||
import StorageClusterCard from './components/StorageClusterMonitor/StorageClusterCard';
|
||||
import StorageClusterChart from './components/StorageClusterMonitor/StorageClusterChart';
|
||||
|
||||
const BaseContentConfig = {
|
||||
renderNodeSelect: false,
|
||||
renderTimeRangeSelect: false,
|
||||
};
|
||||
|
||||
const Index = () => {
|
||||
return (
|
||||
<BaseContent
|
||||
{...BaseContentConfig}
|
||||
renderChartCards={(store) => (
|
||||
<Row gutter={[16, 16]} className={styles.container}>
|
||||
<Col span={24}>
|
||||
<AlertInfo />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<ClusterCard store={store} />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<ClusterChart store={store} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<TopCard store={store} />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<StorageClusterCard store={store} />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<StorageClusterChart
|
||||
store={store}
|
||||
BaseContentConfig={BaseContentConfig}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
126
src/pages/monitor/containers/Overview/index.less
Normal file
126
src/pages/monitor/containers/Overview/index.less
Normal file
@ -0,0 +1,126 @@
|
||||
.container {
|
||||
.card {
|
||||
padding: 16px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.05),
|
||||
0px 0px 10px 0px rgba(0, 0, 0, 0.05);
|
||||
color: #000000;
|
||||
height: 100%;
|
||||
|
||||
.tabs {
|
||||
:global {
|
||||
.ant-tabs-tab {
|
||||
margin-right: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.ant-tabs-nav::before {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.alertCardLine {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.number {
|
||||
font-size: 36px;
|
||||
color: rgb(232, 104, 74);
|
||||
}
|
||||
}
|
||||
|
||||
.alertCardTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.alertInfo {
|
||||
margin-top: 6px;
|
||||
|
||||
.alertNumber {
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.alertLevel {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.alertCardBottom {
|
||||
padding: 0 26px;
|
||||
|
||||
.buttonTitle {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #000000;
|
||||
margin-bottom: 15px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.alertLineInfo {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
height: 100%;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.outer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
|
||||
.inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.inner::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.flexColumnAround {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
29
src/pages/monitor/routes/index.js
Normal file
29
src/pages/monitor/routes/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 BaseLayout from 'layouts/Basic';
|
||||
import E404 from 'pages/base/containers/404';
|
||||
import Overview from '../containers/Overview';
|
||||
|
||||
const PATH = '/monitor-center';
|
||||
export default [
|
||||
{
|
||||
path: PATH,
|
||||
component: BaseLayout,
|
||||
routes: [
|
||||
{ path: `${PATH}/overview-admin`, component: Overview, exact: true },
|
||||
{ path: '*', component: E404 },
|
||||
],
|
||||
},
|
||||
];
|
Loading…
Reference in New Issue
Block a user