From eecf689a1c3044919c40dbb88fd83920f9ddcf7c Mon Sep 17 00:00:00 2001 From: Rivery <9858560+xigongdaEricyang@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:19:19 +0800 Subject: [PATCH] feat: v3.1.0 (#66) * feat: metrics upgrade * feat: add baseline modal * feat: refactor cloud version * feat: update docker image, and upgrade promql * feat: update * fix: setting baseline in machine modal * feat: add docker compose * fix: issue fix * refactor: icon (#68) * fix: issue fix (#69) * chore: add community deploy chore: docker-compose * chore: docker-compose * chore: docker-compose * feat: update disk usage info (#71) * fix: datepicker open error * chore: update docker compose file * feat: relase config * mod: icon (#75) * feat: add disk monitor * feat: update metric * fix: i18n change (#77) * fix: issue (#78) * fix: issue (#79) * fix: cloud issue (#80) * fix: cloud version metrics multi line (#81) * fix: storaged and metad don't have space level metrics (#82) fix: baseline not show when value is 0 fix: version upgrade left align * fix: batch import * fix: issue (#83) * fix: get metric list by nebula_cluster (#85) * mod: config-release (#86) * mod: server proxy (#87) * feat: customize cloud metrics (#88) feat: update icon from fill to color * mod: config (#89) * fix: lastet bugfix (#90) * fix: cloud issue (#92) * fix: i18n error (#93) * fix: some issue fix (#94) * fix: update cloud metric (#96) * fix: metric wrong issue (#97) * feat: add rpm/ deb sqlite (#98) * chore: update version (#100) * fix: use http gateway 2.2.2 (#101) * fix: use http gateway 3.1.4 (#102) * chore: update pack script * fix: space level metric (#104) Co-authored-by: li Nico <37568394+NicolaCage@users.noreply.github.com> --- .github/workflows/nightly.yml | 11 +- .github/workflows/release.yml | 9 +- README.md | 4 +- config.json | 6 +- config/webpack.config.base.ts | 58 +- docker-compose/docker-compose.yaml | 69 +++ package.json | 12 +- scripts/package.sh | 18 +- server/config.json | 12 +- server/dev-server.ts | 16 + server/server.js | 16 + src/App.less | 2 +- src/App.tsx | 10 +- src/components/BaseLineEdit/index.tsx | 75 --- .../index.less | 0 src/components/BaseLineEditModal/index.tsx | 114 ++++ .../Charts/BarChart/index.module.less | 9 + src/components/Charts/BarChart/index.tsx | 48 ++ src/components/Charts/LineChart.tsx | 150 +++-- src/components/Charts/SpaceChart.less | 269 +++++++-- src/components/Charts/SpaceChart.tsx | 237 ++++++-- src/components/DashboardCard/index.less | 4 + src/components/Icon/index.tsx | 4 +- src/components/Instruction/index.less | 2 +- src/components/MetricPopover/index.tsx | 4 +- .../MetricsFilterPanel/index.module.less | 58 ++ src/components/MetricsFilterPanel/index.tsx | 139 +++++ src/components/ModalWrapper/index.tsx | 24 + .../Service/ServiceHeader/index.less | 3 +- .../Service/ServiceHeader/index.tsx | 6 +- .../index.module.less | 21 + .../ServiceMetricsFilterPanel/index.tsx | 116 ++++ src/components/StatusPanel/index.tsx | 103 ++-- src/components/TimeSelect/index.module.less | 21 + src/components/TimeSelect/index.tsx | 84 +++ src/config/constants.ts | 4 +- src/config/locale/en-US.json | 209 ------- src/config/locale/en-US/base.json | 8 + src/config/locale/en-US/common.json | 37 ++ src/config/locale/en-US/component.json | 11 + src/config/locale/en-US/configServer.json | 11 + src/config/locale/en-US/description.json | 23 + src/config/locale/en-US/device.json | 24 + src/config/locale/en-US/index.ts | 24 + .../locale/en-US/metric_description.json | 124 ++++ src/config/locale/en-US/rules.json | 5 + src/config/locale/en-US/service.json | 26 + src/config/locale/zh-CN.json | 210 ------- src/config/locale/zh-CN/base.json | 8 + src/config/locale/zh-CN/common.json | 37 ++ src/config/locale/zh-CN/component.json | 11 + src/config/locale/zh-CN/configServer.json | 11 + src/config/locale/zh-CN/description.json | 23 + src/config/locale/zh-CN/device.json | 24 + src/config/locale/zh-CN/index.ts | 24 + .../locale/zh-CN/metric_description.json | 125 ++++ src/config/locale/zh-CN/rules.json | 5 + src/config/locale/zh-CN/service.json | 27 + src/config/service.ts | 5 + src/index.html | 6 + src/pages/LeaderDistribution/index.tsx | 2 +- src/pages/MachineDashboard/Cards/CPUCard.tsx | 12 +- src/pages/MachineDashboard/Cards/DiskCard.tsx | 28 +- src/pages/MachineDashboard/Cards/LoadCard.tsx | 11 +- .../MachineDashboard/Cards/MemoryCard.tsx | 13 +- .../MachineDashboard/Cards/NetworkIn.tsx | 11 +- .../MachineDashboard/Cards/NetworkOut.tsx | 11 +- .../MachineDashboard/Detail/CPUDetail.tsx | 6 +- .../MachineDashboard/Detail/DiskDetail.tsx | 8 +- .../MachineDashboard/Detail/LoadDetail.tsx | 8 +- .../MachineDashboard/Detail/MemoryDetail.tsx | 6 +- .../MachineDashboard/Detail/NetworkDetail.tsx | 6 +- src/pages/MachineDashboard/Detail/index.less | 124 +++- src/pages/MachineDashboard/Detail/index.tsx | 389 ++++++------- src/pages/MachineDashboard/index.less | 28 +- src/pages/MachineDashboard/index.tsx | 208 +++---- src/pages/MainPage/index.less | 359 ++++++------ src/pages/MainPage/index.tsx | 4 +- src/pages/MainPage/routes.tsx | 4 +- .../ServiceDashboard/Detail/Panel/index.tsx | 290 ---------- src/pages/ServiceDashboard/Detail/index.less | 178 +++--- src/pages/ServiceDashboard/Detail/index.tsx | 543 ++++++++---------- .../CustomServiceQueryPanel/index.less | 4 + .../CustomServiceQueryPanel/index.tsx | 183 +++--- .../ServiceOverview/index.less | 3 +- .../ServiceOverview/index.tsx | 12 +- src/pages/ServiceDashboard/index.less | 19 +- src/pages/ServiceDashboard/index.tsx | 126 ++-- src/pages/ServiceManage/ConfigInfo/index.less | 4 + src/pages/ServiceManage/ConfigInfo/index.tsx | 143 +++-- .../ServiceManage/ServiceInfo/index.less | 4 + src/pages/ServiceManage/ServiceInfo/index.tsx | 4 +- src/static/iconfont/iconfont.js | 2 +- src/static/images/Graphd-icon60.png | Bin 0 -> 3287 bytes src/static/images/Metad-icon60.png | Bin 0 -> 3167 bytes src/static/images/Storaged-icon60.png | Bin 0 -> 1672 bytes src/store/index.ts | 4 + src/store/models/index.ts | 6 +- src/store/models/machine.ts | 501 ++++++++-------- src/store/models/metric.ts | 146 +++-- src/store/models/nebula.ts | 17 +- src/store/models/service.ts | 245 +++++--- src/typings.d.ts | 2 + src/utils/HttpServiceManager.ts | 48 ++ src/utils/chart/chart.ts | 7 +- src/utils/dashboard.ts | 203 +++++-- src/utils/http.ts | 106 ++-- src/utils/index.ts | 26 + src/utils/interface.ts | 52 ++ src/utils/metric.ts | 19 +- src/utils/promQL.ts | 372 ++++++------ src/utils/service.ts | 51 ++ static/iconfont/iconfont.js | 2 +- tsconfig.json | 2 +- vendors/config-release.json | 12 +- vendors/nebula-stats-exporter/config.yaml | 12 - vendors/prometheus/prometheus.yaml | 4 +- 117 files changed, 4359 insertions(+), 2987 deletions(-) create mode 100644 docker-compose/docker-compose.yaml delete mode 100644 src/components/BaseLineEdit/index.tsx rename src/components/{BaseLineEdit => BaseLineEditModal}/index.less (100%) create mode 100644 src/components/BaseLineEditModal/index.tsx create mode 100644 src/components/Charts/BarChart/index.module.less create mode 100644 src/components/Charts/BarChart/index.tsx create mode 100644 src/components/MetricsFilterPanel/index.module.less create mode 100644 src/components/MetricsFilterPanel/index.tsx create mode 100644 src/components/ModalWrapper/index.tsx create mode 100644 src/components/ServiceMetricsFilterPanel/index.module.less create mode 100644 src/components/ServiceMetricsFilterPanel/index.tsx create mode 100644 src/components/TimeSelect/index.module.less create mode 100644 src/components/TimeSelect/index.tsx delete mode 100644 src/config/locale/en-US.json create mode 100644 src/config/locale/en-US/base.json create mode 100644 src/config/locale/en-US/common.json create mode 100644 src/config/locale/en-US/component.json create mode 100644 src/config/locale/en-US/configServer.json create mode 100644 src/config/locale/en-US/description.json create mode 100644 src/config/locale/en-US/device.json create mode 100644 src/config/locale/en-US/index.ts create mode 100644 src/config/locale/en-US/metric_description.json create mode 100644 src/config/locale/en-US/rules.json create mode 100644 src/config/locale/en-US/service.json delete mode 100644 src/config/locale/zh-CN.json create mode 100644 src/config/locale/zh-CN/base.json create mode 100644 src/config/locale/zh-CN/common.json create mode 100644 src/config/locale/zh-CN/component.json create mode 100644 src/config/locale/zh-CN/configServer.json create mode 100644 src/config/locale/zh-CN/description.json create mode 100644 src/config/locale/zh-CN/device.json create mode 100644 src/config/locale/zh-CN/index.ts create mode 100644 src/config/locale/zh-CN/metric_description.json create mode 100644 src/config/locale/zh-CN/rules.json create mode 100644 src/config/locale/zh-CN/service.json delete mode 100644 src/pages/ServiceDashboard/Detail/Panel/index.tsx create mode 100644 src/static/images/Graphd-icon60.png create mode 100644 src/static/images/Metad-icon60.png create mode 100644 src/static/images/Storaged-icon60.png create mode 100644 src/utils/HttpServiceManager.ts diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index f619d0d3..a4843e05 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -9,10 +9,7 @@ jobs: os: - centos7 container: - image: reg.vesoft-inc.com/dashboard/dashboard-dev:${{ matrix.os }} - credentials: - username: ${{ secrets.HARBOR_USERNAME }} - password: ${{ secrets.HARBOR_PASSWORD }} + image: vesoft/nebula-dev:${{ matrix.os }} steps: - name: keep workspace empty run: | @@ -24,7 +21,7 @@ jobs: with: repository: vesoft-inc/nebula-http-gateway path: source/nebula-http-gateway - ref: v2.2.1 + ref: v3.1.4 - uses: actions/setup-go@v2 with: go-version: '^1.13.1' @@ -34,8 +31,8 @@ jobs: - name: ls run: ls -a - name: Package - run: bash ./source/nebula-dashboard/scripts/package.sh ${{ secrets.GA_ID }} + run: bash ./source/nebula-dashboard/scripts/package.sh source/nebula-dashboard source/nebula-http-gateway true ${{ secrets.GA_ID }} - name: Upload to OSS - run: bash ./source/nebula-dashboard/scripts/upload.sh ${{ secrets.OSS_ENDPOINT }} ${{ secrets.OSS_ID }} ${{ secrets.OSS_SECRET }} ${{ secrets.OSS_URL }} + run: bash ./source/nebula-dashboard/scripts/upload.sh ${{ secrets.OSS_ENDPOINT }} ${{ secrets.OSS_ID }} ${{ secrets.OSS_SECRET }} ${{ secrets.OSS_TEST_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 218af0f5..9bcf3192 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,10 +12,7 @@ jobs: os: - centos7 container: - image: reg.vesoft-inc.com/dashboard/dashboard-dev:${{ matrix.os }} - credentials: - username: ${{ secrets.HARBOR_USERNAME }} - password: ${{ secrets.HARBOR_PASSWORD }} + image: vesoft/nebula-dev:${{ matrix.os }} steps: - name: keep workspace empty run: | @@ -27,7 +24,7 @@ jobs: with: repository: vesoft-inc/nebula-http-gateway path: source/nebula-http-gateway - ref: v2.2.1 + ref: v3.1.4 - uses: actions/setup-go@v2 with: go-version: '^1.13.1' @@ -37,7 +34,7 @@ jobs: - name: ls run: ls -a - name: Package - run: bash ./source/nebula-dashboard/scripts/package.sh ${{ secrets.GA_ID }} + run: ./source/nebula-dashboard/scripts/package.sh source/nebula-dashboard source/nebula-http-gateway false ${{ secrets.GA_ID }} - name: Upload to OSS run: bash ./source/nebula-dashboard/scripts/upload.sh ${{ secrets.OSS_ENDPOINT }} ${{ secrets.OSS_ID }} ${{ secrets.OSS_SECRET }} ${{ secrets.OSS_URL }} diff --git a/README.md b/README.md index 289e578f..e7f2510c 100644 --- a/README.md +++ b/README.md @@ -116,5 +116,5 @@ If you plan to set up dashboard in production, refer to:[production guide](DEP ## Documentation -+ [中文](https://docs.nebula-graph.com.cn/3.0.0/nebula-dashboard/1.what-is-dashboard/) -+ [ENGLISH](https://docs.nebula-graph.io/3.0.0/nebula-dashboard/1.what-is-dashboard/) ++ [中文](https://docs.nebula-graph.com.cn/3.2.0/nebula-dashboard/1.what-is-dashboard/) ++ [ENGLISH](https://docs.nebula-graph.io/3.2.0/nebula-dashboard/1.what-is-dashboard/) diff --git a/config.json b/config.json index 6c4da2ca..e8a10132 100644 --- a/config.json +++ b/config.json @@ -2,14 +2,14 @@ "port": 7003, "proxy":{ "gateway":{ - "target": "http://localhost:8090" + "target": "http://192.168.8.44:8090" }, "prometheus":{ - "target": "192.168.8.157:9090" + "target": "192.168.8.44:9090" } }, "nebulaServer": { - "ip": "192.168.8.143", + "ip": "192.168.8.131", "port": 9669 } } diff --git a/config/webpack.config.base.ts b/config/webpack.config.base.ts index a13c34f5..98877bcf 100644 --- a/config/webpack.config.base.ts +++ b/config/webpack.config.base.ts @@ -2,8 +2,13 @@ import path from 'path'; import { Configuration } from 'webpack'; import CopyPlugin from 'copy-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; // import pkg from '../package.json'; +const isDevEnv = () => process.env.NODE_ENV === 'development'; + +const useCssPlugin = () => !isDevEnv(); + const baseConifg: Configuration = { module: { rules: [ @@ -33,21 +38,26 @@ const baseConifg: Configuration = { include: path.join(__dirname, `../src`), }, { - test: /\.less/, + test: /\.css$/, use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - // url: true, - } - }, + useCssPlugin() ? MiniCssExtractPlugin.loader : 'style-loader', + 'css-loader', + 'postcss-loader', + ], + }, + { + test: /(? { res.send({ version: pkg.version, diff --git a/server/server.js b/server/server.js index 86f7869a..ae8a7358 100755 --- a/server/server.js +++ b/server/server.js @@ -52,6 +52,22 @@ app.get('/api/app', (_req, res) => { }) }); +app.use('/api-graph/*', createProxyMiddleware({ + target: getTargetUrl(proxy.graph.target), + pathRewrite: { + '/api-graph': '/', + }, + changeOrigin: true, +})); + +app.use('/api-storage/*', createProxyMiddleware({ + target: getTargetUrl(proxy.storage.target), + pathRewrite: { + '/api-storage': '/', + }, + changeOrigin: true, +})); + app.get('/api/config/custom', async (_req, res) => { if (nebulaServer) { res.send({ diff --git a/src/App.less b/src/App.less index bfcc87b5..250f7741 100644 --- a/src/App.less +++ b/src/App.less @@ -25,7 +25,7 @@ p { height: 100%; overflow: scroll; min-width: 1440px; - min-height: 1000px; + // min-height: 1000px; > .ant-spin-nested-loading { height: 100%; diff --git a/src/App.tsx b/src/App.tsx index 21af1925..c04b6344 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,9 @@ import { Switch, withRouter, } from 'react-router-dom'; +import dayjs from 'dayjs'; +import weekday from "dayjs/plugin/weekday" +import localeData from "dayjs/plugin/localeData" import { handleTrackEvent, trackEvent } from './utils/stat'; import { INTL_LOCALES } from '@/config'; import './App.less'; @@ -18,6 +21,9 @@ import { updateQueryStringParameter } from '@/utils'; import AuthorizedRoute from '@/AuthorizedRoute'; import Login from '@/pages/Login'; +dayjs.extend(weekday) +dayjs.extend(localeData) + // @ts-ignore const MainPage = lazy(() => import('@/pages/MainPage/index')); @@ -36,7 +42,7 @@ class App extends React.Component { if (match) { cookies.set('locale', match[1] === 'EN_US' ? 'EN_US' : 'ZH_CN'); } else { - cookies.set('locale', lang === 'en' ? 'EN_US' : 'ZH_CN'); + cookies.set('locale', lang === 'EN_US' ? 'EN_US' : 'ZH_CN'); } } @@ -55,7 +61,7 @@ class App extends React.Component { loadIntlLocale = () => { intl .init({ - currentLocale: this.currentLocale || 'zh_CN', + currentLocale: this.currentLocale || 'ZH_CN', locales: INTL_LOCALES, }) .then(() => { diff --git a/src/components/BaseLineEdit/index.tsx b/src/components/BaseLineEdit/index.tsx deleted file mode 100644 index fed88439..00000000 --- a/src/components/BaseLineEdit/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import { Button, Form, InputNumber, Select } from 'antd'; -import { FormInstance } from 'antd/lib/form'; -import intl from 'react-intl-universal'; -import { renderUnit } from '@/utils/dashboard'; - -import './index.less'; - -interface IProps { - baseLine: number; - valueType: string; - onClose: () => void; - onBaseLineChange: (values) => void; -} - -class BaseLineEdit extends React.Component { - formRef = React.createRef(); - - render() { - const { valueType, baseLine, onClose, onBaseLineChange } = this.props; - const units = renderUnit(valueType); - const initialValues = { baseLine } as any; - if (units.length) { - initialValues.unit = units[0]; - } - - return ( -
-
- - - - {units.length !== 0 && ( - - - - )} -
- - -
-
-
- ); - } -} - -export default BaseLineEdit; diff --git a/src/components/BaseLineEdit/index.less b/src/components/BaseLineEditModal/index.less similarity index 100% rename from src/components/BaseLineEdit/index.less rename to src/components/BaseLineEditModal/index.less diff --git a/src/components/BaseLineEditModal/index.tsx b/src/components/BaseLineEditModal/index.tsx new file mode 100644 index 00000000..b602ceb8 --- /dev/null +++ b/src/components/BaseLineEditModal/index.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { Button, Form, InputNumber, Modal, Select } from 'antd'; +import intl from 'react-intl-universal'; +import { renderUnit } from '@/utils/dashboard'; + +import './index.less'; +import ModalWrapper from '../ModalWrapper'; +import { VALUE_TYPE } from '@/utils/promQL'; + +interface IProps { + visible?: boolean; + baseLine: number; + valueType: string; + onOk?: (values) => void; + hide?: () => void; + title?: string; + onCancel?: () => void; +} + +function BaseLineEditModal(props: IProps) { + const { valueType, baseLine, onCancel, visible, hide, onOk, title } = props; + const units = useMemo(() => renderUnit(valueType), [valueType]); + const [curVisible, setCurVisible] = useState(visible); + const [form] = Form.useForm(); + + const getInitialValues = () => { + const initialValues = { baseLine } as any; + if (units.length) { + initialValues.unit = units[0]; + } + return initialValues; + } + + useEffect(() => { + setCurVisible(visible); + }, [visible]); + + useEffect(() => { + if (!curVisible) { + hide && hide(); + } + }, [curVisible]); + + const handleCancelClick = () => { + onCancel?.(); + setCurVisible(false); + }; + + const handleOkClick = () => { + setCurVisible(false); + form.validateFields().then(values => { + onOk?.(values) + }) + } + + return ( + +
+
+ + + + {units.length !== 0 && ( + + + + )} +
+ + +
+
+
+
+ ) +} + +export default ModalWrapper(BaseLineEditModal); diff --git a/src/components/Charts/BarChart/index.module.less b/src/components/Charts/BarChart/index.module.less new file mode 100644 index 00000000..8a0a2183 --- /dev/null +++ b/src/components/Charts/BarChart/index.module.less @@ -0,0 +1,9 @@ +.barChar { + width: 100%; + height: 100%; +} + +.nebula-chart-bar { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/components/Charts/BarChart/index.tsx b/src/components/Charts/BarChart/index.tsx new file mode 100644 index 00000000..5e853834 --- /dev/null +++ b/src/components/Charts/BarChart/index.tsx @@ -0,0 +1,48 @@ +import React, { useEffect, useRef } from 'react'; +import { Chart } from '@antv/g2'; + +export interface IProps { + renderChart: (chartInstance: Chart) => void; + options: any; +} + +function BarChart(props: IProps) { + + const { renderChart, options={} } = props; + + const chartRef = useRef(); + const chartInstanceRef = useRef(); + + const renderChartContent = () => { + if (!chartRef.current) return; + chartInstanceRef.current = new Chart({ + container: chartRef.current, + autoFit: true, + padding: [20, 0, 0, 0], + ...options, + }); + + renderChart(chartInstanceRef.current); + // HACK: G2 Chart autoFit don't take effect , refer: https://github.com/antvis/g2/commit/92d607ec5408d1ec949ebd95209c84b04c73b944, but not work + if (chartInstanceRef.current.height < 100) { + const e = document.createEvent('Event'); + e.initEvent('resize', true, true); + window.dispatchEvent(e); + } + chartInstanceRef.current!.render(true); + } + + useEffect(() => { + renderChartContent(); + }, [chartRef.current]); + + return ( +
chartRef.current = ref} + /> + ) +} + +export default BarChart; \ No newline at end of file diff --git a/src/components/Charts/LineChart.tsx b/src/components/Charts/LineChart.tsx index cee249d7..d96ba489 100644 --- a/src/components/Charts/LineChart.tsx +++ b/src/components/Charts/LineChart.tsx @@ -1,59 +1,113 @@ -import React from 'react'; -import { Chart } from '@antv/g2'; +import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; +import { Chart, registerInteraction } from '@antv/g2'; import { ChartCfg } from '@antv/g2/lib/interface'; export interface IProps { renderChart: (chartInstance: Chart) => void; options?: Partial; - tickInterval?: number; baseLine?: number; yAxisMaximum?: number; isDefaultScale?: boolean; } -class LineChart extends React.Component { - chartRef: any; +function LineChart(props: IProps, ref) { + const { isDefaultScale, yAxisMaximum, baseLine, options, renderChart } = props; - chartInstance: Chart; + const chartRef = useRef(); - constructor(props: IProps) { - super(props); - this.chartRef = React.createRef(); - } + const chartInstanceRef = useRef(); - componentDidMount() { - this.renderChart(); - } + const renderChartContent = () => { + if (!chartRef.current) return; + chartInstanceRef.current = new Chart({ + container: chartRef.current, + autoFit: true, + padding: [20, 0, 0, 0], + ...options, + }); + registerInteraction('brush', { + showEnable: [ + { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, + { trigger: 'plot:mouseleave', action: 'cursor:default' }, + ], + start: [ + { + trigger: 'plot:mousedown', + action: ['brush-x:start', 'rect-mask:start', 'rect-mask:show'], + }, + ], + processing: [ + { + trigger: 'plot:mousemove', + action: ['rect-mask:resize'], + }, + ], + end: [ + { + trigger: 'plot:mouseup', + action: ['brush-x:filter', 'brush:end', 'rect-mask:end', 'rect-mask:hide', 'reset-button:show'], + }, + ], + rollback: [{ trigger: 'reset-button:click', action: ['brush:reset', 'reset-button:hide'] }], + }); + chartInstanceRef.current.interaction('brush'); + if (baseLine) { + chartInstanceRef.current.annotation().line({ + start: ['min', baseLine], + end: ['max', baseLine], + style: { + stroke: '#e6522b', + lineWidth: 1, + lineDash: [3, 3], + }, + }); + } + showScaleByBaseLine(); + chartInstanceRef.current.interaction('brush'); + renderChart(chartInstanceRef.current); + chartInstanceRef.current.render(); + }; + + useEffect(() => { + renderChartContent(); + }, []); + + useEffect(() => { + updateChart(); + }, [options, baseLine]) - componentDidUpdate() { - this.updateChart(); - } + useImperativeHandle(ref, () => ({ + updateBaseline: (baseLine) => { + updateChart(baseLine); + }, + })) - showScaleByBaseLine = () => { - const { isDefaultScale, yAxisMaximum, baseLine } = this.props; + const showScaleByBaseLine = (curbaseLine?) => { + let baseLine = curbaseLine || props.baseLine if (isDefaultScale) { - this.chartInstance.scale({ + chartInstanceRef.current?.scale({ value: { min: 0, max: 100, tickInterval: 25, }, }); - } else if (yAxisMaximum === 0 && baseLine) { - this.chartInstance.scale('value', { + } else if ((yAxisMaximum === undefined || yAxisMaximum === 0) && baseLine) { + chartInstanceRef.current?.scale('value', { ticks: [0, baseLine, Math.round(baseLine * 1.5)], }); } else { - this.chartInstance.scale('value', { ticks: [] }); // If yAxisMaximum is not 0, you do not need to set scale + chartInstanceRef.current?.scale('value', { ticks: [] }); // If yAxisMaximum is not 0, you do not need to set scale } }; - updateChart = () => { - const { options, baseLine } = this.props; + const updateChart = (curbaseLine?) => { + let baseLine = curbaseLine || props.baseLine + if (!chartInstanceRef.current) return; if (baseLine !== undefined) { // HACK: baseLine could be 0 - this.chartInstance.annotation().clear(true); - this.chartInstance.annotation().line({ + chartInstanceRef.current?.annotation().clear(true); + chartInstanceRef.current?.annotation().line({ start: ['min', baseLine], end: ['max', baseLine], style: { @@ -63,51 +117,27 @@ class LineChart extends React.Component { zIndex: 999, }, }); - this.showScaleByBaseLine(); + showScaleByBaseLine(baseLine); } // HACK: updateOptions not work, https://github.com/antvis/G2/issues/2844 if (options) { - this.chartInstance.padding = options.padding as any; + chartInstanceRef.current.padding = options.padding as any; } // HACK: G2 Chart autoFit don't take effect , refer: https://github.com/antvis/g2/commit/92d607ec5408d1ec949ebd95209c84b04c73b944, but not work - if (this.chartInstance.height < 100) { + if (chartInstanceRef.current.height < 100) { const e = document.createEvent('Event'); e.initEvent('resize', true, true); window.dispatchEvent(e); } - this.chartInstance.render(true); - }; - - renderChart = () => { - const { options, baseLine } = this.props; - this.chartInstance = new Chart({ - container: this.chartRef.current, - autoFit: true, - padding: [20, 0, 0, 0], - ...options, - }); - if (baseLine) { - this.chartInstance.annotation().line({ - start: ['min', baseLine], - end: ['max', baseLine], - style: { - stroke: '#e6522b', - lineWidth: 1, - lineDash: [3, 3], - }, - }); + if (chartRef.current) { + chartInstanceRef.current.changeSize(chartRef.current.clientWidth, chartRef.current.clientHeight); } - this.showScaleByBaseLine(); - this.chartInstance.interaction('brush'); - this.props.renderChart(this.chartInstance); - this.chartInstance.render(); + chartInstanceRef.current!.render(true); }; - render() { - return ( -
- ); - } + return ( +
chartRef.current = ref} /> + ); } -export default LineChart; +export default forwardRef(LineChart); diff --git a/src/components/Charts/SpaceChart.less b/src/components/Charts/SpaceChart.less index e444a50d..dad2bed5 100644 --- a/src/components/Charts/SpaceChart.less +++ b/src/components/Charts/SpaceChart.less @@ -1,63 +1,210 @@ .nebula-chart-space { - padding: 12px 0; - overflow: auto; - > .tip{ - height: 30px; - color: #a5a2a2; - padding: 5px 0; - font-size: 12px; - text-align: left; - } - > .space-bar { - > .description { - font-size: 12px; - text-align: left; - - > span + span { - margin-left: 20px; - } - } - - > .wrap { - display: flex; - width: 100%; - height: 30px; - margin-top: 8px; - - - > .bar-item { - display: flex; - - > .left { - flex: 1; - opacity: 0.3; - height: 100%; - } - - > .right { - width: 5px; - height: 100%; - } - } - - > .empty { - flex: 1; - background: #ecf0ff; - opacity: 0.3; - } - - > p { - padding-left: 12px; - width: 66px; - line-height: 30px; - font-size: 18px; - font-weight: 400; - text-align: left; - } - } - } - - > .space-bar + .space-bar { - margin-top: 12px; - } + padding: 12px 0; + overflow: auto; + height: 100%; + max-height: 240px; + overflow-y: auto; + + >.tip { + height: 30px; + color: #a5a2a2; + padding: 5px 0; + font-size: 12px; + text-align: left; + } + + >.space-instance { + // padding: 24px; + position: relative; + // >.space-instance-description { + // position: absolute; + // top: 0; + // left: 24px; + // font-weight: bold; + // } + // >.space-bar { + // >.description { + // font-size: 12px; + // text-align: left; + + // >span+span { + // margin-left: 20px; + // } + // } + + + + // >.space-bar+.space-bar { + // margin-top: 12px; + // } + } +} + +.instance-type { + display: flex; + flex-direction: row; + height: 30px; + justify-content: center; + position: absolute; + bottom: 0; + width: 100%; + margin-bottom: 10px; +} + +.instance-item { + display: flex; + align-items: center; +} + +.instance-item:not(:first-child) { + margin-left: 12px; +} + +.instance-label { + width: 10px; + height: 2px; + background: red; + height: 2px; + width: 14px; + margin-left: 12px; +} + +.instance-name { + font-size: 12px; +} + +.instance-label-hidden { + background-color: #eee !important; +} + +.instance-name-hidden { + color: #eee !important; +} + +.disk-info-content { + display: flex; +} + +.disk-th { + display: grid; + grid-template-columns: 20% 20% 20% 40%; + text-align: left; + width: 100%; + height: 30px; + line-height: 30px; + font-style: normal; + font-weight: 600; + font-size: 14px; + color: #BDBDBD; +} + +.disk-tr { + display: grid; + grid-template-columns: 20% 20% 20% 40%; + width: 100%; + text-align: left; + border-bottom: 1px solid #E8E8E8; + padding: 20px 0; +} + +.disk-tr-item { + display: flex; + flex-direction: column; + justify-content: center; +} + +.disk-tr-item > div:not(:last-child) { + margin-bottom: 20px; +} + +.disk-tr-item-info { + align-items: center; + display: flex; + font-size: 12px; + height: 30px; + overflow: hidden; + line-height: 30px; +} + +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 90%; +} + +.disk-usage-detail { + height: 12px; +} + +.disk-usage-percent { + font-size: 12px !important; +} + +.disk-name { + font-style: normal; + font-weight: 600; + font-size: 12px; + line-height: 30px; + color: #000000; +} + +.disk-usage { + width: 100%; + // height: 30px; + // display: flex; +} + +.wrap { + display: flex; + width: 100%; + flex: 1; + margin-top: 8px; + display: flex; + align-items: center; + height: 30px; + + >.bar-item { + display: flex; + height: 100%; + + >.left { + flex: 1; + opacity: 0.3; + height: 100%; + } + + >.right { + width: 5px; + height: 100%; + } + } + + >.empty { + flex: 1; + background: #ecf0ff; + opacity: 0.3; + height: 100%; + } + + >p { + padding-left: 12px; + width: 66px; + line-height: 30px; + font-size: 18px; + font-weight: 400; + text-align: left; + } +} + +.instance-select { + position: absolute !important; + top: 10px; + right: 70px; + // width: 200px; + > .ant-select-arrow { + margin-top: -8px !important; + color: #000 !important; + } } diff --git a/src/components/Charts/SpaceChart.tsx b/src/components/Charts/SpaceChart.tsx index 2e5bd078..413fc5bd 100644 --- a/src/components/Charts/SpaceChart.tsx +++ b/src/components/Charts/SpaceChart.tsx @@ -1,71 +1,198 @@ -import React from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import intl from 'react-intl-universal'; + import { getProperByteDesc, getWhichColor } from '@/utils/dashboard'; +import { DiskMetricInfo } from '@/utils/interface'; import './SpaceChart.less'; +import _ from 'lodash'; +import { Popover, Select, Table } from 'antd'; +import Icon from '../Icon'; -interface IData { - type: string; - value: number; - size: number; -} interface IProps { - data?: IData[]; + diskInfos: DiskMetricInfo[]; } -interface ISpaceBarProps { - name: string; - used: number; - size: number; -} -const SpaceBar = (props: ISpaceBarProps) => { - const { name, used, size: bytes } = props; - const percent = Math.round((used / bytes) * 100); - const color = getWhichColor(percent); - const { desc: sizeDesc } = getProperByteDesc(bytes, 1024); - const { desc: usedDesc } = getProperByteDesc(used, 1024); - const _percent = percent < 1 ? Number(percent.toFixed(2)) : percent; - return ( -
-

- {name} - - {usedDesc}/{sizeDesc} - -

-
-
-
-
+function SpaceChart(props: IProps) { + const { diskInfos } = props; + + const diskInfoMap = useMemo(() => _.groupBy(diskInfos, 'name'), [diskInfos]) + + const instances: string[] = useMemo(() => Object.keys(diskInfoMap), [diskInfoMap]) + + const [curInstances, setCurInstances] = useState([]); + + const [seletedInstance, setSeletedInstance] = useState('all'); + + useEffect(() => { + setCurInstances(instances); + setSeletedInstance('all'); + }, [instances]) + + const diskThs = useMemo(() => ({ + name: diskInfos.some(i => !!i.name), + device: diskInfos.some(i => !!i.device), + mountpoint: diskInfos.some(i => !!i.mountpoint), + used: diskInfos.some(i => !!(i.size && i.used)) + }), [diskInfos]) + + const getDisplayInfos = (infos: DiskMetricInfo[]) => { + return infos.map((info) => { + const { used, size: bytes, device, mountpoint, name } = info; + const percent = Math.round((used / bytes) * 100); + const { desc: sizeDesc } = getProperByteDesc(bytes, 1024); + const { desc: usedDesc } = getProperByteDesc(used, 1024); + return { + percent: percent < 1 ? Number(percent.toFixed(2)) : percent, + device, + mountpoint, + sizeDesc, + usedDesc, + name, + color: getWhichColor(percent) + } + }) + } + + const handleInstanceShow = (instance: string | 'all') => { + if (instance === 'all') { + setCurInstances(instances); + } else { + setCurInstances([instance]); + } + setSeletedInstance(instance); + } + + const renderDiskInfo = () => { + return curInstances.map(instance => { + const displayInfos = getDisplayInfos(diskInfoMap[instance] || []); + return ( +
+ { + diskThs.name && ( +
+ +
{instance}
+
+
+ ) + } + { + diskThs.device && ( +
+ { + displayInfos.map(i => i.device).map((device, i) => ( + +
{device}
+
+ )) + } +
+ ) + } + { + diskThs.mountpoint && ( +
+ { + displayInfos.map(i => i.mountpoint).map((mountpoint, i) => ( + +
{mountpoint}
+
+ )) + } +
+ ) + } +
+ { + displayInfos.map((displayInfo, i) => ( +
+
{displayInfo.usedDesc}/{displayInfo.sizeDesc}
+
+
+
+
+
+
+

{displayInfo.percent}%

+
+
+ )) + } +
-
-

{_percent}%

-
-
- ); -}; - -class SpaceChart extends React.Component { - render() { - let { data } = this.props; - const showTip = !!data?.length && data?.length > 2; - if (showTip) { - data = data?.slice(0, 3); + ) + }) + } + + const gridValue = useMemo(() => { + const count = Object.values(diskThs).filter(t => t).length; + switch (count) { + case 2: + return '40% 60%' + case 3: + return '30% 40% 40%' + case 4: + return '20% 20% 20% 40%' + default: + return '20% 20% 20% 40%' } + }, [diskThs]) + + const renderDiskTabelHead = () => { + return ( -
- {data?.map(instance => ( - - ))} - {showTip &&

{intl.get('device.diskTip')}

} +
+ { + diskThs.name &&
{intl.get('base.spaceChartInstance')}
+ } + { + diskThs.device &&
{intl.get('base.spaceChartDiskname')}
+ } + { + diskThs.mountpoint &&
{intl.get('base.spaceChartMountpoint')}
+ } +
{intl.get('base.spaceChartDiskused')}
- ); + ) } + + return ( +
+
+
+ {renderDiskTabelHead()} +
+ {renderDiskInfo()} +
+ +
+ ); } export default SpaceChart; diff --git a/src/components/DashboardCard/index.less b/src/components/DashboardCard/index.less index 89ce986b..950c59f6 100644 --- a/src/components/DashboardCard/index.less +++ b/src/components/DashboardCard/index.less @@ -1,5 +1,7 @@ .dashboard-card { padding: 5px; + min-height: 293px; + height: calc((100vh - 285px)/ 3); > .inner { height: 100%; @@ -71,6 +73,8 @@ > .disk-detail { padding-left: 20px; + height: 100%; + padding-bottom: 20px; } } } diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 52577c7f..6bc90b9d 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -10,11 +10,11 @@ const Icon = (props: IProps) => { const { icon, className, ...others } = props; return ( ); }; diff --git a/src/components/Instruction/index.less b/src/components/Instruction/index.less index 9f014b48..7cb6a3db 100644 --- a/src/components/Instruction/index.less +++ b/src/components/Instruction/index.less @@ -3,7 +3,7 @@ .icon-instruction { margin: 0 5px; - fill: rgba(140, 140, 140, 1); + color: rgba(140, 140, 140, 1); width: 16px; height: 16px; } diff --git a/src/components/MetricPopover/index.tsx b/src/components/MetricPopover/index.tsx index c9312409..8d29ef3a 100644 --- a/src/components/MetricPopover/index.tsx +++ b/src/components/MetricPopover/index.tsx @@ -10,8 +10,8 @@ export const MetricPopover = (props: { list }) => { const locale = cookies.get('locale'); const manualHref = locale === 'ZH_CN' - ? 'https://docs.nebula-graph.com.cn/3.0.0/nebula-dashboard/6.monitor-parameter/' - : 'https://docs.nebula-graph.io/3.0.0/nebula-dashboard/6.monitor-parameter/'; // TODO update english docs link + ? 'https://docs.nebula-graph.com.cn/3.2.0/nebula-dashboard/6.monitor-parameter/' + : 'https://docs.nebula-graph.io/3.2.0/nebula-dashboard/6.monitor-parameter/'; // TODO update english docs link return ( +
+ +
+ ) +} + +interface IProps { + instanceList: string[]; + onChange?: (values) => void; + children?: React.ReactNode; + initialValues?: any; + values?: any; + onRefresh?: (values) => void; +} + +const MetricsFilterPanel = (props: IProps, ref) => { + + const { instanceList, onChange, children, values = {}, onRefresh } = props; + + const [form] = Form.useForm(); + + const treeData = useMemo(() => ( + [ + { + title: 'all', + value: 'all', + key: 'all', + children: instanceList.map(instance => ({ + title: instance, + value: instance, + key: instance, + children: [], + })), + } + ]), [instanceList]); + + const handleFormChange = () => { + onChange?.(form.getFieldsValue()); + } + + useEffect(() => { + if (values) { + form.setFieldsValue(values); + } + }, [values]) + + const handleTimeSelectChange = (value: TIME_OPTION_TYPE | number[]) => { + form.setFieldsValue({ + timeRange: value, + }); + onChange?.(form.getFieldsValue()); + } + + const handleFrequencyChange = (value: number) => { + form.setFieldsValue({ + frequency: value, + }); + onChange?.(form.getFieldsValue()); + } + + const handleInstanceChange = (value) => { + form.setFieldsValue({ + instances: value, + }); + onChange?.(form.getFieldsValue()); + } + + useImperativeHandle(ref, () => ({ + getForm: (): FormInstance => form, + })) + + const handleRefresh = () => { + onRefresh?.(form.getFieldsValue()) + } + + return ( +
+ + + + { + !isCloudVersion() && ( + + + + ) + } + + + + { + children ? children : null + } +
+ ) +} + +export default forwardRef(MetricsFilterPanel); \ No newline at end of file diff --git a/src/components/ModalWrapper/index.tsx b/src/components/ModalWrapper/index.tsx new file mode 100644 index 00000000..e1d7fce4 --- /dev/null +++ b/src/components/ModalWrapper/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +function ModalWrapper(Component: React.FC) { + const container = document.createElement('div'); + const hide = () => { + setTimeout(() => { + ReactDOM.unmountComponentAtNode(container); + document.body.removeChild(container); + }, 200); + }; + + const show = (props: T) => { + document.body.appendChild(container); + ReactDOM.render(, container); + }; + + return { + show, + hide, + }; +} + +export default ModalWrapper; diff --git a/src/components/Service/ServiceHeader/index.less b/src/components/Service/ServiceHeader/index.less index 7e4657dd..0718164d 100644 --- a/src/components/Service/ServiceHeader/index.less +++ b/src/components/Service/ServiceHeader/index.less @@ -1,8 +1,7 @@ .service-content-header { height: 50px; - padding: 12px 20px; + padding: 12px; width: 100%; - border-bottom: 1px solid #d9d9d9; display: flex; justify-content: space-between; diff --git a/src/components/Service/ServiceHeader/index.tsx b/src/components/Service/ServiceHeader/index.tsx index e5848903..568c5497 100644 --- a/src/components/Service/ServiceHeader/index.tsx +++ b/src/components/Service/ServiceHeader/index.tsx @@ -1,8 +1,8 @@ import React from 'react'; import Icon from '@/components/Icon'; -import metadIcon from '@/static/images/Meta-image.png'; -import graphdIcon from '@/static/images/Graphd-image.png'; -import storagedIcon from '@/static/images/Storage-image.png'; +import metadIcon from '@/static/images/Metad-icon60.png'; +import graphdIcon from '@/static/images/Graphd-icon60.png'; +import storagedIcon from '@/static/images/Storaged-icon60.png'; import './index.less'; interface IProps { diff --git a/src/components/ServiceMetricsFilterPanel/index.module.less b/src/components/ServiceMetricsFilterPanel/index.module.less new file mode 100644 index 00000000..93f91fa1 --- /dev/null +++ b/src/components/ServiceMetricsFilterPanel/index.module.less @@ -0,0 +1,21 @@ +.filterPanelContent { + display: flex; + align-items: center; +} + +.filterPanel { + display: flex; + align-items: center; + // flex: 1; + + :global { + .ant-form-item { + margin-bottom: 20px !important; + margin-right: 10px !important; + } + .ant-form-item-label { + display: flex !important; + align-items: center; + } + } +} \ No newline at end of file diff --git a/src/components/ServiceMetricsFilterPanel/index.tsx b/src/components/ServiceMetricsFilterPanel/index.tsx new file mode 100644 index 00000000..fa616a58 --- /dev/null +++ b/src/components/ServiceMetricsFilterPanel/index.tsx @@ -0,0 +1,116 @@ +import React, { useEffect, useMemo, useRef } from 'react'; +import { Form } from 'antd'; +import intl from 'react-intl-universal'; + +import MetricsFilterPanel from '../MetricsFilterPanel'; +import { DashboardSelect, Option } from '../DashboardSelect'; + +import styles from './index.module.less'; +import { AGGREGATION_OPTIONS, TIME_INTERVAL_OPTIONS } from '@/utils/dashboard'; + +interface IProps { + spaces?: string[]; + instanceList: string[]; + onChange?: (values) => void; + values?: any; + onRefresh?: (values: any) => void; +} + +function ServiceMetricsFilterPanel(props: IProps) { + const { spaces, instanceList, onChange, values, onRefresh } = props; + + const panelRef = useRef() + + const form = useMemo(() => panelRef.current?.getForm(), [panelRef.current]); + + useEffect(() => { + if (!form) return; + if (values) { + form.setFieldsValue(values); + } + }, [values, form]); + + const handleSpaceChange = (value) => { + if (!form) return; + form.setFieldsValue({ + space: value, + }); + onChange?.(form.getFieldsValue()); + } + + const handlePeriodChange = (value) => { + if (!form) return; + form.setFieldsValue({ + period: value, + }); + onChange?.(form.getFieldsValue()); + } + + const handleMetricTypeChange = (value) => { + if (!form) return; + form.setFieldsValue({ + metricType: value, + }); + onChange?.(form.getFieldsValue()); + } + + const handleFilterPanelChange = () => { + onChange?.(form.getFieldsValue()); + } + + return ( +
+ + { + spaces && ( + + + + {spaces.map(space => ( + + ))} + + + ) + } + + + {TIME_INTERVAL_OPTIONS.map(value => ( + + ))} + + + + + {AGGREGATION_OPTIONS.map(type => ( + + ))} + + + +
+ ) +} + +export default ServiceMetricsFilterPanel; \ No newline at end of file diff --git a/src/components/StatusPanel/index.tsx b/src/components/StatusPanel/index.tsx index c457eebe..dc86c342 100644 --- a/src/components/StatusPanel/index.tsx +++ b/src/components/StatusPanel/index.tsx @@ -1,77 +1,80 @@ -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import intl from 'react-intl-universal'; import { connect } from 'react-redux'; -import { IDispatch } from '@/store'; import { NEBULA_COUNT } from '@/utils/promQL'; import { DETAIL_DEFAULT_RANGE } from '@/utils/dashboard'; -import { SERVICE_POLLING_INTERVAL } from '@/utils/service'; - +import { shouldCheckCluster } from '@/utils'; import './index.less'; -const mapState = () => ({}); - -const mapDispatch = (dispatch: IDispatch) => ({ - asyncGetStatus: dispatch.service.asyncGetStatus, +const mapState = (state: any) => ({ + cluster: state.cluster?.cluster, + metricsFilterValues: state.machine.metricsFilterValues, }); -interface IProps extends ReturnType { +const mapDispatch = (dispatch) => ({}); + +interface IProps extends ReturnType { type: string; + clusterID?: string; getStatus: (payload) => void; } -interface IState { - normal: number; - abnormal: number; -} -class StatusPanel extends React.PureComponent { - pollingTimer: any; +function StatusPanel(props: IProps) { - constructor(props: IProps) { - super(props); - this.state = { - normal: 0, - abnormal: 0, - }; - } + const { cluster, type, getStatus, metricsFilterValues } = props; - componentDidMount() { - this.pollingData(); - } + const pollingTimer: any = useRef(); - pollingData = () => { - this.asyncGetStatus(); - this.pollingTimer = setTimeout(this.pollingData, SERVICE_POLLING_INTERVAL); - }; + const [ statusNumInfo, setStatusNumInfo ] = useState({ + abnormal: 0, + normal: 0 + }); - componentWillUnmount() { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); + useEffect(() => { + if (pollingTimer.current) { + clearTimeout(pollingTimer.current); } - } + if (shouldCheckCluster()) { + if (cluster.id) { + pollingData(); + } + } else { + pollingData(); + } + return () => { + if (pollingTimer.current) { + clearTimeout(pollingTimer.current); + } + } + }, [cluster, metricsFilterValues.frequency, metricsFilterValues.timeRange]) - asyncGetStatus = async () => { - const { type } = this.props; - const { normal, abnormal } = (await this.props.getStatus({ + const asyncGetStatus = async () => { + const { normal, abnormal } = (await getStatus({ query: NEBULA_COUNT[type], end: Date.now(), interval: DETAIL_DEFAULT_RANGE, + clusterID: cluster?.id, })) as any; - this.setState({ normal, abnormal }); + setStatusNumInfo({ normal, abnormal }) + }; + + const pollingData = () => { + asyncGetStatus(); + if (metricsFilterValues.frequency > 0) { + pollingTimer.current = setTimeout(pollingData, metricsFilterValues.frequency); + } }; - render() { - const { normal, abnormal } = this.state; - return ( -
    -
  • - {intl.get('service.normal')}: {normal} -
  • -
  • - {intl.get('service.abnormal')}: {abnormal} -
  • -
- ); - } + return ( +
    +
  • + {intl.get('service.normal')}: {statusNumInfo.normal} +
  • +
  • + {intl.get('service.abnormal')}: {statusNumInfo.abnormal} +
  • +
+ ); } export default connect(mapState, mapDispatch)(StatusPanel); diff --git a/src/components/TimeSelect/index.module.less b/src/components/TimeSelect/index.module.less new file mode 100644 index 00000000..d6beb74b --- /dev/null +++ b/src/components/TimeSelect/index.module.less @@ -0,0 +1,21 @@ +.timeSelect { + display: flex; + align-items: center; + + :global { + .ant-radio-group { + > label { + height: 34px; + line-height: 34px; + font-size: 12px; + text-align: center; + padding: 0 10px; + } + } + + .ant-picker-range { + height: 34px; + margin-left: 16px; + } + } +} \ No newline at end of file diff --git a/src/components/TimeSelect/index.tsx b/src/components/TimeSelect/index.tsx new file mode 100644 index 00000000..60acdcba --- /dev/null +++ b/src/components/TimeSelect/index.tsx @@ -0,0 +1,84 @@ +import React, { useState, useEffect } from 'react'; +import { DatePicker, Radio } from 'antd'; +import intl from 'react-intl-universal'; +import dayjs from 'dayjs'; + +import { + TIMEOPTIONS, TIME_OPTION_TYPE, +} from '@/utils/dashboard'; + +import styles from './index.module.less'; + +interface IProps { + value?: TIME_OPTION_TYPE | number[]; + onChange?: (value: any) => void; +} + +const TimeSelect = (props: IProps) => { + + const { value, onChange } = props; + + const [curTimeOption, setCurTimeOption ] = useState(); + + const [cusTimeRange, setCusTimeRange] = useState(); + + useEffect(() => { + if (value) { + if (typeof value === 'string') { + setCurTimeOption(value) + } else if (Array.isArray(value)) { + const [startTime, endTime] = value; + setCusTimeRange([startTime, endTime]); + } + } + }, [value]) + + const disabledDate = (current) => { + return ( + current < dayjs().subtract(14, 'days').endOf('day') || + current > dayjs().endOf('day') + ); + } + + const handleTimeButtonClick = (e) => { + setCusTimeRange(undefined); + const timeOption = e.target.value; + setCurTimeOption(timeOption); + onChange?.(timeOption); + } + + const handleDataPickerChange = (date) => { + setCurTimeOption(undefined); + const timeRange = [date[0].valueOf(), date[1].valueOf()]; + setCusTimeRange(timeRange); + onChange?.(timeRange) + } + + return ( +
+ + {TIMEOPTIONS.map(option => ( + + {intl.get(`component.dashboardDetail.${option.name}`)} + + ))} + + + +
+ ) +} + +export default TimeSelect; \ No newline at end of file diff --git a/src/config/constants.ts b/src/config/constants.ts index 0a773f02..af2b91fb 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -1,5 +1,5 @@ -import enUS from './locale/en-US.json'; -import zhCN from './locale/zh-CN.json'; +import enUS from './locale/en-US'; +import zhCN from './locale/zh-CN'; export const INTL_LOCALE_SELECT = { EN_US: { diff --git a/src/config/locale/en-US.json b/src/config/locale/en-US.json deleted file mode 100644 index 0f67054c..00000000 --- a/src/config/locale/en-US.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "common": { - "account": "Account Login", - "login": "Login", - "logout": "Logout", - "username": "Please enter user name", - "password": "Please enter password", - "rememberPassword": "Remember password", - "version": "Version", - "dashboard": "Dashboard", - "machine": "Machine", - "service": "Service", - "serviceManagement": "Management", - "serviceInfo": "Service Info", - "partitionInfo": "Partition Info", - "config": "Config", - "longTermTask": "Long-term Task", - "metric": "Metric", - "lineChart": "Line Chart", - "barChart": "Bar Chart", - "view": "View", - "set": "Set up", - "cancel": "Cancel", - "confirm": "Confirm", - "overview": "Overview", - "baseLine": "Base Line", - "editBaseLine": "Edit Base Line", - "baseLineTip": "Please enter a number greater than 0", - "unit": "Unit", - "help": "Help", - "manual": "User Manual", - "forum": "Forum", - "forumLink": "https://discuss.nebula-graph.io/", - "all":"All" - }, - "rules": { - "usernameRequired": "Username Required", - "passwordRequired": "Password Required", - "versionRequired":"Enter the nebula version" - }, - "device": { - "title": "Machine", - "overview": "Overview", - "cpu": "CPU", - "disk": "Disk", - "memory": "Memory", - "load": "Load", - "network": "Network", - "networkIn": "Network In", - "networkOut": "Network Out", - "transmitFlow": "Transimit Speed", - "receiveFlow": "Receive Speed", - "diskTip":"For details of all disks, click the icon in the upper right corner.", - "detail": { - "cpu": "CPU Detail", - "memory": "Memory Detail", - "disk": "Disk Detail", - "network": "Network Detail", - "up": "Up", - "down": "Down", - "average": "Average", - "all": "All" - } - }, - "service": { - "serviceStatus": "Status(now)", - "version":"Nebula version", - "normal": "Normal", - "overload": "Overload", - "abnormal": "Abnormal", - "qps": "QPS", - "leaderNumber": "Leader Number", - "leaderDistribute": "Leader distribution", - "all":"All", - "type":"Service Type", - "total":"Total", - "spaces":"Spaces", - "services":"Services", - "partition":"Partition", - "partitionNum": "Partition Number", - "distribution": "Distribution", - "instance":"Instance", - "period":"Period", - "metric":"Metric", - "metricParams":"Metric Methods", - "queryCondition":"Query Condition", - "serviceMetricsDetails": "Service Metrics Details", - "chooseSpace": "Please Choose Space", - "enterPartitionId": "Please enter the partitionId" - }, - "component": { - "dashboardDetail": { - "1hour": "1 Hour", - "6hour": "6 Hours", - "12hour": "12 Hours", - "1day": "1 Day", - "3day": "3 Days", - "7day": "7 Days", - "14day": "14 Days" - } - }, - "configServer": { - "connect": "Connect", - "host": "Host", - "username": "Username", - "password": "Password", - "success": "succeed", - "fail": "Failed", - "clear": "Clear Connect", - "title": "Config Server", - "connectError": "Connection refused, config again" - }, - "description": { - "module": "The name of the service", - "moduleName": "The name of the parameter", - "moduleType": "The data type of Value", - "moduleMode": "The mode of the parameter", - "moduleValue": "The value of the parameter", - "ip": "The IP address of the host", - "port": "The port of the host", - "status": "Host Status", - "gitInfo": "The Commit ID of the current version", - "leaderCount": "The number of leaders", - "partitionDistribution": "The distribution of partitions", - "leaderDistribution": "The distribution of leaders", - "partitionId": "The ID of the partition", - "leader": "The IP address and port of the leader", - "peers": "The IP addresses and ports of all the replicas", - "losts": "The IP addresses and ports of replicas at fault", - "jobId": "The first row shows the job ID, and the other rows show the task IDs.", - "command": "The first row shows the command executed, and the other rows show on which storaged processes the task is running.", - "longTermStatus": "Shows the status of the job or task. ", - "longTermStartTime": "Shows a timestamp indicating the time when the job or task enters the RUNNING phase.", - "longTermStopTime": "Shows a timestamp indicating the time when the job or task gets FINISHED, FAILED, or STOPPED." - }, - "metric_description": { - "cpu_utilization": "The percentage of used CPU", - "cpu_idle": "The percentage of idled CPU", - "cpu_wait": "The percentage of CPU waiting for IO operations", - "cpu_user": "The percentage of CPU used by users", - "cpu_system": "The percentage of CPU used by the system", - "memory_utilization": "The percentage of used memory", - "memory_used": "The memory space used (including caches)", - "memory_actual_used": "The memory space used (not including caches)", - "memory_free": "The memory space available", - "load_1m": "The average load of the system in the last 1 minute", - "load_5m": "The average load of the system in the last 5 minutes", - "load_15m": "The average load of the system in the last 15 minutes", - "disk_used": "The disk space used", - "disk_free": "The disk space available", - "disk_readbytes": "The number of bytes that the system reads in the disk per second", - "disk_writebytes": "The number of bytes that the system writes in the disk per second", - "disk_readiops": "The number of read queries that the disk receives per second", - "disk_writeiops": "The number of write queries that the disk receives per second", - "inode_utilization": "The percentage of used inode", - "network_in_rate": "The number of bytes that the network card receives per second", - "network_out_rate": "The number of bytes that the network card sends out per second", - "network_in_errs": "The number of wrong bytes that the network card receives per second", - "network_out_errs": "The number of wrong bytes that the network card sends out per second", - "network_in_packets": "The number of data packages that the network card receives per second", - "network_out_packets": "The number of data packages that the network card sends out per second", - "num_queries": "The number of queries", - "num_slow_queries": "The number of slow queries", - "query_latency_us": "The average latency of queries", - "slow_query_latency_us": "The average latency of slow queries", - "num_query_errors": "The number of queries in error", - "heartbeat_latency_us": "The latency of heartbeats", - "num_heartbeats": "The number of heartbeats", - "add_edges_atomic_latency_us": "The single latency of adding edges", - "add_edges_latency_us": "The average latency of adding edges", - "add_vertices_latency_us": "The average latency of adding vertices", - "delete_edges_latency_us": "The average latency of deleting edges", - "delete_vertices_latency_us": "The average latency of deleting vertices", - "forward_tranx_latency_us": "The average latency of transmitting.", - "get_neighbors_latency_us": "The average latency of querying neighbors", - "num_active_queries": "The number of queries currently being executed", - "num_sentences": "Number of queries received by the service", - "num_query_errors_leader_changes": "The number of queries to which Leader Changes occurred", - "num_killed_queries": "The number of queries that were killed", - "optimizer_latency_us": "Optimizer phase delay", - "num_aggregate_executors": "Aggregate operator delay", - "num_sort_executors": "Sort operator delay", - "num_indexscan_executors": "indexscan operator delay", - "num_oom_queries": "The number of oom queries", - "num_queries_hit_memory_watermark":"The number of queries that reached the memory threshold", - "num_opened_sessions": "Number of sessions established by the server", - "num_auth_failed_sessions": "Number of sessions that failed login authentication", - "num_auth_failed_sessions_bad_username_password": "Number of sessions that failed to authenticate due to incorrect user names and passwords", - "num_auth_failed_sessions_out_of_max_allowed": "Number of sessions that failed to validate the FLAG_OUT_OF_MAX_ALLOWED_CONNECTIONS parameter", - "num_active_sessions": "Number of active sessions", - "num_reclaimed_expired_sessions": "Number of expired sessions reclaimed by the server", - "num_added_jobs": "Number of tasks added", - "num_stoped_jobs": "Number of stopped tasks", - "commit_log_latency_us": "Raft commit log delay", - "commit_snapshot_latency_us": "Raft commit snapshot of time delay", - "transfer_leader_latency_us": "Raft transfer leader delay", - "num_rpc_sent_to_metad": "Number of RPC requests sent to Metad", - "num_rpc_sent_to_metad_failed": "Number of FAILED RPC requests sent to Metad", - "num_rpc_sent_to_storaged": "Number of RPC requests sent to Storaged", - "num_rpc_sent_to_storaged_failed": "Number of FAILED RPC requests sent to Storaged", - "num_raft_votes": "Raft votes", - "num_edges_inserted": "Number of edges inserted", - "num_vertices_inserted": "Number of points inserted", - "num_edges_deleted": "The number of edges to delete", - "num_tags_deleted": "Number of tags removed", - "num_vertices_deleted": "The number of points deleted", - "more": "More" - } -} \ No newline at end of file diff --git a/src/config/locale/en-US/base.json b/src/config/locale/en-US/base.json new file mode 100644 index 00000000..7b8c460a --- /dev/null +++ b/src/config/locale/en-US/base.json @@ -0,0 +1,8 @@ +{ + "baseLineModalTitle": "Baseline Edit", + "spaceChartAllInstance": "All Instances", + "spaceChartInstance": "instance", + "spaceChartDiskname": "Disk Name", + "spaceChartMountpoint": "Mount Point", + "spaceChartDiskused": "Disk Used" +} \ No newline at end of file diff --git a/src/config/locale/en-US/common.json b/src/config/locale/en-US/common.json new file mode 100644 index 00000000..a1ae60f3 --- /dev/null +++ b/src/config/locale/en-US/common.json @@ -0,0 +1,37 @@ +{ + "account": "Account Login", + "login": "Login", + "logout": "Logout", + "username": "Please enter user name", + "password": "Please enter password", + "rememberPassword": "Remember password", + "version": "Version", + "dashboard": "Dashboard", + "machine": "Machine", + "service": "Service", + "serviceManagement": "Management", + "serviceInfo": "Service Info", + "partitionInfo": "Partition Info", + "config": "Config", + "longTermTask": "Long-term Task", + "metric": "Metric", + "lineChart": "Line Chart", + "barChart": "Bar Chart", + "view": "View", + "set": "Set up", + "cancel": "Cancel", + "confirm": "Confirm", + "overview": "Overview", + "baseLine": "Base Line", + "editBaseLine": "Edit Base Line", + "baseLineTip": "Please enter a number greater than 0", + "unit": "Unit", + "help": "Help", + "manual": "User Manual", + "forum": "Forum", + "forumLink": "https://discuss.nebula-graph.io/", + "configTip": "Please enter a configuration name", + "all":"All", + "metricLabel": "instance", + "updateFrequency": "refresh frequency" +} \ No newline at end of file diff --git a/src/config/locale/en-US/component.json b/src/config/locale/en-US/component.json new file mode 100644 index 00000000..fe0c905a --- /dev/null +++ b/src/config/locale/en-US/component.json @@ -0,0 +1,11 @@ +{ + "dashboardDetail": { + "1hour": "1 Hour", + "6hour": "6 Hours", + "12hour": "12 Hours", + "1day": "1 Day", + "3day": "3 Days", + "7day": "7 Days", + "14day": "14 Days" + } +} \ No newline at end of file diff --git a/src/config/locale/en-US/configServer.json b/src/config/locale/en-US/configServer.json new file mode 100644 index 00000000..c06878d0 --- /dev/null +++ b/src/config/locale/en-US/configServer.json @@ -0,0 +1,11 @@ +{ + "connect": "Connect", + "host": "Host", + "username": "Username", + "password": "Password", + "success": "succeed", + "fail": "Failed", + "clear": "Clear Connect", + "title": "Config Server", + "connectError": "Connection refused, config again" +} \ No newline at end of file diff --git a/src/config/locale/en-US/description.json b/src/config/locale/en-US/description.json new file mode 100644 index 00000000..d5489f9d --- /dev/null +++ b/src/config/locale/en-US/description.json @@ -0,0 +1,23 @@ +{ + "module": "The name of the service", + "moduleName": "The name of the parameter", + "moduleType": "The data type of Value", + "moduleMode": "The mode of the parameter", + "moduleValue": "The value of the parameter", + "ip": "The IP address of the host", + "port": "The port of the host", + "status": "Host Status", + "gitInfo": "The Commit ID of the current version", + "leaderCount": "The number of leaders", + "partitionDistribution": "The distribution of partitions", + "leaderDistribution": "The distribution of leaders", + "partitionId": "The ID of the partition", + "leader": "The IP address and port of the leader", + "peers": "The IP addresses and ports of all the replicas", + "losts": "The IP addresses and ports of replicas at fault", + "jobId": "The first row shows the job ID, and the other rows show the task IDs.", + "command": "The first row shows the command executed, and the other rows show on which storaged processes the task is running.", + "longTermStatus": "Shows the status of the job or task. ", + "longTermStartTime": "Shows a timestamp indicating the time when the job or task enters the RUNNING phase.", + "longTermStopTime": "Shows a timestamp indicating the time when the job or task gets FINISHED, FAILED, or STOPPED." +} \ No newline at end of file diff --git a/src/config/locale/en-US/device.json b/src/config/locale/en-US/device.json new file mode 100644 index 00000000..1c8a52a8 --- /dev/null +++ b/src/config/locale/en-US/device.json @@ -0,0 +1,24 @@ +{ + "title": "Machine", + "overview": "Overview", + "cpu": "CPU", + "disk": "Disk", + "memory": "Memory", + "load": "Load", + "network": "Network", + "networkIn": "Network In", + "networkOut": "Network Out", + "transmitFlow": "Transimit Speed", + "receiveFlow": "Receive Speed", + "diskTip":"For details of all disks, click the icon in the upper right corner.", + "detail": { + "cpu": "CPU Detail", + "memory": "Memory Detail", + "disk": "Disk Detail", + "network": "Network Detail", + "up": "Up", + "down": "Down", + "average": "Average", + "all": "All" + } +} \ No newline at end of file diff --git a/src/config/locale/en-US/index.ts b/src/config/locale/en-US/index.ts new file mode 100644 index 00000000..9c7d1e0f --- /dev/null +++ b/src/config/locale/en-US/index.ts @@ -0,0 +1,24 @@ +import common from './common.json'; +import component from './component.json'; +import configServer from './configServer.json'; +import description from './description.json'; +import device from './device.json'; +import metric_description from './metric_description.json'; +import rules from './rules.json'; +import service from './service.json'; +import base from './base.json'; + +const enUS = { + common, + component, + configServer, + description, + metric_description, + device, + alert, + rules, + service, + base, +}; + +export default enUS; \ No newline at end of file diff --git a/src/config/locale/en-US/metric_description.json b/src/config/locale/en-US/metric_description.json new file mode 100644 index 00000000..f39cdbab --- /dev/null +++ b/src/config/locale/en-US/metric_description.json @@ -0,0 +1,124 @@ +{ + "cpu_utilization": "The percentage of used CPU", + "cpu_idle": "The percentage of idled CPU", + "cpu_wait": "The percentage of CPU waiting for IO operations", + "cpu_user": "The percentage of CPU used by users", + "cpu_system": "The percentage of CPU used by the system", + "memory_utilization": "The percentage of used memory", + "memory_used": "The memory space used (including caches)", + "memory_actual_used": "The memory space used (not including caches)", + "memory_free": "The memory space available", + "load_1m": "The average load of the system in the last 1 minute", + "load_5m": "The average load of the system in the last 5 minutes", + "load_15m": "The average load of the system in the last 15 minutes", + "disk_used": "The disk space used", + "disk_free": "The disk space available", + "disk_readbytes": "The number of bytes that the system reads in the disk per second", + "disk_writebytes": "The number of bytes that the system writes in the disk per second", + "disk_readiops": "The number of read queries that the disk receives per second", + "disk_writeiops": "The number of write queries that the disk receives per second", + "inode_utilization": "The percentage of used inode", + "network_in_rate": "The number of bytes that the network card receives per second", + "network_out_rate": "The number of bytes that the network card sends out per second", + "network_in_errs": "The number of wrong bytes that the network card receives per second", + "network_out_errs": "The number of wrong bytes that the network card sends out per second", + "network_in_packets": "The number of data packages that the network card receives per second", + "network_out_packets": "The number of data packages that the network card sends out per second", + "num_queries": "The number of queries", + "num_slow_queries": "The number of slow queries", + "query_latency_us": "The average latency of queries", + "slow_query_latency_us": "The average latency of slow queries", + "num_query_errors": "The number of queries in error", + "heartbeat_latency_us": "The latency of heartbeats", + "num_heartbeats": "The number of heartbeats", + "add_edges_atomic_latency_us": "The single latency of adding edges", + "add_edges_latency_us": "The average latency of adding edges", + "add_vertices_latency_us": "The average latency of adding vertices", + "delete_edges_latency_us": "The average latency of deleting edges", + "delete_vertices_latency_us": "The average latency of deleting vertices", + "get_neighbors_latency_us": "The average latency of querying neighbors", + "num_active_queries": "The number of queries currently being executed", + "num_sentences": "Number of queries received by the service", + "num_query_errors_leader_changes": "The number of queries to which Leader Changes occurred", + "num_killed_queries": "The number of queries that were killed", + "optimizer_latency_us": "Optimizer phase delay", + "num_aggregate_executors": "Aggregate operator delay", + "num_sort_executors": "Sort operator delay", + "num_indexscan_executors": "indexscan operator delay", + "num_oom_queries": "The number of oom queries", + "num_queries_hit_memory_watermark":"The number of queries that reached the memory threshold", + "num_opened_sessions": "Number of sessions established by the server", + "num_auth_failed_sessions": "Number of sessions that failed login authentication", + "num_auth_failed_sessions_bad_username_password": "Number of sessions that failed to authenticate due to incorrect user names and passwords", + "num_auth_failed_sessions_out_of_max_allowed": "Number of sessions that failed to validate the FLAG_OUT_OF_MAX_ALLOWED_CONNECTIONS parameter", + "num_active_sessions": "Number of active sessions", + "num_reclaimed_expired_sessions": "Number of expired sessions reclaimed by the server", + "num_added_jobs": "Number of tasks added", + "num_stoped_jobs": "Number of stopped tasks", + "commit_log_latency_us": "Raft commit log delay", + "commit_snapshot_latency_us": "Raft commit snapshot of time delay", + "num_rpc_sent_to_storaged": "Number of RPC requests sent to Storaged", + "num_rpc_sent_to_storaged_failed": "Number of FAILED RPC requests sent to Storaged", + "num_agent_heartbeats": "The number of heartbeats for the AgentHBProcessor.", + "agent_heartbeat_latency_us": "The average latency of the AgentHBProcessor.", + "num_get_prop": "The number of executions for the GetPropProcessor.", + "num_get_neighbors_errors": "The number of execution errors for the GetNeighborsProcessor.", + "get_prop_latency_us": "The average latency of executions for the GetPropProcessor.", + "num_edges_deleted": "The number of deleted edges.", + "num_edges_inserted": "The number of inserted edges.", + "num_raft_votes": "The number of votes in Raft.", + "num_rpc_sent_to_metad_failed": "The number of failed RPC requests that the Storage service sent to the Meta service.", + "num_rpc_sent_to_metad": "The number of RPC requests that the Storaged service sent to the Metad service.", + "num_tags_deleted": "The number of deleted tags.", + "num_vertices_deleted": "The number of deleted vertices.", + "num_vertices_inserted": "The number of inserted vertices.", + "transfer_leader_latency_us": "The latency of transferring the raft leader.", + "lookup_latency_us": "The average latency of executions for the LookupProcessor.", + "num_lookup_errors": "The number of execution errors for the LookupProcessor.", + "num_scan_vertex": "The number of executions for the ScanVertexProcessor.", + "num_scan_vertex_errors": "The number of execution errors for the ScanVertexProcessor.", + "update_edge_latency_us": "The average latency of executions for the UpdateEdgeProcessor.", + "num_update_vertex": "The number of executions for the UpdateVertexProcessor.", + "num_update_vertex_errors": "The number of execution errors for the UpdateVertexProcessor.", + "kv_get_latency_us": "The average latency of executions for the Getprocessor.", + "kv_put_latency_us": "The average latency of executions for the PutProcessor.", + "kv_remove_latency_us": "The average latency of executions for the RemoveProcessor.", + "num_kv_get_errors": "The number of execution errors for the GetProcessor.", + "num_kv_get": "The number of executions for the GetProcessor.", + "num_kv_put_errors": "The number of execution errors for the PutProcessor.", + "num_kv_put": "The number of executions for the PutProcessor.", + "num_kv_remove_errors": "The number of execution errors for the RemoveProcessor.", + "num_kv_remove": "The number of executions for the RemoveProcessor.", + "forward_tranx_latency_us": "The average latency of transmission.", + "scan_edge_latency_us": "The average latency of executions for the ScanEdgeProcessor.", + "num_scan_edge_errors": "The number of execution errors for the ScanEdgeProcessor.", + "num_scan_edge": "The number of executions for the ScanEdgeProcessor.", + "scan_vertex_latency_us": "The latency of executions for the ScanVertexProcessor.", + "num_add_edges": "The number of times that edges are added.", + "num_add_edges_errors": "The number of errors when adding edges.", + "num_add_vertices": "The number of times that vertices are added.", + "num_start_elect": "The number of times that Raft starts an election.", + "num_add_vertices_errors": "The number of errors when adding vertices.", + "num_delete_vertices_errors": "The number of errors when deleting vertices.", + "append_log_latency_us": "The latency of replicating the log record to a single node by Raft.", + "num_grant_votes": "The number of times that Raft votes for other nodes.", + "replicate_log_latency_us": "The latency of replicating the log record to most nodes by Raft.", + "num_delete_tags": "The number of times that tags are deleted.", + "num_delete_tags_errors": "The number of errors when deleting tags.", + "num_delete_edges": "The number of edge deletions.", + "num_delete_edges_errors": "The number of errors when deleting edges", + "num_send_snapshot": "The number of times that snapshots are sent.", + "update_vertex_latency_us": "The latency of executions for the UpdateVertexProcessor.", + "append_wal_latency_us": "The Raft write latency for a single WAL.", + "num_update_edge": "The number of executions for the UpdateEdgeProcessor.", + "delete_tags_latency_us": "The average latency of deleting tags.", + "num_update_edge_errors": "The number of execution errors for the UpdateEdgeProcessor.", + "num_get_neighbors": "The number of executions for the GetNeighborsProcessor.", + "num_get_prop_errors": "The number of execution errors for the GetPropProcessor.", + "num_delete_vertices": "The number of times that vertices are deleted.", + "num_lookup": "The number of executions for the LookupProcessor.", + "disk_used_percentage": "The percentage of disk used.", + "num_sync_data": "Storage 同步 Drainer 数据的次数。", + "num_sync_data_errors": "Storage 同步 Drainer 数据出错的次数。", + "more": "More" +} \ No newline at end of file diff --git a/src/config/locale/en-US/rules.json b/src/config/locale/en-US/rules.json new file mode 100644 index 00000000..8f6d7ed2 --- /dev/null +++ b/src/config/locale/en-US/rules.json @@ -0,0 +1,5 @@ +{ + "usernameRequired": "Username Required", + "passwordRequired": "Password Required", + "versionRequired":"Enter the nebula version" +} \ No newline at end of file diff --git a/src/config/locale/en-US/service.json b/src/config/locale/en-US/service.json new file mode 100644 index 00000000..e89ccc5e --- /dev/null +++ b/src/config/locale/en-US/service.json @@ -0,0 +1,26 @@ +{ + "serviceStatus": "Status(now)", + "version":"Nebula version", + "normal": "Normal", + "overload": "Overload", + "abnormal": "Abnormal", + "qps": "QPS", + "leaderNumber": "Leader Number", + "leaderDistribute": "Leader distribution", + "all":"All", + "type":"Service Type", + "total":"Total", + "spaces":"Spaces", + "services":"Services", + "partition":"Partition", + "partitionNum": "Partition Number", + "distribution": "Distribution", + "instance":"Instance", + "period":"Period(s)", + "metric":"Metric", + "metricParams":"Metric Methods", + "queryCondition":"Query Condition", + "serviceMetricsDetails": "Service Metrics Details", + "chooseSpace": "Please Choose Space", + "enterPartitionId": "Please enter the partitionId" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN.json b/src/config/locale/zh-CN.json deleted file mode 100644 index 8a565337..00000000 --- a/src/config/locale/zh-CN.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "common": { - "account": "账户登录", - "login": "登录", - "logout": "登出", - "username": "请输入用户名", - "password": "请输入密码", - "rememberPassword": "记住密码", - "version": "版本", - "dashboard": "看板", - "machine": "机器", - "service": "服务", - "serviceManagement": "管理", - "serviceInfo": "服务信息", - "partitionInfo": "分片信息", - "config": "配置", - "longTermTask": "长时任务", - "metric": "指标", - "lineChart": "折线图", - "barChart": "柱状图", - "view": "查看", - "set": "设置", - "cancel": "取消", - "confirm": "确认", - "overview": "概览", - "baseLine": "基线", - "editBaseLine": "编辑基线", - "baseLineTip": "请输入大于0的值", - "unit": "单位", - "help": "帮助", - "manual": "使用手册", - "forum": "求助论坛", - "forumLink": "https://discuss.nebula-graph.com.cn/", - "all":"全部" - }, - "rules": { - "usernameRequired": "请输入用户名", - "passwordRequired": "请输入密码", - "versionRequired":"请输入nebula版本" - }, - "device": { - "title": "机器", - "overview": "概览", - "cpu": "CPU", - "disk": "磁盘", - "memory": "内存", - "load": "负载", - "network": "流量", - "networkIn": "下行流量", - "networkOut": "上行流量", - "transmitFlow": "上行平均流量", - "receiveFlow": "下行平均流量", - "diskTip":"当前只显示使用率靠前的部分磁盘,详情信息请点击右上角图标", - "detail": { - "cpu": "CPU 监控", - "memory": "内存监控", - "disk": "磁盘监控", - "flow": "流量监控", - "up": "上行", - "down": "下行", - "average": "平均值", - "all": "全部" - } - }, - "service": { - "serviceStatus": "当前服务状态", - "version":"Nebula 版本", - "normal": "正常", - "overload": "超负荷", - "abnormal": "异常", - "qps": "QPS", - "leaderNumber": "Leader 数量", - "leaderDistribute": "Leader 分布", - "all":"全部", - "type":"服务类型", - "total":"总计", - "spaces":"图空间", - "services":"服务", - "partition":"分片", - "partitionNum": "分片数", - "distribution": "分布", - "instance":"实例", - "period":"周期", - "metric":"指标", - "metricParams":"聚合方式", - "queryCondition":"查询条件", - "serviceMetricsDetails": "服务指标详情", - "chooseSpace": "请选择图空间", - "enterPartitionId": "请输入 partitionId" - }, - "component": { - "dashboardDetail": { - "1hour": "1小时", - "6hour": "6小时", - "12hour": "12小时", - "1day": "1天", - "3day": "3天", - "7day": "7天", - "14day": "14天" - } - }, - "configServer": { - "connect": "连接", - "host": "Host", - "username": "用户名", - "password": "密码", - "success": "配置成功", - "fail": "配置失败", - "clear": "清除连接", - "title": "配置数据库", - "connectError": "数据库连接有误,请重新配置" - }, - "description": { - "module": "服务", - "moduleName": "参数名称", - "moduleType": "参数类型", - "moduleMode": "参数模式", - "moduleValue": "参数值", - "ip": "主机地址", - "port": "主机端口", - "status": "主机状态", - "gitInfo": "版本Commit ID", - "leaderCount": "leader总数", - "partitionDistribution": "分片分布", - "leaderDistribution": "leader分布", - "partitionId": "分片序号", - "leader": "分片的leader的IP地址和端口", - "peers": "分片所有副本的IP地址和端口", - "losts": "分片的故障离线副本的的IP地址和端口", - "jobId": "第一行显示作业ID,其他行显示作业相关的任务ID。", - "command": "第一行显示执行的作业命令名称,其他行显示任务对应的nebula-storaged进程。", - "longTermStatus": "显示作业或任务的状态。", - "longTermStartTime": "显示作业或任务开始执行的时间。", - "longTermStopTime": "显示作业或任务结束执行的时间,结束后的状态包括FINISHED、FAILED或STOPPED。" - }, - "metric_description": { - "cpu_utilization": "CPU已使用百分比", - "cpu_idle": "CPU空闲百分比", - "cpu_wait": "等待IO操作的CPU百分比", - "cpu_user": "用户空间(非Nebula Graph图空间)占用的CPU百分比", - "cpu_system": "内核空间(非Nebula Graph内核空间)占用的CPU百分比", - "memory_utilization": "内存已使用百分比", - "memory_used": "已使用内存", - "memory_actual_used": "实际使用内存", - "memory_free": "空闲内存", - "load_1m": "最近1分钟系统平均负载", - "load_5m": "最近5分钟系统平均负载", - "load_15m": "最近15分钟系统平均负载", - "disk_used": "磁盘已使用存储空间", - "disk_free": "磁盘剩余存储空间", - "disk_readbytes": "磁盘每秒读取的字节数", - "disk_writebytes": "磁盘每秒写入的字节数", - "disk_readiops": "磁盘每秒的读请求数量", - "disk_writeiops": "磁盘每秒的写请求数量", - "inode_utilization": "inode已使用百分比", - "network_in_rate": "网卡每秒接收的字节数", - "network_out_rate": "网卡每秒发送的字节数", - "network_in_errs": "网卡每秒接收错误的字节数", - "network_out_errs": "网卡每秒发送错误的字节数", - "network_in_packets": "网卡每秒接收的数据包数量", - "network_out_packets": "网卡每秒发送的数据包数量", - "num_queries": "查询次数", - "num_slow_queries": "慢查询次数", - "query_latency_us": "查询平均延迟", - "num_active_queries": "当前正在执行的query数", - "num_sentences": "服务接收的语句数", - "num_query_errors_leader_changes": "发生Leader Changes的语句数", - "num_killed_queries": "被killed掉的query数量", - "optimizer_latency_us": "优化器阶段延时", - "num_aggregate_executors": "aggregate算子延时", - "num_sort_executors": "sort算子延时", - "num_indexscan_executors": "indexscan算子延时", - "num_oom_queries": "oom的语句数量", - "num_queries_hit_memory_watermark":"达到内存水位线的语句数", - "num_opened_sessions": "服务端建立过的session数量", - "num_auth_failed_sessions": "登陆验证失败的session数量", - "num_auth_failed_sessions_bad_username_password": "因用户名密码错误导验证失败的session数量", - "num_auth_failed_sessions_out_of_max_allowed": "因超过FLAG_OUT_OF_MAX_ALLOWED_CONNECTIONS参数验证失败的session数量", - "num_active_sessions": "当前活跃的session数量", - "num_reclaimed_expired_sessions": "服务端主动回收的过期的session数量", - "slow_query_latency_us": "慢查询平均延迟", - "num_added_jobs": "添加的任务数量", - "num_stoped_jobs": "停止的任务数量", - "commit_log_latency_us": "raft commit log延时", - "commit_snapshot_latency_us": "raft commit 快照延时", - "transfer_leader_latency_us": "raft 转移leader延时", - "num_rpc_sent_to_metad": "发给metad的rpc请求数量", - "num_rpc_sent_to_metad_failed": "发给metad的rpc请求失败的数量", - "num_rpc_sent_to_storaged": "发给storaged的rpc请求数量", - "num_rpc_sent_to_storaged_failed": "发给storaged的rpc请求失败的数量", - "num_raft_votes": "raft投票次数", - "num_query_errors": "查询错误次数", - "heartbeat_latency_us": "心跳延迟", - "num_heartbeats": "心跳次数", - "num_edges_inserted": "插入的边数量", - "num_vertices_inserted": "插入的点数量", - "num_edges_deleted": "删除的边数量", - "num_tags_deleted": "删除的tag数量", - "num_vertices_deleted": "删除的点数量", - - "add_edges_atomic_latency_us": "添加边的单次延迟", - "add_edges_latency_us": "添加边的平均延迟", - "add_vertices_latency_us": "添加点的平均延迟", - "delete_edges_latency_us": "删除边的平均延迟", - "delete_vertices_latency_us": "删除点的平均延迟", - "forward_tranx_latency_us": "传输平均延迟", - "get_neighbors_latency_us": "查询邻居平均延迟", - "more": "更多参数说明" - } -} \ No newline at end of file diff --git a/src/config/locale/zh-CN/base.json b/src/config/locale/zh-CN/base.json new file mode 100644 index 00000000..0700d703 --- /dev/null +++ b/src/config/locale/zh-CN/base.json @@ -0,0 +1,8 @@ +{ + "baseLineModalTitle": "基线设置", + "spaceChartAllInstance": "全部实例", + "spaceChartInstance": "实例", + "spaceChartDiskname": "磁盘名称", + "spaceChartMountpoint": "挂载点", + "spaceChartDiskused": "已使用" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/common.json b/src/config/locale/zh-CN/common.json new file mode 100644 index 00000000..c8ce78ab --- /dev/null +++ b/src/config/locale/zh-CN/common.json @@ -0,0 +1,37 @@ +{ + "account": "账户登录", + "login": "登录", + "logout": "登出", + "username": "请输入用户名", + "password": "请输入密码", + "rememberPassword": "记住密码", + "version": "版本", + "dashboard": "看板", + "machine": "机器", + "service": "服务", + "serviceManagement": "管理", + "serviceInfo": "服务信息", + "partitionInfo": "分片信息", + "config": "配置", + "longTermTask": "长时任务", + "metric": "指标", + "lineChart": "折线图", + "barChart": "柱状图", + "view": "查看", + "set": "设置", + "cancel": "取消", + "confirm": "确认", + "overview": "概览", + "baseLine": "基线", + "editBaseLine": "编辑基线", + "baseLineTip": "请输入大于0的值", + "unit": "单位", + "help": "帮助", + "manual": "使用手册", + "forum": "求助论坛", + "forumLink": "https://discuss.nebula-graph.com.cn/", + "all":"全部", + "updateFrequency": "更新频率", + "configTip": "请输入配置名进行搜索", + "metricLabel": "实例" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/component.json b/src/config/locale/zh-CN/component.json new file mode 100644 index 00000000..e577054f --- /dev/null +++ b/src/config/locale/zh-CN/component.json @@ -0,0 +1,11 @@ +{ + "dashboardDetail": { + "1hour": "1小时", + "6hour": "6小时", + "12hour": "12小时", + "1day": "1天", + "3day": "3天", + "7day": "7天", + "14day": "14天" + } +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/configServer.json b/src/config/locale/zh-CN/configServer.json new file mode 100644 index 00000000..d9b45b83 --- /dev/null +++ b/src/config/locale/zh-CN/configServer.json @@ -0,0 +1,11 @@ +{ + "connect": "连接", + "host": "Host", + "username": "用户名", + "password": "密码", + "success": "配置成功", + "fail": "配置失败", + "clear": "清除连接", + "title": "配置数据库", + "connectError": "数据库连接有误,请重新配置" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/description.json b/src/config/locale/zh-CN/description.json new file mode 100644 index 00000000..c029145a --- /dev/null +++ b/src/config/locale/zh-CN/description.json @@ -0,0 +1,23 @@ +{ + "module": "服务", + "moduleName": "参数名称", + "moduleType": "参数类型", + "moduleMode": "参数模式", + "moduleValue": "参数值", + "ip": "主机地址", + "port": "主机端口", + "status": "主机状态", + "gitInfo": "版本Commit ID", + "leaderCount": "leader总数", + "partitionDistribution": "分片分布", + "leaderDistribution": "leader分布", + "partitionId": "分片序号", + "leader": "分片的leader的IP地址和端口", + "peers": "分片所有副本的IP地址和端口", + "losts": "分片的故障离线副本的的IP地址和端口", + "jobId": "第一行显示作业ID,其他行显示作业相关的任务ID。", + "command": "第一行显示执行的作业命令名称,其他行显示任务对应的nebula-storaged进程。", + "longTermStatus": "显示作业或任务的状态。", + "longTermStartTime": "显示作业或任务开始执行的时间。", + "longTermStopTime": "显示作业或任务结束执行的时间,结束后的状态包括FINISHED、FAILED或STOPPED。" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/device.json b/src/config/locale/zh-CN/device.json new file mode 100644 index 00000000..c9ee9882 --- /dev/null +++ b/src/config/locale/zh-CN/device.json @@ -0,0 +1,24 @@ +{ + "title": "机器", + "overview": "概览", + "cpu": "CPU", + "disk": "Disk", + "memory": "Memory", + "load": "Load", + "network": "Network", + "networkIn": "Network In", + "networkOut": "Network Out", + "transmitFlow": "Transmit Flow", + "receiveFlow": "Receive Flow", + "diskTip": "当前只显示使用率靠前的部分磁盘,详情信息请点击右上角图标", + "detail": { + "cpu": "CPU 监控", + "memory": "Memory监控", + "disk": "Disk监控", + "flow": "Network监控", + "up": "上行", + "down": "下行", + "average": "平均值", + "all": "全部" + } +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/index.ts b/src/config/locale/zh-CN/index.ts new file mode 100644 index 00000000..5fb011ca --- /dev/null +++ b/src/config/locale/zh-CN/index.ts @@ -0,0 +1,24 @@ +import common from './common.json'; +import component from './component.json'; +import configServer from './configServer.json'; +import description from './description.json'; +import device from './device.json'; +import metric_description from './metric_description.json'; +import rules from './rules.json'; +import service from './service.json'; +import base from './base.json'; + +const zhCN = { + common, + component, + configServer, + description, + metric_description, + device, + alert, + rules, + service, + base +}; + +export default zhCN; \ No newline at end of file diff --git a/src/config/locale/zh-CN/metric_description.json b/src/config/locale/zh-CN/metric_description.json new file mode 100644 index 00000000..4b8abe82 --- /dev/null +++ b/src/config/locale/zh-CN/metric_description.json @@ -0,0 +1,125 @@ +{ + "cpu_utilization": "CPU已使用百分比", + "cpu_idle": "CPU空闲百分比", + "cpu_wait": "等待IO操作的CPU百分比", + "cpu_user": "用户空间(非Nebula Graph图空间)占用的CPU百分比", + "cpu_system": "内核空间(非Nebula Graph内核空间)占用的CPU百分比", + "memory_utilization": "内存已使用百分比", + "memory_used": "已使用内存", + "memory_actual_used": "实际使用内存", + "memory_free": "空闲内存", + "load_1m": "最近1分钟系统平均负载", + "load_5m": "最近5分钟系统平均负载", + "load_15m": "最近15分钟系统平均负载", + "disk_used": "磁盘已使用存储空间", + "disk_free": "磁盘剩余存储空间", + "disk_readbytes": "磁盘每秒读取的字节数", + "disk_writebytes": "磁盘每秒写入的字节数", + "disk_readiops": "磁盘每秒的读请求数量", + "disk_writeiops": "磁盘每秒的写请求数量", + "inode_utilization": "inode已使用百分比", + "network_in_rate": "网卡每秒接收的字节数", + "network_out_rate": "网卡每秒发送的字节数", + "network_in_errs": "网卡每秒接收错误的字节数", + "network_out_errs": "网卡每秒发送错误的字节数", + "network_in_packets": "网卡每秒接收的数据包数量", + "network_out_packets": "网卡每秒发送的数据包数量", + "num_queries": "查询次数", + "num_slow_queries": "慢查询次数", + "query_latency_us": "查询平均延迟", + "num_active_queries": "当前正在执行的query数", + "num_sentences": "服务接收的语句数", + "num_query_errors_leader_changes": "发生Leader Changes的语句数", + "num_killed_queries": "被killed掉的query数量", + "optimizer_latency_us": "优化器阶段延时", + "num_aggregate_executors": "aggregate算子延时", + "num_sort_executors": "sort算子延时", + "num_indexscan_executors": "indexscan算子延时", + "num_oom_queries": "oom的语句数量", + "num_queries_hit_memory_watermark":"达到内存水位线的语句数", + "num_opened_sessions": "服务端建立过的session数量", + "num_auth_failed_sessions": "登陆验证失败的session数量", + "num_auth_failed_sessions_bad_username_password": "因用户名密码错误导验证失败的session数量", + "num_auth_failed_sessions_out_of_max_allowed": "因超过FLAG_OUT_OF_MAX_ALLOWED_CONNECTIONS参数验证失败的session数量", + "num_active_sessions": "当前活跃的session数量", + "num_reclaimed_expired_sessions": "服务端主动回收的过期的session数量", + "slow_query_latency_us": "慢查询平均延迟", + "num_added_jobs": "添加的任务数量", + "num_stoped_jobs": "停止的任务数量", + "commit_log_latency_us": "raft commit log延时", + "commit_snapshot_latency_us": "raft commit 快照延时", + "transfer_leader_latency_us": "raft 转移leader延时", + "num_rpc_sent_to_metad": "发给metad的rpc请求数量", + "num_rpc_sent_to_metad_failed": "发给metad的rpc请求失败的数量", + "num_rpc_sent_to_storaged": "发给storaged的rpc请求数量", + "num_rpc_sent_to_storaged_failed": "发给storaged的rpc请求失败的数量", + "num_raft_votes": "raft投票次数", + "num_query_errors": "查询错误次数", + "heartbeat_latency_us": "心跳延迟", + "num_heartbeats": "心跳次数", + "num_edges_inserted": "插入的边数量", + "num_vertices_inserted": "插入的点数量", + "num_edges_deleted": "删除的边数量", + "num_tags_deleted": "删除的tag数量", + "num_vertices_deleted": "删除的点数量", + + "add_edges_atomic_latency_us": "添加边的单次延迟", + "add_edges_latency_us": "添加边的平均延迟", + "add_vertices_latency_us": "添加点的平均延迟", + "delete_edges_latency_us": "删除边的平均延迟", + "delete_vertices_latency_us": "删除点的平均延迟", + "forward_tranx_latency_us": "传输平均延迟", + "get_neighbors_latency_us": "查询邻居平均延迟", + "num_agent_heartbeats": "AgentHBProcessor 心跳次数", + "agent_heartbeat_latency_us": "AgentHBProcessor 延迟时间", + "replicate_log_latency_us": "Raft 复制日志至大多数节点的延迟。", + "num_send_snapshot": "Raft 发送快照至其他节点的次数", + "append_log_latency_us": "Raft 复制日志到单个节点的延迟", + "append_wal_latency_us": "Raft 写入单条 WAL 的延迟。", + "num_grant_votes": "Raft 投票给其他节点的次数。", + "num_start_elect": "Raft 发起投票的次数。", + "num_get_prop": "GetPropProcessor 执行的次数。", + "num_get_neighbors_errors": "GetNeighborsProcessor 执行出错的次数。", + "get_prop_latency_us": "GetPropProcessor 执行的延迟时间。", + "lookup_latency_us": "LookupProcessor 执行的延迟时间。", + "num_lookup_errors": "LookupProcessor 执行时出错的次数。", + "num_scan_vertex": "ScanVertexProcessor 执行的次数。", + "num_scan_vertex_errors": "ScanVertexProcessor 执行时出错的次数。", + "update_edge_latency_us": "UpdateEdgeProcessor 执行的延迟时间。", + "num_update_vertex": "UpdateVertexProcessor 执行的次数。", + "num_update_vertex_errors": "UpdateVertexProcessor 执行时出错的次数。", + "kv_get_latency_us": "Getprocessor 的延迟时间。", + "kv_put_latency_us": "PutProcessor 的延迟时间。", + "kv_remove_latency_us": "RemoveProcessor 的延迟时间。", + "num_kv_get_errors": "GetProcessor 执行出错次数。", + "num_kv_get": "GetProcessor 执行次数。", + "num_kv_put_errors": "PutProcessor 执行出错次数。", + "num_kv_put": "PutProcessor 执行次数。", + "num_kv_remove_errors": "RemoveProcessor 执行出错次数。", + "num_kv_remove": "RemoveProcessor 执行次数。", + "scan_edge_latency_us": "ScanEdgeProcessor 执行的延迟时间。", + "num_scan_edge_errors": "ScanEdgeProcessor 执行时出错的次数。", + "num_scan_edge": "ScanEdgeProcessor 执行的次数。", + "scan_vertex_latency_us": "ScanVertexProcessor 执行的延迟时间。", + "num_add_edges": "添加边的次数。", + "num_add_edges_errors": "添加边时出错的次数。", + "num_add_vertices": "添加点的次数。", + "num_add_vertices_errors": "添加点时出错的次数。", + "num_delete_vertices_errors": "删除点时出错的次数。", + "num_delete_tags": "删除 Tag 的次数。", + "num_delete_tags_errors": "删除 Tag 时出错的次数。", + "num_delete_edges": "删除边的次数。", + "num_delete_edges_errors": "删除边时出错的次数。", + "update_vertex_latency_us": "UpdateVertexProcessor 执行的延迟时间。", + "num_update_edge": "UpdateEdgeProcessor 执行的次数。", + "delete_tags_latency_us": "删除 Tag 的平均延迟时间。", + "num_update_edge_errors": "UpdateEdgeProcessor 执行时出错的次数。", + "num_get_neighbors": "GetNeighborsProcessor 执行的次数。", + "num_get_prop_errors": "GetPropProcessor 执行时出错的次数。", + "num_delete_vertices": "删除点的次数。", + "num_lookup": "LookupProcessor 执行的次数。", + "disk_used_percentage": "磁盘使用率", + "num_sync_data": "The number of times the storage synchronizes data from drainer.", + "num_sync_data_errors": "The number of errors the storage synchronizes data from drainer.", + "more": "更多参数说明" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/rules.json b/src/config/locale/zh-CN/rules.json new file mode 100644 index 00000000..80ff8d4a --- /dev/null +++ b/src/config/locale/zh-CN/rules.json @@ -0,0 +1,5 @@ +{ + "usernameRequired": "请输入用户名", + "passwordRequired": "请输入密码", + "versionRequired":"请输入nebula版本" +} \ No newline at end of file diff --git a/src/config/locale/zh-CN/service.json b/src/config/locale/zh-CN/service.json new file mode 100644 index 00000000..dd90e60b --- /dev/null +++ b/src/config/locale/zh-CN/service.json @@ -0,0 +1,27 @@ +{ + "serviceStatus": "当前服务状态", + "version":"Nebula 版本", + "normal": "正常", + "overload": "超负荷", + "abnormal": "异常", + "qps": "QPS", + "leaderNumber": "Leader 数量", + "leaderDistribute": "Leader 分布", + "all":"全部", + "type":"服务类型", + "total":"总计", + "spaces":"图空间", + "services":"服务", + "partition":"分片", + "partitionNum": "分片数", + "distribution": "分布", + "instance":"实例", + "period":"周期(秒)", + "metric":"指标", + "metricParams":"聚合方式", + "queryCondition":"查询条件", + "serviceMetricsDetails": "服务指标详情", + "chooseSpace": "请选择图空间", + "enterPartitionId": "请输入 partitionId", + "updateFrequency": "更新频率" +} \ No newline at end of file diff --git a/src/config/service.ts b/src/config/service.ts index 7dc54a2f..39b9ef48 100644 --- a/src/config/service.ts +++ b/src/config/service.ts @@ -16,6 +16,9 @@ const getAppInfo = get('/api/app'); const getCustomConfig = get('/api/config/custom'); const getAnnotationLineConfig = get('/api/config/annotation_line'); +const getGraphConfig = get(`/api-graph/flags`); +const getStorageConfig = get(`/api-storage/flags`); + export default { execNGQL, connectDB, @@ -25,5 +28,7 @@ export default { execPromQL, execPromQLByRange, getCustomConfig, + getGraphConfig, + getStorageConfig, getAnnotationLineConfig, }; diff --git a/src/index.html b/src/index.html index f1a12fdc..0533f165 100644 --- a/src/index.html +++ b/src/index.html @@ -73,6 +73,12 @@ top: 200px; } + diff --git a/src/pages/LeaderDistribution/index.tsx b/src/pages/LeaderDistribution/index.tsx index 1fd7e48b..9386ab60 100644 --- a/src/pages/LeaderDistribution/index.tsx +++ b/src/pages/LeaderDistribution/index.tsx @@ -96,7 +96,7 @@ class LeaderDistribution extends React.Component {
- Storage Leader 分布 + Storage Leader {intl.get('service.distribution')}
{ - const { cpuStat } = state.machine; + const { cpuStat, metricsFilterValues } = state.machine; const { cpuBaseLine } = state.setting; const { aliasConfig } = state.app; + return { baseLine: cpuBaseLine, data: getDataByType({ data: cpuStat, - type: 'all', - name: 'instance', + type: metricsFilterValues.instanceList, + nameObj: getMetricsUniqName(MetricScene.CPU), aliasConfig, }), valueType: VALUE_TYPE.percentage, - loading: !!state.loading.effects.machine.asyncGetCPUStatByRange, + loading: false, }; }; export default connect(mapState)(LineCard); diff --git a/src/pages/MachineDashboard/Cards/DiskCard.tsx b/src/pages/MachineDashboard/Cards/DiskCard.tsx index b76a1696..df6cb2a4 100644 --- a/src/pages/MachineDashboard/Cards/DiskCard.tsx +++ b/src/pages/MachineDashboard/Cards/DiskCard.tsx @@ -3,14 +3,18 @@ import { connect } from 'react-redux'; import _ from 'lodash'; import { IRootState } from '@/store'; import SpaceChart from '@/components/Charts/SpaceChart'; +import { DiskMetricInfo, MetricScene } from '@/utils/interface'; +import { getMetricsUniqName } from '@/utils/dashboard'; const mapState = (state: IRootState) => { - const { diskSizeStat, diskStat } = state.machine; + const { diskSizeStat, diskStat, metricsFilterValues } = state.machine; const { aliasConfig } = state.app; + const { instanceList } = metricsFilterValues; + return { // According to type, only the detail increases total - diskUsageDetail: diskStat + diskUsageDetails: diskStat .filter(item => item.metric.instance !== 'total') .map((instance, idx) => { const latestValues = _.last(instance.values); @@ -18,12 +22,20 @@ const mapState = (state: IRootState) => { if (diskSizeStat[idx]) { size = Number(diskSizeStat[idx].value[1]); } - const name = instance.metric.instance; + const { name, showName } = getMetricsUniqName(MetricScene.DISK); + const { device, mountpoint } = instance.metric; return { size, - type: aliasConfig[name] || name, - value: latestValues ? Number(latestValues[1]) : 0, - }; + name: showName(aliasConfig?.[name] || instance.metric[name]), + used: latestValues ? Number(latestValues[1]) : 0, + device, + mountpoint + } as DiskMetricInfo; + }).filter(item => { + if (instanceList.includes('all')) { + return true; + } + return instanceList.includes(item.name) }), }; }; @@ -32,10 +44,10 @@ interface IProps extends ReturnType {} class DiskCard extends React.Component { render() { - const { diskUsageDetail } = this.props; + const { diskUsageDetails } = this.props; return (
- +
); } diff --git a/src/pages/MachineDashboard/Cards/LoadCard.tsx b/src/pages/MachineDashboard/Cards/LoadCard.tsx index d2253bb2..e039d0d4 100644 --- a/src/pages/MachineDashboard/Cards/LoadCard.tsx +++ b/src/pages/MachineDashboard/Cards/LoadCard.tsx @@ -2,23 +2,24 @@ import { connect } from 'react-redux'; import { IRootState } from '@/store'; import LineCard from '@/components/DashboardCard/LineCard'; -import { getDataByType } from '@/utils/dashboard'; +import { getDataByType, getMetricsUniqName } from '@/utils/dashboard'; import { VALUE_TYPE } from '@/utils/promQL'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => { - const { loadStat } = state.machine; + const { loadStat, metricsFilterValues } = state.machine; const { loadBaseLine } = state.setting; const { aliasConfig } = state.app; return { baseLine: loadBaseLine, data: getDataByType({ data: loadStat, - type: 'all', - name: 'instance', + type: metricsFilterValues.instanceList, + nameObj: getMetricsUniqName(MetricScene.LOAD), aliasConfig, }), valueType: VALUE_TYPE.number, - loading: !!state.loading.effects.machine.asyncGetLoadByRange, + loading: false, }; }; diff --git a/src/pages/MachineDashboard/Cards/MemoryCard.tsx b/src/pages/MachineDashboard/Cards/MemoryCard.tsx index 326811fb..935f793a 100644 --- a/src/pages/MachineDashboard/Cards/MemoryCard.tsx +++ b/src/pages/MachineDashboard/Cards/MemoryCard.tsx @@ -1,26 +1,25 @@ import { connect } from 'react-redux'; import LineCard from '@/components/DashboardCard/LineCard'; import { IRootState } from '@/store'; -import { getDataByType } from '@/utils/dashboard'; +import { getDataByType, getMetricsUniqName } from '@/utils/dashboard'; import { VALUE_TYPE } from '@/utils/promQL'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => { - const { memoryStat, memorySizeStat } = state.machine; + const { memoryStat, memorySizeStat, metricsFilterValues } = state.machine; const { memoryBaseLine } = state.setting; const { aliasConfig } = state.app; return { data: getDataByType({ data: memoryStat, - type: 'all', - name: 'instance', + type: metricsFilterValues.instanceList, + nameObj: getMetricsUniqName(MetricScene.MEMORY), aliasConfig, }), sizes: memorySizeStat, baseLine: memoryBaseLine, valueType: VALUE_TYPE.percentage, - loading: - !!state.loading.effects.machine.asyncGetMemorySizeStat && - !!state.loading.effects.machine.asyncGetMemoryStatByRange, + loading:false, }; }; diff --git a/src/pages/MachineDashboard/Cards/NetworkIn.tsx b/src/pages/MachineDashboard/Cards/NetworkIn.tsx index b4ee9f85..c4bf412d 100644 --- a/src/pages/MachineDashboard/Cards/NetworkIn.tsx +++ b/src/pages/MachineDashboard/Cards/NetworkIn.tsx @@ -2,23 +2,24 @@ import { connect } from 'react-redux'; import { IRootState } from '@/store'; import LineCard from '@/components/DashboardCard/LineCard'; -import { getDataByType } from '@/utils/dashboard'; +import { getDataByType, getMetricsUniqName } from '@/utils/dashboard'; import { VALUE_TYPE } from '@/utils/promQL'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => { - const { networkInStat } = state.machine; + const { networkInStat, metricsFilterValues } = state.machine; const { networkInBaseLine } = state.setting; const { aliasConfig } = state.app; return { baseLine: networkInBaseLine, data: getDataByType({ data: networkInStat, - type: 'all', - name: 'instance', + type: metricsFilterValues.instanceList, + nameObj: getMetricsUniqName(MetricScene.NETWORK), aliasConfig, }), valueType: VALUE_TYPE.byteSecondNet, - loading: !!state.loading.effects.machine.asyncGetNetworkStatByRange, + loading: false, }; }; diff --git a/src/pages/MachineDashboard/Cards/NetworkOut.tsx b/src/pages/MachineDashboard/Cards/NetworkOut.tsx index d56a6cee..a02cf746 100644 --- a/src/pages/MachineDashboard/Cards/NetworkOut.tsx +++ b/src/pages/MachineDashboard/Cards/NetworkOut.tsx @@ -3,22 +3,23 @@ import { IRootState } from '@/store'; import { VALUE_TYPE } from '@/utils/promQL'; import LineCard from '@/components/DashboardCard/LineCard'; -import { getDataByType } from '@/utils/dashboard'; +import { getDataByType, getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => { - const { networkOutStat } = state.machine; + const { networkOutStat, metricsFilterValues } = state.machine; const { networkOutBaseLine } = state.setting; const { aliasConfig } = state.app; return { baseLine: networkOutBaseLine, data: getDataByType({ data: networkOutStat, - type: 'all', - name: 'instance', + type: metricsFilterValues.instanceList, + nameObj: getMetricsUniqName(MetricScene.NETWORK), aliasConfig, }), valueType: VALUE_TYPE.byteSecondNet, - loading: !!state.loading.effects.machine.asyncGetNetworkStatByRange, + loading: false, }; }; diff --git a/src/pages/MachineDashboard/Detail/CPUDetail.tsx b/src/pages/MachineDashboard/Detail/CPUDetail.tsx index f99b64ba..bfa8f5cf 100644 --- a/src/pages/MachineDashboard/Detail/CPUDetail.tsx +++ b/src/pages/MachineDashboard/Detail/CPUDetail.tsx @@ -2,15 +2,17 @@ import { connect } from 'react-redux'; import Detail from '.'; import { IDispatch, IRootState } from '@/store'; import { SUPPORT_METRICS } from '@/utils/promQL'; +import { getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => ({ type: 'cpu', - dataSource: state.machine.cpuStat, metricOptions: SUPPORT_METRICS.cpu, + dataTypeObj: getMetricsUniqName(MetricScene.CPU), loading: !!state.loading.effects.machine.asyncGetCPUStatByRange, }); -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetDataSourceByRange: dispatch.machine.asyncGetCPUStatByRange, }); diff --git a/src/pages/MachineDashboard/Detail/DiskDetail.tsx b/src/pages/MachineDashboard/Detail/DiskDetail.tsx index 437f358c..591fd1bf 100644 --- a/src/pages/MachineDashboard/Detail/DiskDetail.tsx +++ b/src/pages/MachineDashboard/Detail/DiskDetail.tsx @@ -3,15 +3,19 @@ import { connect } from 'react-redux'; import Detail from '.'; import { IDispatch, IRootState } from '@/store'; import { SUPPORT_METRICS } from '@/utils/promQL'; +import { getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => ({ type: 'disk', - dataSource: state.machine.diskStat, + instances: state.machine.instanceList, metricOptions: SUPPORT_METRICS.disk, + dataTypeObj: getMetricsUniqName(MetricScene.DISK), + metricsFilterValues: state.machine.metricsFilterValues, loading: state.loading.effects.machine.asyncGetDiskStatByRange, }); -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetDataSourceByRange: dispatch.machine.asyncGetDiskStatByRange, }); diff --git a/src/pages/MachineDashboard/Detail/LoadDetail.tsx b/src/pages/MachineDashboard/Detail/LoadDetail.tsx index 9d61694b..51608840 100644 --- a/src/pages/MachineDashboard/Detail/LoadDetail.tsx +++ b/src/pages/MachineDashboard/Detail/LoadDetail.tsx @@ -3,15 +3,19 @@ import { connect } from 'react-redux'; import Detail from '.'; import { IDispatch, IRootState } from '@/store'; import { SUPPORT_METRICS } from '@/utils/promQL'; +import { getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => ({ type: 'load', - dataSource: state.machine.loadStat, + instances: state.machine.instanceList, metricOptions: SUPPORT_METRICS.load, + dataTypeObj: getMetricsUniqName(MetricScene.LOAD), + metricsFilterValues: state.machine.metricsFilterValues, loading: state.loading.effects.machine.asyncGetLoadByRange, }); -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetDataSourceByRange: dispatch.machine.asyncGetLoadByRange, }); diff --git a/src/pages/MachineDashboard/Detail/MemoryDetail.tsx b/src/pages/MachineDashboard/Detail/MemoryDetail.tsx index 7b8c639c..ca304c10 100644 --- a/src/pages/MachineDashboard/Detail/MemoryDetail.tsx +++ b/src/pages/MachineDashboard/Detail/MemoryDetail.tsx @@ -3,15 +3,17 @@ import { connect } from 'react-redux'; import Detail from '.'; import { IDispatch, IRootState } from '@/store'; import { SUPPORT_METRICS } from '@/utils/promQL'; +import { getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => ({ type: 'memory', - dataSource: state.machine.memoryStat, metricOptions: SUPPORT_METRICS.memory, + dataTypeObj: getMetricsUniqName(MetricScene.MEMORY), loading: state.loading.effects.machine.asyncGetMemoryStatByRange, }); -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetDataSourceByRange: dispatch.machine.asyncGetMemoryStatByRange, }); diff --git a/src/pages/MachineDashboard/Detail/NetworkDetail.tsx b/src/pages/MachineDashboard/Detail/NetworkDetail.tsx index 58540743..bc5e5155 100644 --- a/src/pages/MachineDashboard/Detail/NetworkDetail.tsx +++ b/src/pages/MachineDashboard/Detail/NetworkDetail.tsx @@ -2,15 +2,17 @@ import { connect } from 'react-redux'; import Detail from '.'; import { IDispatch, IRootState } from '@/store'; import { SUPPORT_METRICS } from '@/utils/promQL'; +import { getMetricsUniqName } from '@/utils/dashboard'; +import { MetricScene } from '@/utils/interface'; const mapState = (state: IRootState) => ({ type: 'network', - dataSource: state.machine.networkStat, metricOptions: SUPPORT_METRICS.network, + dataTypeObj: getMetricsUniqName(MetricScene.NETWORK), loading: state.loading.effects.machine.asyncGetNetworkStatByRange, }); -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetDataSourceByRange: dispatch.machine.asyncGetNetworkStatByRange, }); diff --git a/src/pages/MachineDashboard/Detail/index.less b/src/pages/MachineDashboard/Detail/index.less index 6fb63254..b1a90dc6 100644 --- a/src/pages/MachineDashboard/Detail/index.less +++ b/src/pages/MachineDashboard/Detail/index.less @@ -1,20 +1,114 @@ .ant-spin-nested-loading.machine-detail { - height: 100%; - - .ant-spin { - position: absolute; - left: 50%; - top: 80px; - transform: translate(-50%, 50%); - } - - > .ant-spin-container { - height: 100%; - } + // height: 100%; + + .ant-spin { + position: absolute; + left: 50%; + top: 80px; + transform: translate(-50%, 50%); + } + } .machine-modal { - .ant-modal-header { - visibility: hidden; - } + .ant-modal-header { + visibility: hidden; + } } + +.dashboard-detail { + display: flex; + flex-direction: column; + + >.filter { + display: flex; + justify-content: space-between; + // align-items: flex-end; + flex-wrap: wrap; + position: relative; + min-height: 75px; + padding: 24px 24px 0; + background-color: #fff; + } + + >.detail-content { + margin-top: 24px; + flex: 1; + display: flex; + flex-direction: row; + flex-wrap: wrap; + + >.chart-item { + // max-width: 700px; + height: 350px; + border: 2px solid rgba(0, 0, 0, .06); + margin-bottom: 24px; + border-radius: 3px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #fff; + margin-right: 15px; + + >.chart-title { + width: 100%; + line-height: 30px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + margin-top: 12px; + } + + >.chart-content { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + + >.nebula-chart { + height: 90%; + width: 90%; + display: flex; + align-items: center; + justify-content: center; + } + } + + >.base-line { + position: absolute; + top: 15px; + right: 24px; + } + } + + @media screen and (max-width: 1960px) and (min-width: 1200px) { + >.chart-item { + width: calc(50% - 15px); + } + } + + @media screen and (min-width: 1960px) and (max-width: 2480px) { + >.chart-item { + width: calc(33.3% - 15px); + } + } + + @media screen and (min-width: 2480px) { + >.chart-item { + width: calc(25% - 15px); + } + } + } +} + +.chart-title-desc { + height: 16px; + margin-left: 6px; + width: 16px; +} \ No newline at end of file diff --git a/src/pages/MachineDashboard/Detail/index.tsx b/src/pages/MachineDashboard/Detail/index.tsx index 85cbb62e..cd78b450 100644 --- a/src/pages/MachineDashboard/Detail/index.tsx +++ b/src/pages/MachineDashboard/Detail/index.tsx @@ -1,42 +1,39 @@ -import React from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import intl from 'react-intl-universal'; import { Chart } from '@antv/g2'; -import { uniq } from 'lodash'; -import { Spin } from 'antd'; +import { Popover, Spin } from 'antd'; import { connect } from 'react-redux'; -import MachineDetail from '@/components/MachineDetail'; import LineChart from '@/components/Charts/LineChart'; import { - CARD_POLLING_INTERVAL, - DETAIL_DEFAULT_RANGE, + calcTimeRange, getBaseLineByUnit, getDataByType, - getMaxNum, + getDiskData, getProperTickInterval, } from '@/utils/dashboard'; import { configDetailChart, updateDetailChart } from '@/utils/chart/chart'; -import { IStatRangeItem } from '@/utils/interface'; import { IDispatch, IRootState } from '@/store'; +import { VALUE_TYPE } from '@/utils/promQL'; + +import { shouldCheckCluster } from '@/utils'; +import MetricsFilterPanel from '@/components/MetricsFilterPanel'; +import Icon from '@/components/Icon'; +import BaseLineEditModal from '@/components/BaseLineEditModal'; import './index.less'; -import { SUPPORT_METRICS, VALUE_TYPE } from '@/utils/promQL'; -import { trackEvent } from '@/utils/stat'; -import Modal from '@/components/Modal'; -import BaseLineEdit from '@/components/BaseLineEdit'; -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncUpdateBaseLine: (key, value) => dispatch.setting.update({ [key]: value, }), + updateMetricsFiltervalues: dispatch.machine.updateMetricsFiltervalues, }); const mapState = (state: IRootState) => ({ aliasConfig: state.app.aliasConfig, - cpuBaseLine: state.setting.cpuBaseLine, - memoryBaseLine: state.setting.memoryBaseLine, - loadBaseLine: state.setting.loadBaseLine, - diskBaseLine: state.setting.diskBaseLine, - networkBaseLine: state.setting.networkBaseLine, + cluster: (state as any).cluster?.cluster, + instances: state.machine.instanceList, + metricsFilterValues: state.machine.metricsFilterValues, }); interface IProps extends ReturnType, @@ -46,232 +43,206 @@ interface IProps start: number; end: number; metric: string; - }) => void; - dataSource: IStatRangeItem[]; + clusterID?: string; + }) => Promise; metricOptions: { metric: string; valueType: VALUE_TYPE; }[]; loading: true; + dataTypeObj: any; } -interface IState { - maxNum: number; - startTimestamps: number; - endTimestamps: number; - currentInstance: string; - currentMetricOption: typeof SUPPORT_METRICS.cpu[0]; -} +let pollingTimer: any; -class Detail extends React.Component { - pollingTimer: any; +function Detail(props: IProps) { - chartInstance: Chart; + const { metricOptions, loading, aliasConfig, asyncGetDataSourceByRange, asyncUpdateBaseLine, cluster, instances, metricsFilterValues, updateMetricsFiltervalues, type, dataTypeObj } = props; - modalHandler; + const [maxNum, setMaxNum] = useState(0); + const [dataSources, setDataSources] = useState([]); - constructor(props: IProps) { - super(props); - const endTimestamps = Date.now(); - this.state = { - maxNum: 0, - endTimestamps, - startTimestamps: endTimestamps - DETAIL_DEFAULT_RANGE, - currentInstance: 'all', - currentMetricOption: props.metricOptions[0], - }; - } + const [showLoading, setShowLoading] = useState(false); - componentDidMount() { - this.pollingData(); - } + const metricCharts: any = useMemo(() => (metricOptions || []).map( + (metric, i) => ({ + metric, + chartInstance: undefined, + index: i, + baseLine: undefined, + }) + ), [metricOptions]); - componentWillUnmount() { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); - } - } + useEffect(() => { + setShowLoading(loading && metricsFilterValues.frequency === 0) + }, [loading, metricsFilterValues.frequency]) - getData = async () => { - const { startTimestamps, endTimestamps, currentMetricOption } = this.state; - await this.props.asyncGetDataSourceByRange({ - start: startTimestamps, - end: endTimestamps, - metric: currentMetricOption.metric, - }); - this.updateChart(); - }; + useEffect(() => { + if (pollingTimer) { + clearTimeout(pollingTimer); + } + if (shouldCheckCluster()) { + if (cluster?.id) { + pollingData(); + } + } else { + pollingData(); + } + }, [cluster, metricsFilterValues.frequency, metricsFilterValues.timeRange]) - pollingData = () => { - this.getData(); - this.pollingTimer = setTimeout(this.pollingData, CARD_POLLING_INTERVAL); - }; + useEffect(() => { + updateChart(); + }, [metricsFilterValues.instanceList, dataSources]) - handleIntervalChange = (startTimestamps, endTimestamps) => { - const { type } = this.props; - trackEvent(`${type}_detail`, 'select_interval', `from_${type}_detail`); - this.setState( - { - startTimestamps, - endTimestamps, - }, - this.getData, - ); + useEffect(() => () => { + if (pollingTimer) { + clearTimeout(pollingTimer); + } + }, []) + + const getData = async () => { + const [startTimestamps, endTimestamps] = calcTimeRange(metricsFilterValues.timeRange); + const getPromise = (chart) => { + return new Promise((resolve, reject) => { + asyncGetDataSourceByRange({ + start: startTimestamps, + end: endTimestamps, + metric: chart.metric.metric, + clusterID: cluster?.id, + }).then(res => { + resolve(res); + }).catch(e => { + reject(e); + }); + }) + } + Promise.all(metricCharts.map(chart => getPromise(chart))).then((dataSources) => { + setDataSources(dataSources) + }) }; - handleInstanceChange = instance => { - const { type } = this.props; - this.setState( - { - currentInstance: instance, - }, - this.updateChart, - ); - trackEvent(`${type}_detail`, 'select_data_type', `from_${type}_detail`); + const pollingData = () => { + getData(); + if (metricsFilterValues.frequency > 0) { + pollingTimer = setTimeout(pollingData, metricsFilterValues.frequency); + } }; - handleMetricChange = async metric => { - const { metricOptions, type, asyncUpdateBaseLine } = this.props; - const metricOption = metricOptions.find(option => option.metric === metric); - trackEvent(`${type}_detail`, 'select_metric_query', `from_${type}_detail`); - await asyncUpdateBaseLine(`${type}BaseLine`, undefined); - if (metricOption) { - this.setState( - { - currentMetricOption: metricOption, - }, - this.getData, - ); - } + const handleMetricChange = async values => { + updateMetricsFiltervalues(values); }; - handleBaseLineChange = async value => { - const { type, asyncUpdateBaseLine } = this.props; - const { currentMetricOption } = this.state; - const { baseLine, unit } = value; - await asyncUpdateBaseLine( - `${type}BaseLine`, - getBaseLineByUnit({ - baseLine, - unit, - valueType: currentMetricOption.valueType, - }), - ); - this.modalHandler.hide(); + const handleBaseLineChange = async (metricChart, values) => { + const { baseLine, unit } = values; + metricChart.baseLine = getBaseLineByUnit({ + baseLine, + unit, + valueType: metricChart.valueType, + }); + metricChart.chartRef.updateBaseline(metricChart.baseLine); }; - renderChart = (chartInstance: Chart) => { - const { currentMetricOption } = this.state; - const { startTimestamps, endTimestamps } = this.state; - this.chartInstance = chartInstance; + const renderChart = (i: number) => (chartInstance: Chart) => { + const [startTimestamps, endTimestamps] = calcTimeRange(metricsFilterValues.timeRange); + metricCharts[i].chartInstance = chartInstance; configDetailChart(chartInstance, { tickInterval: getProperTickInterval(endTimestamps - startTimestamps), - valueType: currentMetricOption.valueType, + valueType: metricCharts[i].metric.valueType, }); }; - updateChart = () => { - const { dataSource, type, aliasConfig } = this.props; - const { currentInstance, startTimestamps, endTimestamps } = this.state; - const data = getDataByType({ - data: dataSource, - type: currentInstance, - name: 'instance', - aliasConfig, - }); - this.setState({ - maxNum: getMaxNum(data), - }); - updateDetailChart(this.chartInstance, { - type, - tickInterval: getProperTickInterval(endTimestamps - startTimestamps), - }).changeData(data); - this.chartInstance.autoFit = true; + const updateChart = () => { + const [startTimestamps, endTimestamps] = calcTimeRange(metricsFilterValues.timeRange); + metricCharts.forEach((chart, i) => { + if (chart.chartInstance) { + const data = type === 'disk' ? + getDiskData({ + data: dataSources[i] || [], + type: metricsFilterValues.instanceList, + nameObj: dataTypeObj, + aliasConfig, + }) : + getDataByType({ + data: dataSources[i] || [], + type: metricsFilterValues.instanceList, + nameObj: dataTypeObj, + aliasConfig, + }); + setMaxNum(100); + updateDetailChart(chart.chartInstance, { + type, + tickInterval: getProperTickInterval(endTimestamps - startTimestamps), + }).changeData(data); + chart.chartInstance.autoFit = true; + } + }) }; - handleBaseLineEdit = () => { - if (this.modalHandler) { - this.modalHandler.show(); - } + const handleBaseLineEdit = (metricChart) => () => { + BaseLineEditModal.show({ + baseLine: metricChart.baseLine, + valueType: metricChart.metric.valueType, + onOk: (values) => handleBaseLineChange(metricChart, values), + }); }; - handleClose = () => { - if (this.modalHandler) { - this.modalHandler.hide(); - } - }; + const handleRefreshData = () => { + setShowLoading(loading); + getData(); + } - render() { - const { - maxNum, - startTimestamps, - endTimestamps, - currentInstance, - currentMetricOption, - } = this.state; - const { dataSource, metricOptions, loading, aliasConfig, type } = - this.props; - const instances = uniq( - dataSource.map(instance => instance.metric.instance), - ); - const typeOptions = [ - { - name: intl.get('device.detail.all'), - value: 'all', - }, - ...instances.map(instance => ({ - name: aliasConfig[instance] || instance, - value: instance, - })), - ]; - const baseLine = this.props[`${type}BaseLine`]; - return ( - - - - - (this.modalHandler = handler)} - footer={null} - > - +
+
+ - - - ); - } +
+
+ { + metricCharts.map((metricChart, i) => ( +
+
+ {metricChart.metric.metric} + {intl.get(`metric_description.${metricChart.metric.metric}`)}
+ } + > + + +
+
+ metricChart.chartRef = ref} + renderChart={renderChart(i)} + /> +
+
+ + {intl.get('common.baseLine')} +
+
+ )) + } +
+
+ + ); } export default connect(mapState, mapDispatch)(Detail); diff --git a/src/pages/MachineDashboard/index.less b/src/pages/MachineDashboard/index.less index a0905177..65f488dd 100644 --- a/src/pages/MachineDashboard/index.less +++ b/src/pages/MachineDashboard/index.less @@ -1,15 +1,29 @@ +.machine-dashboard-spinning, .ant-spin-container{ + min-height: 100%; +} .machine-dashboard { - height: 100%; - overflow-y: auto; + min-height: 100%; + // overflow-y: auto; display: flex; flex-direction: column; + background-color: #fff; + + .common-header { + background: #fff; + margin: 15px 0; + justify-content: flex-start; + padding-bottom: 0; + height: auto; + } > .ant-row { flex: 1; .dashboard-card { - height: 100%; - + height: calc((100vh - 285px)/ 3); + .inner{ + border: 1px solid #D9D9D9; + } .nebula-chart-line { height: 100%; } @@ -44,3 +58,9 @@ } } } + +.chart-title-desc { + height: 16px; + margin-left: 6px; + width: 16px; +} \ No newline at end of file diff --git a/src/pages/MachineDashboard/index.tsx b/src/pages/MachineDashboard/index.tsx index 53aa7f56..d94ea532 100644 --- a/src/pages/MachineDashboard/index.tsx +++ b/src/pages/MachineDashboard/index.tsx @@ -1,8 +1,7 @@ import DashboardCard from '@/components/DashboardCard'; -import Modal from '@/components/Modal'; -import { Col, Row } from 'antd'; +import { Col, Row, Spin } from 'antd'; import { connect } from 'react-redux'; -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import intl from 'react-intl-universal'; import CPUCard from './Cards/CPUCard'; import './index.less'; @@ -13,15 +12,15 @@ import NetworkOut from './Cards/NetworkOut'; import NetworkIn from './Cards/NetworkIn'; import { SUPPORT_METRICS, VALUE_TYPE } from '@/utils/promQL'; import { - CARD_POLLING_INTERVAL, - CARD_RANGE, MACHINE_TYPE, getBaseLineByUnit, + calcTimeRange, + getMachineRouterPath, } from '@/utils/dashboard'; -import { IDispatch, IRootState } from '@/store'; -import BaseLineEdit from '@/components/BaseLineEdit'; +import BaseLineEditModal from '@/components/BaseLineEditModal'; +import MetricsFilterPanel from '@/components/MetricsFilterPanel'; -const mapDispatch: any = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: any) => ({ asyncGetCPUStatByRange: dispatch.machine.asyncGetCPUStatByRange, asyncGetMemoryStatByRange: dispatch.machine.asyncGetMemoryStatByRange, asyncGetMemorySizeStat: dispatch.machine.asyncGetMemorySizeStat, @@ -33,123 +32,132 @@ const mapDispatch: any = (dispatch: IDispatch) => ({ dispatch.setting.update({ [key]: value, }), + updateMetricsFiltervalues: dispatch.machine.updateMetricsFiltervalues, }); -const mapState = (state: IRootState) => ({ +const mapState = (state: any) => ({ cpuBaseLine: state.setting.cpuBaseLine, memoryBaseLine: state.setting.memoryBaseLine, networkOutBaseLine: state.setting.networkOutBaseLine, networkInBaseLine: state.setting.networkInBaseLine, loadBaseLine: state.setting.loadBaseLine, + instanceList: state.machine.instanceList as any, + metricsFilterValues: state.machine.metricsFilterValues, + loading: state.loading.effects.machine.asyncGetMetricsData, }); interface IProps extends ReturnType, - ReturnType {} - -interface IState { - editPanelType: string; + ReturnType { + cluster?: any; } -class MachineDashboard extends React.Component { - pollingTimer: any; - modalHandler; +let pollingTimer: any; - constructor(props: IProps) { - super(props); - this.state = { - editPanelType: '', - }; - } +function MachineDashboard(props: IProps) { - componentDidMount() { - this.props.asyncGetMemorySizeStat(); - this.props.asyncGetDiskSizeStat(); + const { asyncGetMemorySizeStat, asyncGetDiskSizeStat, cluster, metricsFilterValues, + asyncUpdateBaseLine, asyncGetCPUStatByRange, asyncGetMemoryStatByRange, asyncGetDiskStatByRange, + asyncGetLoadByRange, asyncGetNetworkStatByRange, updateMetricsFiltervalues, instanceList, + loading, + } = props; - this.getMachineStatus(); - this.pollingMachineStatus(); - } + const [showLoading, setShowLoading] = useState(false); - componentWillUnmount() { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); + useEffect(() => { + asyncGetMemorySizeStat(cluster?.id); + asyncGetDiskSizeStat(cluster?.id); + getMachineStatus(); + return () => { + if (pollingTimer) { + clearTimeout(pollingTimer); + } } - } + }, [cluster]) - handleConfigPanel = (editPanelType: string) => { - this.setState( - { - editPanelType, - }, - this.modalHandler.show, - ); + useEffect(() => { + setShowLoading(loading && metricsFilterValues.frequency === 0) + }, [loading, metricsFilterValues.frequency]) + + useEffect(() => { + if (pollingTimer) { + clearTimeout(pollingTimer); + } + pollingMachineStatus(); + }, [metricsFilterValues.timeRange, metricsFilterValues.frequency]) + + const handleConfigPanel = (editPanelType: string) => { + BaseLineEditModal.show({ + baseLine: props[`${editPanelType}BaseLine`], + valueType: getValueType(editPanelType), + onOk: (values) => handleBaseLineChange(values, editPanelType), + }); }; - handleBaseLineChange = async value => { - const { editPanelType } = this.state; + const handleBaseLineChange = async (value, editPanelType) => { const { baseLine, unit } = value; - await this.props.asyncUpdateBaseLine( + await asyncUpdateBaseLine( `${editPanelType}BaseLine`, getBaseLineByUnit({ baseLine, unit, - valueType: this.getValueType(editPanelType), + valueType: getValueType(editPanelType), }), ); - this.handleClose(); - }; - - handleClose = () => { - if (this.modalHandler) { - this.modalHandler.hide(); - } }; - getMachineStatus = () => { - const end = Date.now(); - const start = end - CARD_RANGE; - this.props.asyncGetCPUStatByRange({ + const getMachineStatus = () => { + const [start, end] = calcTimeRange(metricsFilterValues.timeRange); + asyncGetCPUStatByRange({ start, end, metric: SUPPORT_METRICS.cpu[0].metric, + clusterID: cluster?.id }); - this.props.asyncGetMemoryStatByRange({ + asyncGetMemoryStatByRange({ start, end, metric: SUPPORT_METRICS.memory[0].metric, + clusterID: cluster?.id }); - this.props.asyncGetDiskStatByRange({ + asyncGetDiskStatByRange({ start: end - 1000, end, - metric: SUPPORT_METRICS.disk[0].metric, + metric: SUPPORT_METRICS.disk[1].metric, + clusterID: cluster?.id }); - this.props.asyncGetLoadByRange({ + asyncGetLoadByRange({ start, end, metric: SUPPORT_METRICS.load[0].metric, + clusterID: cluster?.id }); - this.props.asyncGetNetworkStatByRange({ + asyncGetNetworkStatByRange({ start, end, metric: SUPPORT_METRICS.network[0].metric, inOrOut: 'in', + clusterID: cluster?.id }); - this.props.asyncGetNetworkStatByRange({ + asyncGetNetworkStatByRange({ start, end, metric: SUPPORT_METRICS.network[1].metric, inOrOut: 'out', + clusterID: cluster?.id }); }; - pollingMachineStatus = () => { - this.pollingTimer = setTimeout(() => { - this.getMachineStatus(); - this.pollingMachineStatus(); - }, CARD_POLLING_INTERVAL); + const pollingMachineStatus = () => { + getMachineStatus(); + if (metricsFilterValues.frequency > 0) { + pollingTimer = setTimeout(() => { + pollingMachineStatus(); + }, metricsFilterValues.frequency); + } }; - getValueType = type => { + const getValueType = type => { switch (type) { case MACHINE_TYPE.cpu: case MACHINE_TYPE.memory: @@ -164,16 +172,38 @@ class MachineDashboard extends React.Component { } }; - render() { - const { editPanelType } = this.state; - return ( + const handleMetricsChange = (values) => { + updateMetricsFiltervalues(values); + } + + const getViewPath = (path: string): string => { + if (cluster?.id) { + return getMachineRouterPath(path, cluster.id); + } + return path; + } + + const handleRefreshData = () => { + getMachineStatus(); + } + + return ( +
+
+ +
this.handleConfigPanel(MACHINE_TYPE.cpu)} + viewPath={getViewPath("/machine/cpu")} + onConfigPanel={() => handleConfigPanel(MACHINE_TYPE.cpu)} > @@ -181,8 +211,8 @@ class MachineDashboard extends React.Component { this.handleConfigPanel(MACHINE_TYPE.memory)} + viewPath={getViewPath("/machine/memory")} + onConfigPanel={() => handleConfigPanel(MACHINE_TYPE.memory)} > @@ -192,8 +222,8 @@ class MachineDashboard extends React.Component { this.handleConfigPanel(MACHINE_TYPE.load)} + viewPath={getViewPath("/machine/load")} + onConfigPanel={() => handleConfigPanel(MACHINE_TYPE.load)} > @@ -201,7 +231,7 @@ class MachineDashboard extends React.Component { @@ -211,9 +241,9 @@ class MachineDashboard extends React.Component { - this.handleConfigPanel(MACHINE_TYPE.networkOut) + handleConfigPanel(MACHINE_TYPE.networkOut) } > @@ -222,32 +252,18 @@ class MachineDashboard extends React.Component { - this.handleConfigPanel(MACHINE_TYPE.networkIn) + handleConfigPanel(MACHINE_TYPE.networkIn) } > - (this.modalHandler = handler)} - footer={null} - > - -
- ); - } +
+ ) } export default connect(mapState, mapDispatch)(MachineDashboard); diff --git a/src/pages/MainPage/index.less b/src/pages/MainPage/index.less index be4d9539..cfac08af 100644 --- a/src/pages/MainPage/index.less +++ b/src/pages/MainPage/index.less @@ -1,187 +1,194 @@ @import '@/common.less'; .nebula-stat { - .nebula-sider { - position: relative; - background-color: #1e2025; - - .ant-layout-sider-children { - display: flex; - flex-direction: column; - } - - .sidebar-header { - height: 80px; - display: flex; - align-items: center; - justify-content: center; - - .logo { - width: 28px; - height: 28px; - } - - .title { - font-family: Futura-Bold, serif; - font-size: 12px; - color: #fff; - font-weight: 700; - margin-left: 7px; - } - } - - .sidebar-menu { - overflow: auto; - padding-bottom: 20px; - background-color: #1e2025; - - .ant-menu-submenu-title, - .ant-menu-item { - display: flex; - align-items: center; - margin: 0; - } - - .menu-icon { - fill: #fff; - width: 16px; - height: 16px; - margin-right: 10px; - margin-left: 6px; - } - - .ant-menu-title-content { - margin-left: 0; - } - - .ant-menu-submenu-title { - // background-color: #1E2025; - .icon { - fill: #fff; - width: 16px; - height: 16px; - margin-left: 0; - } - } - } - - .ant-menu-item { - background-color: #2f3436; - } - - .ant-menu-inline-collapsed { - width: 56px; - - .ant-menu-submenu-selected { - background-color: @blue; - } - } - - .sidebar-footer { - padding: 0 20px 20px; - color: #fff; - font-size: 14px; - display: flex; - flex-direction: column; - width: 100%; - position: absolute; - bottom: 0; - - .btn-logout { - display: flex; - align-items: center; - margin-bottom: 40px; - cursor: pointer; - - .menu-btn { - fill: #fff; - width: 16px; - height: 16px; - } - - .text-logout { - margin-left: 10px; - } - } - - .row { - display: flex; - align-items: center; - margin-top: 13px; - justify-content: space-between; - - .help { - color: #fff; - margin-left: -40px; - } - } - - .btn-collapse { - display: inline-flex; - cursor: pointer; - - .menu-collapse-icon { - fill: #fff; - width: 16px; - height: 16px; - } - } - } - } - - .ant-layout-sider-collapsed { - .sidebar-footer { - padding: 0 0 20px; - display: flex; - align-items: center; - - .row { - margin-top: 20px; - } - } - - .dark { - width: 100%; - - .select-label { - justify-content: center; - } - } - } - - .main-content { - padding: 15px; - height: 100%; - - .ant-select { - min-width: 134px; - } - } + height: 100vh !important; + + .nebula-sider { + position: relative; + background-color: #1e2025; + z-index: 9; + height: auto; + + .ant-layout-sider-children { + display: flex; + flex-direction: column; + } + + .sidebar-header { + height: 80px; + display: flex; + align-items: center; + justify-content: center; + + .logo { + width: 28px; + height: 28px; + } + + .title { + font-family: Futura-Bold, serif; + font-size: 12px; + color: #fff; + font-weight: 700; + margin-left: 7px; + } + } + + .sidebar-menu { + background-color: #1e2025; + margin-bottom: 170px; + flex: 1; + overflow-y: scroll; + + .ant-menu-submenu-title, + .ant-menu-item { + display: flex; + align-items: center; + margin: 0; + } + + .menu-icon { + color: #fff; + width: 16px; + height: 16px; + margin-right: 10px; + margin-left: 6px; + } + + .ant-menu-title-content { + margin-left: 0; + } + + .ant-menu-submenu-title { + + // background-color: #1E2025; + .icon { + color: #fff; + width: 16px; + height: 16px; + margin-left: 0; + } + } + } + + .ant-menu-item { + background-color: #2f3436; + } + + .ant-menu-inline-collapsed { + width: 56px; + + .ant-menu-submenu-selected { + background-color: @blue; + } + } + + .sidebar-footer { + padding: 0 20px 20px; + color: #fff; + font-size: 14px; + display: flex; + flex-direction: column; + width: 100%; + position: absolute; + bottom: 0; + background-color: #1e2025; + + .btn-logout { + display: flex; + align-items: center; + margin-bottom: 40px; + cursor: pointer; + + .menu-btn { + color: #fff; + width: 16px; + height: 16px; + } + + .text-logout { + margin-left: 10px; + } + } + + .row { + display: flex; + align-items: center; + margin-top: 13px; + justify-content: space-between; + + .help { + color: #fff; + margin-left: -40px; + } + } + + .btn-collapse { + display: inline-flex; + cursor: pointer; + + .menu-collapse-icon { + color: #fff; + width: 16px; + height: 16px; + } + } + } + } + + .ant-layout-sider-collapsed { + .sidebar-footer { + padding: 0 0 20px; + display: flex; + align-items: center; + + .row { + margin-top: 20px; + } + } + + .dark { + width: 100%; + + .select-label { + justify-content: center; + } + } + } + + .main-content { + padding: 15px; + height: 100%; + overflow: scroll; + + .ant-select { + min-width: 134px; + } + } } .ant-menu-vertical { - .ant-menu-item { - display: flex; - align-items: center; - background-color: #2f3436; - margin: 0 !important; - - .menu-icon { - fill: #fff; - } - } + .ant-menu-item { + display: flex; + align-items: center; + background-color: #2f3436; + margin: 0 !important; + + .menu-icon { + color: #fff; + } + } } .ant-dropdown-menu { - background-color: #2f3436; + background-color: #2f3436; - .ant-dropdown-menu-item { - color: #fff; - font-size: 14px; - } - - .ant-dropdown-menu-item:hover { - background-color: #4372ff; - } -} + .ant-dropdown-menu-item { + color: #fff; + font-size: 14px; + } + .ant-dropdown-menu-item:hover { + background-color: #4372ff; + } +} \ No newline at end of file diff --git a/src/pages/MainPage/index.tsx b/src/pages/MainPage/index.tsx index d5fecbcf..95b97f85 100644 --- a/src/pages/MainPage/index.tsx +++ b/src/pages/MainPage/index.tsx @@ -118,8 +118,8 @@ class MainPage extends React.Component { const locale = cookies.get('locale'); const manualHref = locale === 'ZH_CN' - ? 'https://docs.nebula-graph.com.cn/3.0.0/nebula-dashboard/1.what-is-dashboard/' - : 'https://docs.nebula-graph.io/3.0.0/nebula-dashboard/1.what-is-dashboard/'; + ? 'https://docs.nebula-graph.com.cn/3.2.0/nebula-dashboard/1.what-is-dashboard/' + : 'https://docs.nebula-graph.io/3.2.0/nebula-dashboard/1.what-is-dashboard/'; if (activeKey === undefined) { activeKey = 'machine-overview'; diff --git a/src/pages/MainPage/routes.tsx b/src/pages/MainPage/routes.tsx index 03022669..147b6c92 100644 --- a/src/pages/MainPage/routes.tsx +++ b/src/pages/MainPage/routes.tsx @@ -1,4 +1,4 @@ -import { lazy } from 'react'; +// import { lazy } from 'react'; import intl from 'react-intl-universal'; import LoadDetail from '../MachineDashboard/Detail/LoadDetail'; import MachineDashboard from '@/pages/MachineDashboard'; @@ -63,7 +63,7 @@ export const MenuList = [ { key: 'service', title: intl.get('common.service'), - icon: '#iconnav-service', + icon: '#iconnav-servise', children: [ { key: 'service-overview', diff --git a/src/pages/ServiceDashboard/Detail/Panel/index.tsx b/src/pages/ServiceDashboard/Detail/Panel/index.tsx deleted file mode 100644 index efc61dd3..00000000 --- a/src/pages/ServiceDashboard/Detail/Panel/index.tsx +++ /dev/null @@ -1,290 +0,0 @@ -import { DatePicker, Form, Radio, Row } from 'antd'; -import React from 'react'; -import intl from 'react-intl-universal'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { connect } from 'react-redux'; -import { FormInstance } from 'antd/lib/form'; -import { Chart } from '@antv/g2'; -import dayjs from 'dayjs'; -import { - DETAIL_DEFAULT_RANGE, - TIMEOPTIONS, - TIME_INTERVAL_OPTIONS, - getDefaultTimeRange, -} from '@/utils/dashboard'; -import { IDispatch, IRootState } from '@/store'; -import { SERVICE_QUERY_PERIOD } from '@/utils/service'; -import StatusPanel from '@/components/StatusPanel'; -import { DashboardSelect, Option } from '@/components/DashboardSelect'; -import { trackEvent } from '@/utils/stat'; -import { MetricPopover } from '@/components/MetricPopover'; - -import '../index.less'; - -const mapDispatch = (dispatch: IDispatch) => ({ - asyncGetStatus: dispatch.service.asyncGetStatus, -}); - -const mapState = (state: IRootState) => ({ - aliasConfig: state.app.aliasConfig, - serviceMetric: state.serviceMetric, - spaces: state.nebula.spaces, -}); - -interface IProps - extends ReturnType, - ReturnType, - RouteComponentProps { - serviceType: string; - metricsValueType: string; - defaultFormParams: { - interval: number; - instance: string; - metric: string; - metricFunction: string; - period: number; - timeRange: dayjs.Dayjs[]; - }; - instanceList: string[]; - onTimeChange?: (interval: number) => void; - onServiceTypeChange: (type) => void; - onConfigUpdate: (values) => void; - onMetricsValueTypeChange: (type) => void; -} - -class ServicePanel extends React.Component { - chartInstance: Chart; - - pollingTimer: any; - - modalHandler; - - formRef = React.createRef(); - - componentDidUpdate() { - const { defaultFormParams } = this.props; - const { metric } = this.formRef.current?.getFieldsValue(); - if (defaultFormParams.metric !== metric) { - this.formRef.current!.setFieldsValue(defaultFormParams); - } - } - - handleTimeButtonClick = e => { - const { onTimeChange } = this.props; - if (onTimeChange) { - onTimeChange(e.target.value); - } - }; - - initialConfig = async () => { - const { - serviceMetric, - serviceType, - onServiceTypeChange, - onMetricsValueTypeChange, - } = this.props; - if (serviceType) { - const metricsList = serviceMetric[`${serviceType}d`]; - console.log('metricsList', metricsList); - const timeRange = getDefaultTimeRange(); - const defaultFormParams = { - interval: DETAIL_DEFAULT_RANGE, - instance: 'all', - metric: metricsList[0].metric, - metricFunction: metricsList[0].metricType[0].value, - period: SERVICE_QUERY_PERIOD, - timeRange, - }; - if (this.formRef.current) { - this.formRef.current.setFieldsValue(defaultFormParams); - } - await onServiceTypeChange(serviceType); - await onMetricsValueTypeChange(metricsList[0].valueType); - } - }; - - handleConfigUpdate = changedValues => { - const { serviceType, serviceMetric } = this.props; - switch (true) { - case !!changedValues.interval: - const timeRange = getDefaultTimeRange(changedValues.interval); - this.formRef.current!.setFieldsValue({ timeRange }); - trackEvent( - `${serviceType}_detail`, - 'select_interval', - `from_${serviceType}_detail`, - ); - break; - case !!changedValues.timeRange: - this.formRef.current!.setFieldsValue({ interval: null }); - trackEvent( - `${serviceType}_detail`, - 'select_time_range', - `from_${serviceType}_detail`, - ); - break; - case !!changedValues.metric: - const selectedMetrics = serviceMetric[`${serviceType}d`].filter( - item => item.metric === changedValues.metric, - )[0]; - this.formRef.current!.setFieldsValue({ - metricFunction: selectedMetrics.metricType[0].value, - space: '', - }); - trackEvent( - `${serviceType}_detail`, - 'select_metric_type', - `from_${serviceType}_detail`, - ); - break; - default: - break; - } - this.props.onConfigUpdate(changedValues); - }; - - disabledDate(current) { - return ( - current < dayjs().subtract(14, 'days').endOf('day') || - current > dayjs().endOf('day') - ); - } - - render() { - const { - defaultFormParams, - serviceMetric, - spaces, - serviceType, - aliasConfig, - instanceList, - asyncGetStatus, - } = this.props; - return ( -
- -
- - - {TIMEOPTIONS.map(option => ( - - {intl.get(`component.dashboardDetail.${option.name}`)} - - ))} - - - - - -
- - - -
- - - - - {instanceList.map(value => ( - - ))} - - - - - {serviceMetric[`${serviceType}d`].map(metric => ( - - ))} - - - - - prevValues.metric !== currentValues.metric - } - > - {({ getFieldValue }) => { - const metric = getFieldValue('metric'); - const isSpaceMetric = serviceMetric[`${serviceType}d`].filter( - item => item.metric === metric, - )[0]?.isSpaceMetric; - console.log('spaces', spaces); - return getFieldValue('metric') && isSpaceMetric ? ( - - - - {spaces?.map(space => ( - - ))} - - - ) : null; - }} - - - prevValues.metric !== currentValues.metric - } - > - {({ getFieldValue }) => { - const metric = getFieldValue('metric'); - const typeList = serviceMetric[`${serviceType}d`].filter( - item => item.metric === metric, - )[0]?.metricType; - return metric ? ( - - - {typeList?.map(params => ( - - ))} - - - ) : null; - }} - - - - {TIME_INTERVAL_OPTIONS.map(value => ( - - ))} - - - -
- ); - } -} -export default connect(mapState, mapDispatch)(withRouter(ServicePanel)); diff --git a/src/pages/ServiceDashboard/Detail/index.less b/src/pages/ServiceDashboard/Detail/index.less index bf12d897..522d02e8 100644 --- a/src/pages/ServiceDashboard/Detail/index.less +++ b/src/pages/ServiceDashboard/Detail/index.less @@ -1,104 +1,104 @@ -.service-metrics { - background-color: #fff; - height: 100%; - overflow: auto; - .common-header { - span { - text-transform: capitalize; - } - } +.ant-spin-nested-loading.service-detail { + // height: 100%; - .container { - padding: 20px 30px 0; - height: calc(100% - 75px); - display: flex; - position: relative; - flex-direction: column; + .ant-spin { + position: absolute; + left: 50%; + top: 80px; + transform: translate(-50%, 50%); + } - .metrics-params-form { - display: flex; - flex-direction: column; - >.ant-row:first-child { - justify-content: space-between; - } +} - .row-left { - display: flex; +.dashboard-detail { + display: flex; + flex-direction: column; - .ant-radio-group { - > label { - height: 34px; - line-height: 34px; - font-size: 12px; - text-align: center; - padding: 0 10px; - } - } + >.filter { + display: flex; + justify-content: space-between; + // align-items: flex-end; + flex-wrap: wrap; + position: relative; + min-height: 75px; + padding: 24px 24px 0; + background-color: #fff; + } - .ant-picker-range { - height: 34px; - margin-left: 16px; - } - } + >.detail-content { + margin-top: 24px; + flex: 1; + display: flex; + flex-direction: row; + flex-wrap: wrap; - .service-status{ - margin-left: 10px; - } + >.chart-item { + width: calc(50% - 15px); + height: 350px; + border: 2px solid rgba(0, 0, 0, .06); + margin-bottom: 24px; + border-radius: 3px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #fff; + margin-right: 15px; - .status-panel { - padding-left: 10px; - } + >.chart-title { + width: 100%; + line-height: 30px; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + margin-top: 12px; + } - .metric-filter { - width: calc(100% - 90px); - flex: 1; - display: flex; - .ant-form-item:not(:first-child) { - padding-left: 15px; - } - .metric-params{ - .ant-form-item-control{ - .ant-select{ - width: 85px; - min-width: unset; - } - } - } + >.chart-content { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; - .metric-list{ - .ant-form-item-control{ - min-width: 180px; - } - } - } + >.nebula-chart { + height: 90%; + width: 90%; + display: flex; + align-items: center; + justify-content: center; + } + } + >.base-line { + position: absolute; + top: 15px; + right: 24px; + } + } - .metric-info-icon { - margin: 6px 10px 0 10px; - } - } + @media screen and (max-width: 1960px) and (min-width: 1200px) { + >.chart-item { + width: calc(50% - 15px); + } + } - .nebula-chart { - flex: 1; + @media screen and (min-width: 1960px) and (max-width: 2480px) { + >.chart-item { + width: calc(33.3% - 15px); + } + } - .ant-spin-container { - height: 100%; + @media screen and (min-width: 2480px) { + >.chart-item { + width: calc(25% - 15px); + } + } + } +} - .nebula-chart-line { - height: 100%; - } - } - } - .btn-icon-with-desc { - position: absolute; - right: 30px; - top: 85px; - } - } -} -.service-modal { - .ant-modal-header { - visibility: hidden; - } -} diff --git a/src/pages/ServiceDashboard/Detail/index.tsx b/src/pages/ServiceDashboard/Detail/index.tsx index 9f068fca..2adeb1bd 100644 --- a/src/pages/ServiceDashboard/Detail/index.tsx +++ b/src/pages/ServiceDashboard/Detail/index.tsx @@ -1,368 +1,323 @@ -import { Spin } from 'antd'; -import React from 'react'; +import { Popover, Spin } from 'antd'; +import React, { useEffect, useMemo, useState } from 'react'; import intl from 'react-intl-universal'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { RouteComponentProps, useLocation, withRouter } from 'react-router-dom'; import { connect } from 'react-redux'; -import { FormInstance } from 'antd/lib/form'; import { Chart } from '@antv/g2'; -import dayjs from 'dayjs'; -import Panel from './Panel'; import { - CARD_POLLING_INTERVAL, - DETAIL_DEFAULT_RANGE, - NEED_ADD_SUM_QUERYS, - getBaseLineByUnit, getDataByType, - getDefaultTimeRange, - getMaxNum, getProperTickInterval, + calcTimeRange, + getBaseLineByUnit, + getMaxNum, + getMetricsUniqName, } from '@/utils/dashboard'; import { IDispatch, IRootState } from '@/store'; import { VALUE_TYPE } from '@/utils/promQL'; -import { SERVICE_QUERY_PERIOD } from '@/utils/service'; -import { IStatRangeItem } from '@/utils/interface'; -import ServiceHeader from '@/components/Service/ServiceHeader'; +import { IMetricOption, MetricScene, ServiceMetricsPanelValue } from '@/utils/interface'; import LineChart from '@/components/Charts/LineChart'; import { configDetailChart, updateDetailChart } from '@/utils/chart/chart'; import Icon from '@/components/Icon'; -import Modal from '@/components/Modal'; -import BaseLineEdit from '@/components/BaseLineEdit'; +import BaseLineEditModal from '@/components/BaseLineEditModal'; -import './index.less'; +import ServiceMetricsFilterPanel from '@/components/ServiceMetricsFilterPanel'; +import { shouldCheckCluster } from '@/utils'; -interface IState { - serviceType: string; - metricsValueType: VALUE_TYPE; - instanceList: string[]; - data: IStatRangeItem[]; - maxNum: number; - totalData: IStatRangeItem[]; - baseLine: number | undefined; - interval: number; - instance: string; - metricSpace: string; - metric: string; - metricFunction: string; - period: number; - timeRange: dayjs.Dayjs[]; -} +import './index.less'; -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetStatus: dispatch.service.asyncGetStatus, - asyncGetMetricsData: dispatch.service.asyncGetMetricsData, - asyncGetMetricsSumData: dispatch.service.asyncGetMetricsSumData, + asyncGetSpaces: dispatch.serviceMetric.asyncGetSpaces, + asyncFetchMetricsData: dispatch.service.asyncGetMetricsData, asyncUpdateBaseLine: (key, value) => dispatch.machine.update({ [key]: value, }), + updateMetricsFiltervalues: dispatch.service.updateMetricsFiltervalues, }); const mapState = (state: IRootState) => ({ - loading: state.loading.models.service, aliasConfig: state.app.aliasConfig, serviceMetric: state.serviceMetric, + cluster: (state as any)?.cluster?.cluster, + metricsFilterValues: (state as any).service.metricsFilterValues as ServiceMetricsPanelValue, + instanceList: (state as any).service.instanceList, + loading: state.loading.models.service, }); interface IProps extends ReturnType, ReturnType, - RouteComponentProps {} + RouteComponentProps { } + +let pollingTimer: any; + +function ServiceDetail(props: IProps) { + const { asyncFetchMetricsData, serviceMetric, loading, cluster, updateMetricsFiltervalues, metricsFilterValues, instanceList, asyncGetSpaces } = props; + + const location = useLocation(); + + const [serviceType, setServiceType] = useState(''); + const [dataSources, setDataSources] = useState([]); + const [showLoading, setShowLoading] = useState(false); -class ServiceDetail extends React.Component { - chartInstance: Chart; + useEffect(() => { + setShowLoading(loading && metricsFilterValues.frequency === 0) + }, [loading, metricsFilterValues.frequency]); - pollingTimer: any; + useEffect(() => { + const [ start, end ] = calcTimeRange(metricsFilterValues.timeRange); + if (shouldCheckCluster()) { + if (cluster?.id) { + asyncGetSpaces({ + clusterID: cluster.id, + start, + end + }) + } + } else { + asyncGetSpaces({ + start, + end + }) + } + }, [metricsFilterValues.timeRange, cluster]) - modalHandler; + const metricOptions = useMemo(() => { + if (serviceMetric.graphd.length === 0 + || serviceMetric.storaged.length === 0 + || serviceMetric.metad.length === 0) { + return []; + } + let options = []; + if (serviceType) { + options = serviceMetric[`${serviceType}d`].map(item => { + return { + metric: item.metric, + isSpaceMetric: item.isSpaceMetric, + metricType: item.metricType, + valueType: item.valueType, + } + }) + } + return options; + }, [serviceType, serviceMetric.graphd, serviceMetric.metad, serviceMetric.storaged]); - formRef = React.createRef(); + const metricTypeMap = useMemo(() => { + const map = {}; + metricOptions.forEach(option => { + option.metricType.forEach(type => { + const { key, value } = type; + const metricItem = { + metric: option.metric, + isSpaceMetric: option.isSpaceMetric, + metricType: option.metricType, + valueType: option.valueType, + metricFunction: value, + }; + if (!map[key]) { + map[key] = [metricItem]; + } else { + map[key].push(metricItem) + } + }) + }) + return map; + }, [metricOptions]); - constructor(props: IProps) { - super(props); - const { - location: { pathname }, - } = this.props; - const regx = /(\w+)-metrics/g; - const match = regx.exec(pathname); + useEffect(() => { + const match = /(\w+)-metrics/g.exec(location.pathname); let serviceType = ''; if (match) { serviceType = match[1] || 'graph'; } - this.state = { - serviceType, - metricsValueType: VALUE_TYPE.number, - data: [], - maxNum: 0, - totalData: [], - instanceList: [], + setServiceType(serviceType); + }, [location]); + + const metricCharts: any = useMemo(() => { + if (Object.keys(metricTypeMap).length === 0) return []; + const { metricType } = metricsFilterValues; + const charts = metricTypeMap[metricType].map((metric, i) => ({ + metric, + chartInstance: undefined, + index: i, baseLine: undefined, - interval: DETAIL_DEFAULT_RANGE, - instance: 'all', - metricSpace: '', - metric: '', - metricFunction: '', - period: SERVICE_QUERY_PERIOD, - timeRange: getDefaultTimeRange(), - }; - } + })); + return charts; + }, [metricTypeMap, metricsFilterValues.metricType]) - componentDidMount() { - const { serviceMetric } = this.props; - const { serviceType } = this.state; - if (serviceMetric[`${serviceType}d`].length > 0) { - this.setState( - { - metric: serviceMetric[`${serviceType}d`][0]?.metric, - metricFunction: - serviceMetric[`${serviceType}d`][0]?.metricType?.[0].value, - metricsValueType: serviceMetric[`${serviceType}d`][0]?.valueType, - }, - this.pollingData, - ); + useEffect(() => { + if (pollingTimer) { + clearPolling(); } - } - - /* eslint-disable-next-line */ - UNSAFE_componentWillReceiveProps(nextProps) { - const { serviceMetric } = this.props; - const { serviceType } = this.state; - if ( - nextProps.serviceMetric[`${serviceType}d`] !== - serviceMetric[`${serviceType}d`] - ) { - this.setState( - { - metric: nextProps.serviceMetric[`${serviceType}d`][0]?.metric, - metricFunction: - nextProps.serviceMetric[`${serviceType}d`][0]?.metricType?.[0] - .value, - metricsValueType: - nextProps.serviceMetric[`${serviceType}d`][0]?.valueType, - }, - this.pollingData, - ); + if (shouldCheckCluster()) { + if (cluster?.id) { + pollingData(); + } + } else { + pollingData(); } - } + }, [metricsFilterValues.timeRange, metricsFilterValues.frequency, + metricsFilterValues.metricType, metricsFilterValues.period, metricsFilterValues.space, cluster, metricCharts]); - componentWillUnmount() { - this.clearPolling(); - } + useEffect(() => () => { + clearPolling(); + }, []) - pollingData = async () => { - await this.asyncGetMetricsData(); - this.pollingTimer = setTimeout(this.pollingData, CARD_POLLING_INTERVAL); - }; - - clearPolling = () => { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); + const clearPolling = () => { + if (pollingTimer) { + clearTimeout(pollingTimer); } }; - resetPollingData = () => { - this.clearPolling(); - this.pollingData(); + useEffect(() => { + if (dataSources.length === 0) return; + updateChart(); + }, [metricsFilterValues.instanceList, dataSources]) + + const asyncGetMetricsData = async () => { + const { timeRange, period, space, metricType } = metricsFilterValues; + const [startTime, endTime] = calcTimeRange(timeRange); + const getPromise = (chart) => { + return new Promise((resolve, reject) => { + const item = metricTypeMap[metricType].find(metricItem => metricItem.metric === chart.metric.metric); + asyncFetchMetricsData({ + query: item.metricFunction + period, + start: startTime, + end: endTime, + space: serviceType === 'graph' ? space : undefined, + clusterID: cluster?.id, + }).then(res => { + resolve(res); + }).catch(e => { + reject(e) + }); + }) + } + Promise.all(metricCharts.map(chart => getPromise(chart))).then((dataSources) => { + setDataSources(dataSources) + }) }; - asyncGetMetricsData = async () => { - const { timeRange, metric, metricFunction, period } = this.state; - const [startTime, endTime] = timeRange as any; - let data = await this.props.asyncGetMetricsData({ - query: metricFunction + period, - start: startTime, - end: endTime, - }); - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - const totalData = await this.props.asyncGetMetricsSumData({ - query: metricFunction + period, - start: startTime, - end: endTime, - }); - data = data.concat(totalData); + const pollingData = async () => { + asyncGetMetricsData(); + if (metricsFilterValues.frequency > 0) { + pollingTimer = setTimeout(pollingData, metricsFilterValues.frequency); } - const instanceList = data.map(item => item.metric.instanceName); - this.setState({ - data, - instanceList, - }); - this.updateChart(); }; - updateChart = () => { - const { timeRange, instance, serviceType, data } = this.state; - const { aliasConfig } = this.props; - const [startTime, endTime] = timeRange as any; - const _data = getDataByType({ - data, - type: instance, - name: 'instanceName', - aliasConfig, - }); - this.setState({ - maxNum: getMaxNum(_data), - }); - updateDetailChart(this.chartInstance, { - type: serviceType, - tickInterval: getProperTickInterval(endTime - startTime), - }).changeData(_data); + const updateChart = () => { + const { aliasConfig } = props; + const { instanceList } = metricsFilterValues; + const [startTimestamps, endTimestamps] = calcTimeRange(metricsFilterValues.timeRange); + metricCharts.forEach((chart, i) => { + if (chart.chartInstance) { + const data = getDataByType({ + data: dataSources[i] || [], + type: instanceList, + nameObj: getMetricsUniqName(MetricScene.SERVICE), + aliasConfig, + }); + chart.maxNum = getMaxNum(data); + updateDetailChart(chart.chartInstance, { + type: serviceType, + tickInterval: getProperTickInterval(endTimestamps - startTimestamps), + }).changeData(data); + chart.chartInstance.autoFit = true; + } + }) }; - renderChart = (chartInstance: Chart) => { - const { timeRange, metricsValueType } = this.state; - const [startTime, endTime] = timeRange as any; - this.chartInstance = chartInstance; + const renderChart = (i: number) => (chartInstance: Chart) => { + const [startTimestamps, endTimestamps] = calcTimeRange(metricsFilterValues.timeRange); + metricCharts[i].chartInstance = chartInstance; configDetailChart(chartInstance, { - tickInterval: getProperTickInterval(endTime - startTime), - valueType: metricsValueType, + tickInterval: getProperTickInterval(endTimestamps - startTimestamps), + valueType: metricCharts[i].metric.valueType, }); }; - handleServiceTypeChange = serviceType => { - this.setState( - { - serviceType, - }, - this.pollingData, - ); + const handleBaseLineEdit = (metricChart) => () => { + BaseLineEditModal.show({ + baseLine: metricChart.baseLine, + valueType: metricChart.metric.valueType, + onOk: (values) => handleBaseLineChange(metricChart, values), + }) }; - handleMetricsValueTypeChange = metricsValueType => { - this.setState({ - metricsValueType, + const handleBaseLineChange = async (metricChart, values) => { + const { baseLine, unit } = values; + metricChart.baseLine = getBaseLineByUnit({ + baseLine, + unit, + valueType: metricChart.valueType, }); + metricChart.chartRef.updateBaseline(metricChart.baseLine); }; - handleIntervalChange = interval => { - this.setState( - { - timeRange: getDefaultTimeRange(interval), - }, - this.asyncGetMetricsData, - ); - }; - - handleConfigUpdate = changedValues => { - const { serviceMetric } = this.props; - const { serviceType } = this.state; - if (changedValues.metric) { - const selectedMetrics = serviceMetric[`${serviceType}d`].filter( - item => item.metric === changedValues.metric, - )[0]; - this.setState( - { - metric: changedValues.metric, - metricsValueType: selectedMetrics.valueType, - metricFunction: selectedMetrics.metricType[0].value, - baseLine: undefined, - }, - this.resetPollingData, - ); - } else { - this.setState(changedValues, this.resetPollingData); - } + const handleMetricChange = async values => { + updateMetricsFiltervalues(values); }; - handleBaseLineEdit = () => { - if (this.modalHandler) { - this.modalHandler.show(); - } - }; - - handleClose = () => { - if (this.modalHandler) { - this.modalHandler.hide(); - } - }; - - handleBaseLineChange = value => { - const { baseLine, unit } = value; - const { metricsValueType } = this.state; - this.setState( - { - baseLine: getBaseLineByUnit({ - baseLine, - unit, - valueType: metricsValueType, - }), - }, - this.handleClose, - ); - }; + const handleRefresh = () => { + setShowLoading(!!loading); + asyncGetMetricsData(); + } - render() { - const { - metricsValueType, - interval, - instance, - metric, - maxNum, - metricSpace, - metricFunction, - period, - timeRange, - serviceType, - baseLine, - instanceList, - } = this.state; - const { loading } = this.props; - const defaultFormParams = { - interval, - instance, - metric, - space: metricSpace, - metricFunction, - period, - timeRange, - }; - return ( -
- -
- +
+
+ -
- - {intl.get('common.baseLine')} -
- - - - (this.modalHandler = handler)} - footer={null} - > - - +
+
+ { + metricCharts.map((metricChart, i) => ( +
+
+ {metricChart.metric.metric} + {intl.get(`metric_description.${metricChart.metric.metric}`)}
+ } + > + + +
+
+ metricChart.chartRef = ref} + renderChart={renderChart(i)} + /> +
+
+ + {intl.get('common.baseLine')} +
+
+ )) + }
- ); - } + + ); } export default connect(mapState, mapDispatch)(withRouter(ServiceDetail)); diff --git a/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.less b/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.less index 55b023b2..48ad002c 100644 --- a/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.less +++ b/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.less @@ -39,6 +39,10 @@ .btn-icon-with-desc { margin-top: 0; } + + .hide-period{ + margin-top: 5px; + } } } diff --git a/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.tsx b/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.tsx index 24003b3f..09c6b496 100644 --- a/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.tsx +++ b/src/pages/ServiceDashboard/ServiceOverview/CustomServiceQueryPanel/index.tsx @@ -1,26 +1,25 @@ -import React from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; import intl from 'react-intl-universal'; import { connect } from 'react-redux'; -import { isEqual } from 'lodash'; import { Popover } from 'antd'; import Icon from '@/components/Icon'; -import { IServicePanelConfig, IStatRangeItem } from '@/utils/interface'; -import { getDataByType } from '@/utils/dashboard'; -import { - SERVICE_DEFAULT_RANGE, - SERVICE_POLLING_INTERVAL, -} from '@/utils/service'; +import { IServicePanelConfig, MetricScene } from '@/utils/interface'; +import { calcTimeRange, getDataByType, getMetricsUniqName } from '@/utils/dashboard'; import Card from '@/components/Service/ServiceCard/Card'; import { IDispatch, IRootState } from '@/store'; +import { shouldCheckCluster } from '@/utils'; +import classnames from "classnames"; import './index.less'; -const mapDispatch = (dispatch: IDispatch) => ({ - asyncGetMetricsData: dispatch.service.asyncGetMetricsData, +const mapDispatch: any = (dispatch: IDispatch) => ({ + asyncGetMetricsData: dispatch.service.asyncGetMetricsData as any, }); const mapState = (state: IRootState) => ({ aliasConfig: state.app.aliasConfig, + cluster: (state as any).cluster?.cluster, + metricsFilterValues: state.service.metricsFilterValues, }); interface IProps @@ -28,112 +27,104 @@ interface IProps ReturnType { onConfigPanel: () => void; config: IServicePanelConfig; - aliasConfig: any; + isHidePeriod?: boolean; } -interface IState { - data: IStatRangeItem[]; -} -class CustomServiceQueryPanel extends React.Component { - pollingTimer: any; +function CustomServiceQueryPanel(props: IProps) { - constructor(props: IProps) { - super(props); - this.state = { - data: [], - }; - } + const { config, cluster, asyncGetMetricsData, onConfigPanel, aliasConfig, metricsFilterValues, isHidePeriod } = props; - componentDidMount() { - this.pollingData(); - } + const [data, setData] = useState([]) - componentWillUnmount() { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); - } - } + let pollingTimer: any = useMemo(() => undefined, []); - componentDidUpdate(prevProps) { - if (!isEqual(prevProps.config, this.props.config)) { - this.resetPollingData(); + useEffect(() => { + if (pollingTimer) { + clearTimeout(pollingTimer); } - } - - resetPollingData = () => { - if (this.pollingTimer) { - clearTimeout(this.pollingTimer); + if (shouldCheckCluster()) { + if (cluster.id) { + pollingData(); + } + } else { + pollingData(); } - this.pollingData(); - }; + return () => { + if (pollingTimer) { + clearTimeout(pollingTimer); + } + } + }, [metricsFilterValues, metricsFilterValues, cluster, config]) - getMetricsData = async () => { - const { config } = this.props; - const { period: metricPeriod, metricFunction } = config; - const end = Date.now(); - const data = await this.props.asyncGetMetricsData({ + const getMetricsData = async () => { + const { period: metricPeriod, metricFunction, space } = config; + const [start, end] = calcTimeRange(metricsFilterValues.timeRange); + const data = await asyncGetMetricsData({ query: metricFunction + metricPeriod, // EXPLAIN: query like nebula_graphd_num_queries_rate_600 - start: end - SERVICE_DEFAULT_RANGE, + start, end, + space, + clusterID: cluster?.id }); - this.setState({ - data, - }); + setData(data) }; - pollingData = () => { - this.getMetricsData(); - this.pollingTimer = setTimeout(this.pollingData, SERVICE_POLLING_INTERVAL); + const pollingData = () => { + getMetricsData(); + if (metricsFilterValues.frequency > 0) { + pollingTimer = setTimeout(() => { + pollingData(); + }, metricsFilterValues.frequency); + } }; - render() { - const { data } = this.state; - const { - config: { metric, period, metricType, baseLine }, - aliasConfig, - } = this.props; - return ( -
-
- +
+ + {config.metric} + +
+ { + !isHidePeriod && ( + <> + + {intl.get('service.period')}: {config.period} + + + {intl.get('service.metricParams')}: {config.metricType} + + + ) + } +
- {metric} - -
- - {intl.get('service.period')}: {period} - - - {intl.get('service.metricParams')}: {metricType} - -
- - {intl.get('common.set')} -
+ + {intl.get('common.set')}
-
- {data.length > 0 && ( - - )} -
- ); - } +
+ +
+
+ ); } export default connect(mapState, mapDispatch)(CustomServiceQueryPanel); diff --git a/src/pages/ServiceDashboard/ServiceOverview/index.less b/src/pages/ServiceDashboard/ServiceOverview/index.less index b86d9d58..8b364b4f 100644 --- a/src/pages/ServiceDashboard/ServiceOverview/index.less +++ b/src/pages/ServiceDashboard/ServiceOverview/index.less @@ -3,5 +3,4 @@ .btn-icon-with-desc { float: right; margin-top: 5px; -} - +} \ No newline at end of file diff --git a/src/pages/ServiceDashboard/ServiceOverview/index.tsx b/src/pages/ServiceDashboard/ServiceOverview/index.tsx index 4831e801..ad585758 100644 --- a/src/pages/ServiceDashboard/ServiceOverview/index.tsx +++ b/src/pages/ServiceDashboard/ServiceOverview/index.tsx @@ -1,6 +1,5 @@ import { Col, Row } from 'antd'; import React from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; import intl from 'react-intl-universal'; import _ from 'lodash'; import CustomServiceQueryPanel from './CustomServiceQueryPanel'; @@ -11,19 +10,20 @@ import Icon from '@/components/Icon'; import { trackPageView } from '@/utils/stat'; import './index.less'; -interface IProps extends RouteComponentProps { +interface IProps { serviceType: string; icon: string; configs: IServicePanelConfig[]; onConfigPanel: (serviceType: string, index: number) => void; getStatus: (payload) => void; + onView: (serviceType: string) => void; } -class ServiceOverview extends React.PureComponent { +class ServiceOverview extends React.Component { handleView = () => { - const { serviceType } = this.props; + const { serviceType, onView } = this.props; trackPageView(`${serviceType}_metrics`); - this.props.history.push(`/service/${serviceType}-metrics`); + onView(serviceType); }; render() { @@ -54,4 +54,4 @@ class ServiceOverview extends React.PureComponent { } } -export default withRouter(ServiceOverview); +export default ServiceOverview; diff --git a/src/pages/ServiceDashboard/index.less b/src/pages/ServiceDashboard/index.less index cfd3b351..9ef35bdd 100644 --- a/src/pages/ServiceDashboard/index.less +++ b/src/pages/ServiceDashboard/index.less @@ -1,14 +1,23 @@ .service-table { - height: 100%; - overflow-y: auto; + min-height: 100%; + background-color: #fff; display: flex; flex-direction: column; + .common-header { + background: #fff; + margin: 15px 0; + justify-content: flex-start; + height: auto; + padding-bottom: 0; + } + > .service-table-item { flex: 1; background: #fff; - margin-bottom: 30px; - border: 1px solid #dcdcdc; + // margin-bottom: 30px; + padding-top: 10px; + border-top: 1px solid #dcdcdc; > .ant-row { height: calc(100% - 50px); @@ -34,4 +43,4 @@ margin-top: 10px; } } -} +} \ No newline at end of file diff --git a/src/pages/ServiceDashboard/index.tsx b/src/pages/ServiceDashboard/index.tsx index 8d2bdcfc..cef7b5e8 100644 --- a/src/pages/ServiceDashboard/index.tsx +++ b/src/pages/ServiceDashboard/index.tsx @@ -1,71 +1,109 @@ -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { connect } from 'react-redux'; import intl from 'react-intl-universal'; +import { RouteComponentProps, useHistory, withRouter } from 'react-router-dom'; + import ServiceOverview from './ServiceOverview'; import { IDispatch, IRootState } from '@/store'; import Modal from '@/components/Modal'; import ServiceCardEdit from '@/components/Service/ServiceCardEdit'; import { METRIC_SERVICE_TYPES } from '@/utils/metric'; +import MetricsFilterPanel from '@/components/MetricsFilterPanel'; + import './index.less'; +import { ServiceMetricsPanelValue } from '@/utils/interface'; +import { calcTimeRange } from '@/utils/dashboard'; +import { shouldCheckCluster } from '@/utils'; -const mapDispatch = (dispatch: IDispatch) => ({ +const mapDispatch: any = (dispatch: IDispatch) => ({ asyncGetStatus: dispatch.service.asyncGetStatus, + asyncGetSpaces: dispatch.serviceMetric.asyncGetSpaces, + updateMetricsFiltervalues: dispatch.service.updateMetricsFiltervalues, updatePanelConfig: values => dispatch.service.update({ panelConfig: values, }), }); -const mapState = (state: IRootState) => ({ +const mapState: any = (state: IRootState) => ({ panelConfig: state.service.panelConfig, aliasConfig: state.app.aliasConfig, + instanceList: state.service.instanceList as any, + cluster: (state as any)?.cluster?.cluster, serviceMetric: state.serviceMetric, + metricsFilterValues: (state as any).service.metricsFilterValues as ServiceMetricsPanelValue, }); interface IProps - extends ReturnType, - ReturnType {} - -interface IState { - editPanelType: string; - editPanelIndex: number; + extends RouteComponentProps, ReturnType, + ReturnType { + onView: (serviceType: string) => void; } -class ServiceDashboard extends React.Component { - pollingTimer: any; - modalHandler; +function ServiceDashboard(props: IProps){ - constructor(props: IProps) { - super(props); - this.state = { - editPanelType: '', - editPanelIndex: 0, - }; - } + const { panelConfig, serviceMetric, updatePanelConfig, asyncGetStatus, onView, instanceList, updateMetricsFiltervalues, metricsFilterValues, asyncGetSpaces, cluster } = props; - handleConfigPanel = (serviceType: string, index: number) => { - this.setState( - { - editPanelType: serviceType, - editPanelIndex: index, - }, - this.modalHandler.show, - ); - }; + const [editPanelType, setEditPanelType ] = useState(''); + const [editPanelIndex, setEditPanelIndex ] = useState(0) + + const history = useHistory(); + + const modalHandlerRef = useRef(); + + useEffect(() => { + const [ start, end ] = calcTimeRange(metricsFilterValues.timeRange); + if (shouldCheckCluster()) { + if (cluster?.id) { + asyncGetSpaces({ + clusterID: cluster.id, + start, + end + }) + } + } else { + asyncGetSpaces({ + start, + end + }) + } + }, [metricsFilterValues.timeRange, cluster]) + + const handleConfigPanel = (serviceType: string, index: number) => { + setEditPanelIndex(index); + setEditPanelType(serviceType); + modalHandlerRef.current.show(); + } - handleModalClose = () => { - if (this.modalHandler) { - this.modalHandler.hide(); + const handleModalClose = () => { + if (modalHandlerRef.current) { + modalHandlerRef.current.hide(); } + } + + const handleView = (serviceType: string) => { + history.push(`/service/${serviceType}-metrics`); }; - render() { - const { editPanelType, editPanelIndex } = this.state; - const { panelConfig, serviceMetric, updatePanelConfig, asyncGetStatus } = - this.props; - // TODO: Use hooks to resolve situations where render is jamming - return ( + const handleMetricsChange = (values) => { + updateMetricsFiltervalues(values); + } + + const handleRefreshData = (values) => { + updateMetricsFiltervalues(values); + } + + return ( + <>
+
+ +
{METRIC_SERVICE_TYPES.map(type => ( { icon={`#iconnav-${type}`} configs={panelConfig[type]} getStatus={asyncGetStatus} - onConfigPanel={this.handleConfigPanel} + onConfigPanel={handleConfigPanel} + onView={onView ?? handleView} /> ))} (this.modalHandler = handler)} + handlerRef={handler => (modalHandlerRef.current = handler)} title={intl.get('service.queryCondition')} footer={null} > @@ -88,12 +127,13 @@ class ServiceDashboard extends React.Component { editType={editPanelType} editIndex={editPanelIndex} panelConfig={panelConfig} - onClose={this.handleModalClose} + onClose={handleModalClose} onPanelConfigUpdate={updatePanelConfig} />
- ); - } + + ); } -export default connect(mapState, mapDispatch)(ServiceDashboard); + +export default connect(mapState, mapDispatch)(withRouter(ServiceDashboard)); diff --git a/src/pages/ServiceManage/ConfigInfo/index.less b/src/pages/ServiceManage/ConfigInfo/index.less index fc807be7..c9694ece 100644 --- a/src/pages/ServiceManage/ConfigInfo/index.less +++ b/src/pages/ServiceManage/ConfigInfo/index.less @@ -12,4 +12,8 @@ } } } + input{ + width: 265px; + float: right; + } } diff --git a/src/pages/ServiceManage/ConfigInfo/index.tsx b/src/pages/ServiceManage/ConfigInfo/index.tsx index 0e81520b..42709ec4 100644 --- a/src/pages/ServiceManage/ConfigInfo/index.tsx +++ b/src/pages/ServiceManage/ConfigInfo/index.tsx @@ -1,6 +1,6 @@ import _ from 'lodash'; -import React from 'react'; -import { Radio, Table } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { Radio,Input, Table } from 'antd'; import intl from 'react-intl-universal'; import { connect } from 'react-redux'; import { IDispatch, IRootState } from '@/store'; @@ -15,89 +15,78 @@ const mapState = (state: IRootState) => ({ const mapDispatch = (dispatch: IDispatch) => ({ asyncGetServiceConfigs: dispatch.nebula.asyncGetServiceConfigs, -}); +}) as any; interface IProps extends ReturnType, ReturnType {} -class ConfigInfo extends React.Component { - componentDidMount() { - this.props.asyncGetServiceConfigs(); - } +const NebulaConfig: React.FC = (props: IProps) => { + const [name, setName] = useState(''); + + useEffect(()=>{ + props.asyncGetServiceConfigs('graph'); + },[]) - handleModuleChange = e => { + const handleModuleChange = e => { trackEvent('service-info', 'click_module'); - this.props.asyncGetServiceConfigs(e.target.value); + props.asyncGetServiceConfigs(e.target.value); + }; + + const getData = () => { + if (!name) { + return configs; + } + return configs.filter((config: any) => config.name.includes(name)); }; - render() { - const { configs, loading } = this.props; - const columns = [ - { - title: ( - - ), - dataIndex: 'module', - render: module => ( - {module} - ), - }, - { - title: ( - - ), - dataIndex: 'name', - }, - { - title: ( - - ), - dataIndex: 'type', - }, - { - title: ( - - ), - dataIndex: 'value', - }, - ]; - return ( -
-
- - {intl.get('service.all')} - Storage - Graph - {/* TODO: Nebula 2.0.1 does not support meta modifications, support can be released in later versions - Meta - */} - -
- record.module + record.name} - dataSource={configs} - columns={columns} + const { configs, loading } = props; + const columns = [ + { + title: ( + + ), + dataIndex: 'name', + }, + { + title: ( + + ), + dataIndex: 'value', + }, + ]; + return ( +
+
+ + Storage + Graph + {/* TODO: Nebula 2.0.1 does not support meta modifications, support can be released in later versions + Meta + */} + + setName(e.target.value)} />
- ); - } +
record.value + record.name} + dataSource={getData()} + columns={columns} + /> + + ); } -export default connect(mapState, mapDispatch)(ConfigInfo); +export default connect(mapState, mapDispatch)(NebulaConfig); diff --git a/src/pages/ServiceManage/ServiceInfo/index.less b/src/pages/ServiceManage/ServiceInfo/index.less index cd9facad..d025367c 100644 --- a/src/pages/ServiceManage/ServiceInfo/index.less +++ b/src/pages/ServiceManage/ServiceInfo/index.less @@ -17,4 +17,8 @@ } } } + .partition-table, .leader-table{ + max-height: 350px; + overflow: auto; + } } diff --git a/src/pages/ServiceManage/ServiceInfo/index.tsx b/src/pages/ServiceManage/ServiceInfo/index.tsx index b5291ffc..a607108d 100644 --- a/src/pages/ServiceManage/ServiceInfo/index.tsx +++ b/src/pages/ServiceManage/ServiceInfo/index.tsx @@ -15,7 +15,7 @@ const mapState = (state: IRootState) => ({ const mapDispatch = (dispatch: IDispatch) => ({ asyncGetServices: dispatch.nebula.asyncGetServices, -}); +}) as any; interface IProps extends ReturnType, ReturnType {} @@ -84,6 +84,7 @@ class ServiceInfo extends React.Component { /> ), dataIndex: 'Partition distribution', + render: distribution =>
{distribution}
}, { title: ( @@ -93,6 +94,7 @@ class ServiceInfo extends React.Component { /> ), dataIndex: 'Leader distribution', + render: distribution =>
{distribution}
}, ]; return ( diff --git a/src/static/iconfont/iconfont.js b/src/static/iconfont/iconfont.js index fe3a9cb8..46813ca8 100644 --- a/src/static/iconfont/iconfont.js +++ b/src/static/iconfont/iconfont.js @@ -1 +1 @@ -!function(a){var l,o,h,v,m,i='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),z=function(a,l){l.parentNode.insertBefore(a,l)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function e(){m||(m=!0,h())}function n(){try{v.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}e()}l=function(){var a,l;(l=document.createElement("div")).innerHTML=i,i=null,(a=l.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",l=a,(a=document.body).firstChild?z(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),l()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(h=l,v=a.document,m=!1,n(),v.onreadystatechange=function(){"complete"==v.readyState&&(v.onreadystatechange=null,e())})}(window); \ No newline at end of file +!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window); \ No newline at end of file diff --git a/src/static/images/Graphd-icon60.png b/src/static/images/Graphd-icon60.png new file mode 100644 index 0000000000000000000000000000000000000000..b98a1c10d30653cb604f1719c1fc7bbf5c5beff0 GIT binary patch literal 3287 zcmV;|3@G!7P)fX2(X$It$`-6Y!s*$3DB<6rJJk^$A3T(8`*Rh7uj^v z)ZIC&251(}CSuV|8n~$pB#2|kFrCPcN@_&CXqk^;&o|_#6isqw$aASX?EL{WElM0d zzB%Wfd(J&`L)0M~8ymBhWzB@c;WL#=WriP0)^3LG$%R6pEZ=AOJHEx=ab|36EJGa- z^7F?mWO8$J^CI8BMMlhClZDWcNaV`!@Nm}alRODr6ZY+zu!cQ`g}&xhXjg$@vFs); zld)Lc$nxz3J;_e}+^x#Yj_{VW-@hXbez%7#zH*mn)ve$jL@$p?2%$9qB@BT!F zz`{ab8Xq6O>ioPT@RKJ`(#2wNj{ln`Ly*ap%jM(Fc)a7HS=`HH#O()?(P;E-=Lg$@ zYu0;@41EJzaSB#!TX4;K4SfX9kL~=0yjz$M6G(KX{_^3t4CW?GtrhZ{-J4AEyQTieqp?i-bw3MrU=Fy=F9e>WUZ?B#% z(7~idstIv{p+bsScod`Ci!obdHx4Aq^wQKejSQ;)(5is2AAK65zkV9E|L?}RnIgSD zt5#?w0_UHG>7NTjY_SpY%ZbSXojmfCg2oaj1%G#am@PK!`?+tPe`;H_3dCJB#Qpo1 zBRj6xFR*wJB#|NRZ?BE)xMIKj@y4KS@vqOye`f!un+x5D>j07A-h)85;yPT<#OM*=;$mAR;iVY%Q;5 zyjxws6<9Stww?lSZL51lI~wjUjXtD7Oal(yF*LtK;wRfzj57WcK&^23~Xh61j-8OjalrZ+%kv71vov_a#@FFs#7h>%9(M?of3z1EUxB{1+S z^2^gDdawd3m_5hSAg|s)XK{hH0t1Yv%`#aK zcX8#}>B{7R?nKV=>wUfu_AD-@H!9|?lIWQStl=cx>qTDAHy0Nyt{`F#ca#mv+9#9u zr82+y#kL*f>5kSG>`={Q#q2Th?!&5EZU?3!5E+&T(J@`BA5WtLu=X-flPY##g(;1n zzz&ge5X1$65Eww@KYTRgDeyt?g(WqJfZg0JP6n@$GQ@r7!V}SQo@%sq(e*r7iO}~C zShg!Iv?;^>XFX(3aV(Es+N-#*`ZvxO0wpfczc+-cd0=FOV*Dki`3sJy=T?eEs}KMca$$yb!+`fe?79ZVYWDGQ?fZ$$uP(d$yJ_Lk;BXMKufjRS^h*8v~$5 zkum0~7S};VHAyAfzuVkqxM^Soai2e2wli4E3OWp$i3{*vmU6)nS^IbT?>6EggY`X* zf|2-;=7&?Imes1&O(U=vZmxIXp_-5IV5RkOmvg}hw6qu4ZQh$xWKHd+Ppg|oU@E1Dg3rC=5In&VJP?4UbR$R-2!VevS>BnN{A)q2xXq?OSS5noG^{sB z?imC^V9ZA$GOhr3M?t}Tf1*NDhs$l}x5z(ZMQ6~GAAwo2GgoWm@<8^vRr>AAO~r|RI=#D44?&y?5WE4zs{cKf;o|t) zh33LRSS&7ZtQV2}%hSDWVeJ&H1@1&#titHz+FB49!Bn$(_0fYCB}R51b1(XoJ3(84 z{dV`I*&V+)Rqp!2Y3Q1$7r9+KOb|rYat!W7Tr^cnTorHfK(#3xZrQEZ2_6Px@?Kq5 z!VB72uwQX;RXFLnDaKeNI~E?wTybD8>6yT22hBEI&Hzy^B3Nf`*=f@?j@@{vgifeV z$EpqB&U^Mc=f)HZBG+fRun}0U;j$G;do!@!UEocrz+N|sf(63GV&{=go35SZtPrG= zOE}Bx#V#Vit?3$5B8ZJ! zo?m}+d-p|HFx^SUt68a3+&w#WXK{c&UvL*W5{d9Fj9ov;D@P}aw$Q39T8Y5dGlTR& zM$NuQd)0K^su|s3U?5@(+zGhaQ1#kk@*;~z(JIA7)Ae3;)FV&*%5<@5IVvWk78L}Nv z*ogcIjeVjz*_O8zVgjq`oBiyzo;#mm+9fGOJ-IE-1eVAmwPH!rPQJCgx=w$|-QGlw zq;?muz<1a9Msm9J|M%?)5tIfr5rI`MMANm_s!O9Aq7n&d1FFz2&GoW59g_yw83RYIAIB6s~cAzNk zs!tw~{mi0Kw4@-gUfr+g3OD`I)Dw~dwc(?}TLg*<3=4i!U{eZ+`_6?t1%c|yjP5@( zJOK*-(umK4Up-yu%90&%gVY0bVzSVc$SMo|%9iY4^~3u&hJ2+Ou`UaB-yn1hG~*jC z^?mj2ps#J|rw$eD6GYWU*veN`qKt@D9_@tg#SUP-0%^uas}Wjw*ji|aIX+_9RaeK9OJp^9m`y?6q5xZ2A zBST*j3Wc%|_!b%Z63epghbxszh7A46e3+RhLtnz$X6!iL1X_JbHjzjiw=-GnrhiX{ zej|f_YztmpT}?(JkySGE4drtAcq)~$qiEv)>fi$HIC8#L|5(#<$HvCS9RKkw8G^(u7I&uc z!KMZOQmOPy&SRPcNI+n%GCc0iHvL_@C$6onUF9cVB!jlVE%VU&R;%B)U9wYq;;;FZ zC4+7tD8AHAT;RGU3xhVoZa&zb$)E+WS|lqb0doj+`8)m_Dt=>QV;Smz{{ns* V2wJWES%d%p002ovPDHLkV1oXTIRXFx literal 0 HcmV?d00001 diff --git a/src/static/images/Metad-icon60.png b/src/static/images/Metad-icon60.png new file mode 100644 index 0000000000000000000000000000000000000000..ec1051a47565824808970c4008e72fd9d25ef542 GIT binary patch literal 3167 zcmV-l450IgP)CZ=hs5V}w(ER2nfRk?3s3G54d#}_suVMypDuFy(>(PGI=%!{nW+D4Tg z3&mn_DX37Oz~pD|Wv$k3s5LK+kB{FP^!FnL_D%9X=(!>?tcN;etF2aRu3RqHe)hMc z-mtyBy-H7)M20hv$mnU$^}n9)e*tD$Wa2sptSwpHd;PD!)q<(z{+^!xA~KwXguYcO zl@{gihXOa7&6$IPgJt@0Mr625t=(>)m;U(BM3cDlA`>?aOb!nZ-;uv~E!c0p--?XX zK&`lpR_wK4zx8IM2pS)I@e669uZWCPLBpn--ZN%}sOPmTGExVLJeQ|9b46sN5=`d~ zk;<3FJHU3UBOYzN7R5qde0XX^oEsey?*J7D{63jzkyaq==ihIOyK8TUX>a_pDE{zX zG1WqowwH1gz1k8foeGq~Lgb@3OXB|V?4^8!EE z*zwHq3u?Vlk{18$tiWOXEPQNeO7sw{Ik^pyH`y)h%y2o-BMH*BqieD#F>m^15c&RkNL#z=Njo z1|f$%+zA10dD%L-!s(X3?YHKngib`@xc#hGzC`77yz<-aPHN4zQGvsT7rC|noL~NI z$kqa(AHIAgHV)dn@+9=bANl3+y}tNh`e#wKI58ry?ld0!Cc=vs#)e}luOdzUIaYz= z2rnL4eqxkuj&*>g;aZz8;5?wfidji z&i0u0vtfasg}>#R;f;MNzsWC;MS%0~Pl>3BTwn+bfmP^+7o!eGh9yq_jCEmAx8%qL z{^aTRfquxepGC`C_j|eWCc_Qjvr6=g2?||cwmspP{ubAT$ySv|1;*lnD2d$8tp)hp zu;E1=KfBlMiYGh!;`#1@CoJZCIoFvzF)S{e9A;e$WgrHII~@Ov|F2h``VB$;IqGFc zz?dWZ_!pT!6F_KGz*+i#ai$r)FzeKt zuqk#~n8298(+pD>#?QiBVtgG{?f1l{egrBb=9W1v8fEd|`rC6S%ykg0nZVQyFM_Wc zd)^J@m|q_A3G_+C{j!A*`*+{0_P-c0ldf$wJfu*qtH zpS@O_=HvKW75Hpb1v43N34_%FKUakL9vd)`s%t!NCL4-WVYR?Ptp~05seyq0n!*gP zADEA->0qtEs| zj$`N(mR-ey0-Gy<`th?n_-<+P|LV*BATtD9F&!9*j#1P#cC5BbYmgDiT$-$?Ge-;usyY* z)dJ5dTIojy5_qfDM9+?yvm{}#T3}VG1GJtv*GA#9RlWHOnZV)Um|>kw<2uLjlOTMb(O>AW?|S{VbSf7jj2C5$N#t z1u0q+1o_?c#Ibxb-iy!RR21x|asej<_BLEksLi35-JNbcl6GTw!v$6XsT1&CPE^YC z`wpy$2F~nkKnjPAR-3g3N^Z1<5Lj`6x#QmHMJ#Mb8AQVnFwqG$!Zf>dzLHaWlaTBT z`-8a4WFR6QQ4M8KAza#*1e}^|Kud!uolYP$2yw5|wMQ+Q#o~~-z+P?(jR|upvdI7g z0xr7Z!X?@>;y=87T zR+-8uj#LR@QaHt#GbAj=1jZWYi$lS}$wQoRlepPqA;8C{PaP|bQZHdrCF0u4bI6k7~XLVCJ)%^-= za>=jpRE9uiedC4}r6~>*TtKUW5WD0Xds7*L>7hcXl_Bu6Fvsw-9CM6SnP2ay%R7_e^4n$RQWj%`u?8kIM_ zOh(tozwpa*%`w~yvduw&5$lNUO-f9XBf2&(wh3+Ah+J{=w_!`;Woj;VLAIt>Z`0qN~Y@>s8ZR!-e zhI{47KZiBjar;?V8XUr5ayBZFgRyM$&${77-F#g5-Vhe%*ffb9ZHHQa-xrz#Zjux_ z%JPqWp}4~y-SSe{IQ?u8Rvb>5gn!0$|4mUPuHS^Ud8APu73(eyd*0c@Df=;Hwn3cX zMGSi|p0#Dwhjj-;$1OA$m;7uHRvz&lx7k*q8;*pi5H~458-$etOT0rZtDEc-N{)L- zrFZg5t;x>+0n0HzGo`hH(==8KEQ#A}yY6R$a6(`SMCP7k3cTeu0Vk%}8eWVNrvwI3 z`DaAc5MJc^Sxg2jPEiT);Kka?erS&mM1ccri*K@D@GCdrEPP==x_a zaZlqJq6UG#r5|R5!(h%93k-CoB?sZ6pB+Rlms^Fv|D>OsgpZhU6jc0;xxBD

m zu?h)(iGH1-i_eHGND>tgu!fE<-Q)kzJzkxdm{<`Z_z8_Z?;jJSTyg*a002ovPDHLk FV1lhxBN+ey literal 0 HcmV?d00001 diff --git a/src/static/images/Storaged-icon60.png b/src/static/images/Storaged-icon60.png new file mode 100644 index 0000000000000000000000000000000000000000..39b94f8b3581b06f9783695345c4b9f91adfaf0a GIT binary patch literal 1672 zcmV;326y?1P)xkp)Ksaen!eZtqOgrHi%N|uRqDQrrmJom!e0>BbQ{X5tA2&}z`f1}qiJ&BP3$V%v?wx7q&w{)J{wY8o*2 zvv*Ln?TxE>uCK3e`l!bn23&2Df8b@7pzZO1Y}Iw$$#goMf8nu4FDMiWtGpN`=olQb zjI<{Yua6yGARryEw{RN4zI7PV6NOxd2yGZV{y>a0|Ntdwcqyx-r3p7R4SE4 zzMCQFWO>i?hHL%teiO~%&Je;iK`N0*+^+o+R&cd?HwcWuRh*V84lB4?y( zl**)vvVfsIAz;HHV8g`;LcO1lH+}r+*DG3|uL>&IaM~-_9!^m_lP15oNACI)3U~?8 z(Hd~qWQO7+L*ac}-u&`K+Wm2zf+tT2qM@ycwmH~ZxHVRnO&30Rn;<&+0v_yXxZX;3 zhc}3Zwt$1Ocl3j0mmnJ20uBdMdoPCU9kCwN&{x4cTHgI>y>8U(-hWKxpw6bo2$BY5a9uF%I79wh5wvL6+h4%`SBE4cGgdMa(XYkhXv&$jZuQ z-8vau?JYV4(a}1JmVjlRaBQfDh^yA)&>>pd_hd=cde|(|27@faVRm7g!t6q52?918 z0ydnw*@f?>3Di(^MrHW7R1LCfr_Ba9ZT3to#iz|;cA??W2OACn8x8>*4gnhu0UJ); z?7~uo{Cf{@dAl*4l zmQXKz^j_U%?Cg<2N?d)NAUfItmPy}@n&tTAS24RVLRu^MNL}cUqoOTf&uRSSUNV&h zGyJ2YFW`+lx$FPct&>aJ?f(2HL3FgXjpap$yv-8XL%m_RxWs$(w0C|USFyKFpb7S5 z84j}x+Z1LOLQ4>^;SjLl)XgqbbQU0}on0sa=Sczk=2fNt*vm?;S!mxFB}MWwir(Nm z83N;2mbEGsyhdOo-}nED1woJ_Fx6^|tr8fCv&~s@hx=hUmX{(hiPPWTKOAl*aWnlD zfoXE`Bdp-9t*w-8+v+a21?SH5yy0{@?SvJ~ck-xWqbx|ca>uHzsaCOU6QeKgYz|lN zWG&$RU6s`;o+apXv$b$V-M;97fq^+R)tyVS^!@f1PZl4hB^)>}tC-t>5I^@TV1<`A?S z85B=93s)R_z+XCT8Fup@`<9?BNY%=Y3>g$3_gsrkfVY)1yaRiNo9zk0F=XAsol$%U za85R9TYb@q0zQaG+F5?HEC(*9A7uE=GlZQasz`u&8R+t7{Hq-COD2;!I)!H$6;=UX Sx7Bd~0000; interface ILoadingPlugin { diff --git a/src/store/models/index.ts b/src/store/models/index.ts index 73321ded..3e57d90d 100644 --- a/src/store/models/index.ts +++ b/src/store/models/index.ts @@ -1,6 +1,6 @@ export * from './app'; -export * from './machine'; +export { machine } from './machine'; export * from './nebula'; -export * from './service'; +export { service } from './service'; export * from './setting'; -export * from './metric'; +export { serviceMetric } from './metric'; diff --git a/src/store/models/machine.ts b/src/store/models/machine.ts index 3295524e..0e5b1203 100644 --- a/src/store/models/machine.ts +++ b/src/store/models/machine.ts @@ -1,12 +1,14 @@ import { createModel } from '@rematch/core'; import _ from 'lodash'; -import service from '@/config/service'; -import { NEED_ADD_SUM_QUERYS, getProperStep } from '@/utils/dashboard'; +import serviceApi from '@/config/service'; +import { NEED_ADD_SUM_QUERYS, getProperStep, getMetricsUniqName } from '@/utils/dashboard'; import { LINUX } from '@/utils/promQL'; -import { IStatRangeItem, IStatSingleItem } from '@/utils/interface'; +import { IStatRangeItem, IStatSingleItem, MetricScene, MetricsPanelValue } from '@/utils/interface'; +import { unique } from '@/utils'; +import { InitMachineMetricsFilterValues } from '@/utils/metric'; const PROMQL = LINUX; -interface IState { +export interface IState { cpuStat: IStatRangeItem[]; diskStat: IStatRangeItem[]; memoryStat: IStatRangeItem[]; @@ -16,284 +18,241 @@ interface IState { networkStat: IStatRangeItem[]; memorySizeStat: IStatSingleItem[]; diskSizeStat: IStatSingleItem[]; + instanceList: any[]; + metricsFilterValues: MetricsPanelValue; } -export const machine = createModel({ - state: { - cpuStat: [] as IStatRangeItem[], - diskStat: [] as IStatRangeItem[], - memoryStat: [] as IStatRangeItem[], - networkOutStat: [] as IStatRangeItem[], - networkInStat: [] as IStatRangeItem[], - networkStat: [] as IStatRangeItem[], - loadStat: [] as IStatRangeItem[], - memorySizeStat: [] as IStatSingleItem[], - diskSizeStat: [] as IStatSingleItem[], - }, - reducers: { - update: (state: IState, payload: any) => ({ - ...state, - ...payload, - }), - }, - effects: () => ({ - async asyncGetCPUStatByRange(payload: { - start: number; - end: number; - metric: string; - }) { - const { start, end, metric } = payload; - const _start = start / 1000; - const _end = end / 1000; - const step = getProperStep(start, end); - const { code, data } = (await service.execPromQLByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - })) as any; - let cpuStat = []; - if (code === 0) { - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - cpuStat = (await this.asyncGetSumDataByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - data: data.result, - })) as any; - } else { - cpuStat = data.result; - } - } - this.update({ - cpuStat, - }); +export function MachineModelWrapper(service,) { + return createModel({ + state: { + cpuStat: [] as IStatRangeItem[], + diskStat: [] as IStatRangeItem[], + memoryStat: [] as IStatRangeItem[], + networkOutStat: [] as IStatRangeItem[], + networkInStat: [] as IStatRangeItem[], + networkStat: [] as IStatRangeItem[], + loadStat: [] as IStatRangeItem[], + memorySizeStat: [] as IStatSingleItem[], + diskSizeStat: [] as IStatSingleItem[], + instanceList: [], + metricsFilterValues: InitMachineMetricsFilterValues, }, - - async asyncGetMemoryStatByRange(payload: { - start: number; - end: number; - metric: string; - }) { - const { start, end, metric } = payload; - const _start = start / 1000; - const _end = end / 1000; - const step = getProperStep(start, end); - const { code, data } = (await service.execPromQLByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - })) as any; - let memoryStat = []; - - if (code === 0) { - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - memoryStat = (await this.asyncGetSumDataByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - data: data.result, - })) as any; - } else { - memoryStat = data.result; + reducers: { + update: (state: IState, payload: any) => ({ + ...state, + ...payload, + }), + updateInstanceList: (state: IState, payload: any) => { + const instanceList = unique(state.instanceList.concat(payload)); + return { ...state, instanceList }; + }, + updateMetricsFilterValues: (state: IState, payload: any) => { + const metricsFilterValues = { + ...state.metricsFilterValues, + ...payload.metricsFilterValues + } + debugger; + return { + ...state, + metricsFilterValues } } - - this.update({ - memoryStat, - }); }, - - async asyncGetMemorySizeStat() { - const { code, data } = (await service.execPromQL({ - query: PROMQL.memory_size, - })) as any; - - let memorySizeStat = []; - - if (code === 0) { - memorySizeStat = data.result; - } - - this.update({ - memorySizeStat, - }); + effects: () => ({ + async asyncGetMetricsData(payload: { + start: number; + end: number; + metric: string; + clusterID?: string; + }) { + const { start, end, clusterID, metric } = payload; + const _start = start / 1000; + const _end = end / 1000; + const step = getProperStep(start, end); + const { code, data } = (await service.execPromQLByRange({ + clusterID, + query: PROMQL(clusterID)[metric], + start: _start, + end: _end, + step, + })) as any; + let result:any = []; + if (code === 0) { + if (NEED_ADD_SUM_QUERYS.includes(metric)) { + result = (await this.asyncGetSumDataByRange({ + clusterID, + query: PROMQL(clusterID)[metric], + start: _start, + end: _end, + step, + data: data.result, + })) as any; + } else { + result = data.result; + } + } + const instanceList = result.map(item => item.metric.instance).filter(instance => instance !== 'total'); + this.updateInstanceList(instanceList); + return result; }, - - async asyncGetDiskStatByRange(payload: { - start: number; - end: number; - metric: string; - }) { - const { start, end, metric } = payload; - const _start = start / 1000; - const _end = end / 1000; - const step = getProperStep(start, end); - const { code, data } = (await service.execPromQLByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - })) as any; - let diskStat = [] as any; - if (code === 0) { - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - diskStat = (await this.asyncGetSumDataByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - data: data.result, - })) as any; - } else { - diskStat = data.result; + async asyncGetCPUStatByRange(payload: { + start: number; + end: number; + metric: string; + clusterID?: string; + }) { + let cpuStat = await this.asyncGetMetricsData({ + ...payload, + nameObj: getMetricsUniqName(MetricScene.CPU) + }); + this.update({ + cpuStat, + }); + return cpuStat; + }, + + async asyncGetMemoryStatByRange(payload: { + start: number; + end: number; + metric: string; + clusterID?: string; + }) { + let memoryStat = await this.asyncGetMetricsData({ + ...payload, + nameObj: getMetricsUniqName(MetricScene.MEMORY) + }); + this.update({ + memoryStat, + }); + return memoryStat; + }, + + async asyncGetMemorySizeStat(clusterID?: string) { + const { code, data } = (await service.execPromQL({ + clusterID, + query: PROMQL(clusterID).memory_size, + })) as any; + let memorySizeStat = []; + if (code === 0) { + memorySizeStat = data.result; } - diskStat = diskStat.map((stat: any) => { - if (stat.metric.device) { - return { - values: stat.values, - metric: { - ...stat.metric, - instance: `${stat.metric.instance} (${stat.metric.device})`, - }, - }; - } - return stat; + this.update({ + memorySizeStat, }); - } - - this.update({ - diskStat, - }); - }, - - async asyncGetDiskSizeStat() { - const { code, data } = (await service.execPromQL({ - query: PROMQL.disk_size, - })) as any; - let diskSizeStat = []; - if (code === 0) { - diskSizeStat = data.result; - } - this.update({ - diskSizeStat, - }); - }, - - async asyncGetLoadByRange(payload: { - start: number; - end: number; - metric: string; - }) { - const { start, end, metric } = payload; - const _start = start / 1000; - const _end = end / 1000; - const step = getProperStep(start, end); - const { code, data } = (await service.execPromQLByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - })) as any; - - let loadStat = []; - if (code === 0) { - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - loadStat = (await this.asyncGetSumDataByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - data: data.result, - })) as any; - } else { - loadStat = data.result; + return memorySizeStat; + }, + + async asyncGetDiskStatByRange(payload: { + start: number; + end: number; + metric: string; + clusterID: string; + }) { + let diskStat = await this.asyncGetMetricsData({ + ...payload, + nameObj: getMetricsUniqName(MetricScene.DISK) + }); + this.update({ + diskStat, + }); + return diskStat; + }, + + async asyncGetDiskSizeStat(clusterID?: string) { + const { code, data } = (await service.execPromQL({ + clusterID, + query: PROMQL(clusterID).disk_size, + })) as any; + let diskSizeStat = []; + if (code === 0) { + diskSizeStat = data.result; } - } - this.update({ - loadStat, - }); - }, - - async asyncGetNetworkStatByRange(payload: { - start: number; - end: number; - metric: string; - inOrOut?: string; - }) { - const { start, end, metric, inOrOut } = payload; - const _start = start / 1000; - const _end = end / 1000; - const step = getProperStep(start, end); - const { code, data } = (await service.execPromQLByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - })) as any; - - let networkStat = []; - - if (code === 0) { - if (NEED_ADD_SUM_QUERYS.includes(metric)) { - networkStat = (await this.asyncGetSumDataByRange({ - query: PROMQL[metric], - start: _start, - end: _end, - step, - data: data.result, - })) as any; - } else { - networkStat = data.result; + this.update({ + diskSizeStat, + }); + return diskSizeStat; + }, + + async asyncGetLoadByRange(payload: { + start: number; + end: number; + metric: string; + clusterID: string; + }) { + let loadStat = await this.asyncGetMetricsData({ + ...payload, + nameObj: getMetricsUniqName(MetricScene.LOAD) + }); + this.update({ + loadStat, + }); + return loadStat; + }, + + async asyncGetNetworkStatByRange(payload: { + start: number; + end: number; + metric: string; + inOrOut?: string; + clusterID?: string; + }) { + const { start, end, metric, clusterID, inOrOut } = payload; + let networkStat = await this.asyncGetMetricsData({ start, end, metric, clusterID, nameObj: getMetricsUniqName(MetricScene.NETWORK) }); + switch (inOrOut) { + case 'in': + this.update({ + networkInStat: networkStat, + }); + break; + case 'out': + this.update({ + networkOutStat: networkStat, + }); + break; + default: + this.update({ + networkStat, + }); } - } + return networkStat; + }, + + async asyncGetSumDataByRange(payload: { + clusterID?: string; + query: string; + start: number; + end: number; + step: number; + data: any[]; + }) { + const { query, start, end, step, data, clusterID } = payload; + const { code, data: dataStat } = (await service.execPromQLByRange({ + clusterID, + query: `sum(${query})`, + start, + end, + step, + })) as any; + if (code === 0 && dataStat.result.length !== 0) { + const sumData = { + metric: { + instance: 'total', + job: 'total', + }, + } as any; + sumData.values = dataStat.result[0].values; + return data.concat(sumData); + } + return data; + }, - switch (inOrOut) { - case 'in': - this.update({ - networkInStat: networkStat, - }); - break; - case 'out': - this.update({ - networkOutStat: networkStat, - }); - break; - default: - this.update({ - networkStat, - }); + updateMetricsFiltervalues(values: MetricsPanelValue) { + this.updateMetricsFilterValues({ + metricsFilterValues: values, + }); } - }, + }), + }); +} - async asyncGetSumDataByRange(payload: { - query: string; - start: number; - end: number; - step: number; - data: any[]; - }) { - const { query, start, end, step, data } = payload; - const { code, data: dataStat } = (await service.execPromQLByRange({ - query: `sum(${query})`, - start, - end, - step, - })) as any; - if (code === 0 && dataStat.result.length !== 0) { - const sumData = { - metric: { - instance: 'total', - job: 'total', - }, - } as any; - sumData.values = dataStat.result[0].values; - return data.concat(sumData); - } - return data; - }, - }), -}); +export const machine = MachineModelWrapper(serviceApi); \ No newline at end of file diff --git a/src/store/models/metric.ts b/src/store/models/metric.ts index 5e8ffff9..823e7c42 100644 --- a/src/store/models/metric.ts +++ b/src/store/models/metric.ts @@ -3,72 +3,102 @@ import _ from 'lodash'; import { compare } from 'compare-versions'; import service from '@/config/service'; import { filterServiceMetrics } from '@/utils/metric'; +import { getClusterPrefix } from '@/utils/promQL'; interface IState { graphd: any[]; metad: any; storaged: any[]; + spaces: string; } -export const serviceMetric = createModel({ - state: { - graphd: [], - metad: [], - storaged: [], - }, - reducers: { - update: (state: IState, payload: any) => ({ - ...state, - ...payload, - }), - }, - effects: () => ({ - async asyncGetServiceMetric(payload: { - componentType: string; - version: string; - }) { - const { componentType, version } = payload; - let metricList = []; - let spaceMetricList = []; - switch (true) { - case compare(version, 'v3.0.0', '<'): { - const { code, data } = (await service.getMetrics({ - 'match[]': `{componentType="${componentType}",__name__!~"ALERTS.*",__name__!~".*count"}`, - })) as any; - if (code === 0) { - metricList = data; - } - break; - } - case compare(version, 'v3.0.0', '>='): - { - const { code, data } = (await service.getMetrics({ - 'match[]': `{componentType="${componentType}",space!="",__name__!~"ALERTS.*",__name__!~".*count"}`, +export function MetricModelWrapper(serviceApi) { + return createModel({ + state: { + graphd: [], + metad: [], + storaged: [], + spaces: [], + }, + reducers: { + update: (state: IState, payload: any) => ({ + ...state, + ...payload, + }), + }, + effects: () => ({ + async asyncGetServiceMetric(payload: { + clusterID?: string; + componentType: string; + version: string; + }) { + const { componentType, version, clusterID } = payload; + let metricList = []; + let spaceMetricList = []; + const clusterSuffix1 = clusterID ? `,${getClusterPrefix()}='${clusterID}'` : ''; + switch (true) { + case compare(version, 'v3.0.0', '<'): { + const { code, data } = (await serviceApi.getMetrics({ + clusterID, + 'match[]': `{componentType="${componentType}",__name__!~"ALERTS.*",__name__!~".*count"${clusterSuffix1}}`, })) as any; if (code === 0) { - spaceMetricList = data; - const { code: _code, data: metricData } = - (await service.getMetrics({ - 'match[]': `{componentType="${componentType}",space="",__name__!~"ALERTS.*",__name__!~".*count"}`, - })) as any; - if (_code === 0) { - metricList = metricData; - } + metricList = data; } + break; } - break; - default: - break; - } - const metrics = filterServiceMetrics({ - metricList, - componentType, - spaceMetricList, - version, - }); - this.update({ - [componentType]: metrics, - }); - }, - }), -}); + case compare(version, 'v3.0.0', '>='): + { + const { code, data } = (await serviceApi.getMetrics({ + clusterID, + 'match[]': `{componentType="${componentType}",space!="",__name__!~"ALERTS.*",__name__!~".*count"${clusterSuffix1}}`, + })) as any; + if (code === 0) { + spaceMetricList = data; + const { code: _code, data: metricData } = + (await serviceApi.getMetrics({ + clusterID, + 'match[]': `{componentType="${componentType}",space="",__name__!~"ALERTS.*",__name__!~".*count"${clusterSuffix1}}`, + })) as any; + if (_code === 0) { + metricList = metricData; + } + } + } + break; + default: + break; + } + const metrics = filterServiceMetrics({ + metricList, + componentType, + spaceMetricList, + version, + }); + this.update({ + [componentType]: metrics, + }); + }, + async asyncGetSpaces({ clusterID, start, end }) { + start = start / 1000; + end = end / 1000; + const { data: res } = (await service.getSpaces({ + 'match[]': clusterID ? `{${getClusterPrefix()}='${clusterID}'}` : undefined, + start, + end + })) as any; + if (Array.isArray(res)) { + this.update({ + spaces: res, + }); + } else if (res.status === 'success') { + this.update({ + spaces: res.data, + }); + } + }, + }), + }); +} + +export const serviceMetric = MetricModelWrapper(service); diff --git a/src/store/models/nebula.ts b/src/store/models/nebula.ts index c1266ee5..aa4d823f 100644 --- a/src/store/models/nebula.ts +++ b/src/store/models/nebula.ts @@ -2,6 +2,7 @@ import { createModel } from '@rematch/core'; import _ from 'lodash'; import cookies from 'js-cookie'; import service from '@/config/service'; +import { getConfigData } from "@/utils/dashboard"; interface IState { configs: any[]; @@ -32,16 +33,12 @@ export const nebula = createModel({ }), }, effects: (dispatch: any) => ({ - async asyncGetServiceConfigs(module?: string) { - const { code, data } = (await service.execNGQL({ - gql: - module && module !== 'all' - ? `SHOW CONFIGS ${module} ` - : 'SHOW CONFIGS', - })) as any; - if (code === 0) { + async asyncGetServiceConfigs(module) { + const data = module === 'graph'?await service.getGraphConfig(): await service.getStorageConfig() as any; + + if (data) { this.update({ - configs: data.tables ? data.tables : [], + configs: getConfigData(data), }); } }, @@ -123,7 +120,7 @@ export const nebula = createModel({ const res = await dispatch.nebula.asyncGetHostsInfo(type); return res.map(item => ({ name: `${item.Host}:${item.Port}`, - version: item['Git Info Sha'], + version: item['Version'], })); }, }), diff --git a/src/store/models/service.ts b/src/store/models/service.ts index a9d7edaa..911a7812 100644 --- a/src/store/models/service.ts +++ b/src/store/models/service.ts @@ -1,9 +1,12 @@ import { createModel } from '@rematch/core'; import _ from 'lodash'; import serviceApi from '@/config/service'; -import { IServicePanelConfig } from '@/utils/interface'; +import { IServicePanelConfig, ServiceMetricsPanelValue } from '@/utils/interface'; import { DEFAULT_SERVICE_PANEL_CONFIG } from '@/utils/service'; import { getProperStep } from '@/utils/dashboard'; +import { unique } from '@/utils'; +import { getClusterPrefix } from '@/utils/promQL'; +import { InitMetricsFilterValues } from '@/utils/metric'; interface IState { panelConfig: { @@ -11,104 +14,162 @@ interface IState { storage: IServicePanelConfig[]; meta: IServicePanelConfig[]; }; + instanceList: string[]; + metricsFilterValues: ServiceMetricsPanelValue; } -export const service = createModel({ - state: { - panelConfig: localStorage.getItem('panelConfig') - ? JSON.parse(localStorage.getItem('panelConfig')!) - : DEFAULT_SERVICE_PANEL_CONFIG, - }, - reducers: { - update: (state: IState, payload: any) => ({ - ...state, - ...payload, - }), - }, - effects: () => ({ - async asyncGetMetricsSumData(payload: { - query: string; - start: number; - end: number; - }) { - const { start, end, query } = payload; - const step = getProperStep(start, end); - const _start = start / 1000; - const _end = end / 1000; - const { code, data } = (await serviceApi.execPromQLByRange({ - query: `sum(${query})`, - start: _start, - end: _end, - step, - })) as any; - - if (code === 0 && data.result.length !== 0) { - const sumData = { - metric: { - instanceName: 'total', - instance: 'total', - }, - } as any; - sumData.values = data.result[0].values; - return sumData; - } - return []; +export function SereviceModelWrapper(serviceApi) { + return createModel({ + state: { + panelConfig: localStorage.getItem('panelConfig') + ? JSON.parse(localStorage.getItem('panelConfig')!) + : DEFAULT_SERVICE_PANEL_CONFIG, + instanceList: [], + metricsFilterValues: InitMetricsFilterValues }, - - async asyncGetMetricsData(payload: { - query: string; - start: number; - end: number; - }) { - const { start, end, query } = payload; - const step = getProperStep(start, end); - const _start = start / 1000; - const _end = end / 1000; - const { code, data } = (await serviceApi.execPromQLByRange({ - query, - start: _start, - end: _end, - step, - })) as any; - let stat = [] as any; - if (code === 0 && data.result.length !== 0) { - stat = data.result; + reducers: { + update: (state: IState, payload: any) => ({ + ...state, + ...payload, + }), + updateInstanceList: (state: IState, payload: any) => { + const instanceList = unique(state.instanceList.concat(payload)); + return { ...state, instanceList }; + }, + updateMetricsFilterValues: (state: IState, payload: any) => { + const metricsFilterValues = { + ...state.metricsFilterValues, + ...payload.metricsFilterValues + } + return { + ...state, + metricsFilterValues + } } - return stat; }, + effects: () => ({ + async asyncGetMetricsSumData(payload: { + query: string; + start: number; + end: number; + space?: string; + clusterID?: string; + }) { + const { start, end, space, query: _query, clusterID } = payload; + const step = getProperStep(start, end); + const _start = start / 1000; + const _end = end / 1000; + let query = `sum(${_query}{${getClusterPrefix()}="${clusterID}"})`; + query = `${_query}{${getClusterPrefix()}="${clusterID}", space="${space || ''}"}`; + const { code, data } = (await serviceApi.execPromQLByRange({ + clusterID, + query, + start: _start, + end: _end, + step, + })) as any; + + if (code === 0 && data.result.length !== 0) { + const sumData = { + metric: { + instanceName: 'total', + instance: 'total', + }, + } as any; + sumData.values = data.result[0].values; + return sumData; + } + return []; + }, - async asyncGetStatus(payload: { - interval: number; - end: number; - query: string; - }) { - const { interval, end, query } = payload; - const start = end - interval; - const step = getProperStep(start, end); - const _start = start / 1000; - const _end = end / 1000; - const { code, data } = (await serviceApi.execPromQLByRange({ - query, - start: _start, - end: _end, - step, - })) as any; - let normal = 0; - let abnormal = 0; - if (code === 0) { - data.result.forEach(item => { - const value = item.values.pop(); - if (value[1] === '1') { - normal++; + async asyncGetMetricsData(payload: { + query: string; + space?: string; + start: number; + end: number; + clusterID?: string; + noSuffix?: boolean; + }) { + const { + start, + space, + end, + query: _query, + clusterID, + noSuffix = false, + } = payload; + const step = getProperStep(start, end); + const _start = start / 1000; + const _end = end / 1000; + let query = _query; + if (!noSuffix) { + if (clusterID) { + query = `${_query}{${getClusterPrefix()}="${clusterID}", space="${space || ''}"}`; } else { - abnormal++; + query = `${_query}{space="${space || ''}"}`; } + } + const { code, data } = (await serviceApi.execPromQLByRange({ + clusterID, + query, + start: _start, + end: _end, + step, + })) as any; + let stat = [] as any; + if (code === 0 && data.result.length !== 0) { + stat = data.result; + } + const list = stat.map(item => { + const instanceName = item.metric.instanceName || item.metric.instance; + return instanceName.slice(0, instanceName.indexOf('-')) + }); + this.updateInstanceList(list) + return stat; + }, + + async asyncGetStatus(payload: { + interval: number; + end: number; + query: string; + clusterID?: string; + }) { + const { interval, end, query, clusterID } = payload; + const start = end - interval; + const step = getProperStep(start, end); + const _start = start / 1000; + const _end = end / 1000; + const { code, data } = (await serviceApi.execPromQLByRange({ + clusterID, + query: clusterID ? `${query}{${getClusterPrefix()}="${clusterID}"}` : query, + start: _start, + end: _end, + step, + })) as any; + let normal = 0; + let abnormal = 0; + if (code === 0) { + data.result.forEach(item => { + const value = item.values.pop(); + if (value[1] === '1') { + normal++; + } else { + abnormal++; + } + }); + } + return { + normal, + abnormal, + }; + }, + updateMetricsFiltervalues(values: ServiceMetricsPanelValue) { + this.updateMetricsFilterValues({ + metricsFilterValues: values, }); } - return { - normal, - abnormal, - }; - }, - }), -}); + }), + }); +} + +export const service = SereviceModelWrapper(serviceApi); \ No newline at end of file diff --git a/src/typings.d.ts b/src/typings.d.ts index e32ed9e2..a17ae901 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -2,3 +2,5 @@ declare module '*.module.less' { const content: { [className: string]: string }; export default content; } + +declare var VERSION_TYPE: any; \ No newline at end of file diff --git a/src/utils/HttpServiceManager.ts b/src/utils/HttpServiceManager.ts new file mode 100644 index 00000000..895c3c85 --- /dev/null +++ b/src/utils/HttpServiceManager.ts @@ -0,0 +1,48 @@ + +import axios, { AxiosInstance } from 'axios'; +import { trackEvent } from './stat'; + +export class HttpSeriveManager { + + public static axiosInstance: AxiosInstance; + + static get _axiosInstance(): AxiosInstance { + if (!HttpSeriveManager.axiosInstance) { + HttpSeriveManager.axiosInstance = axios.create({ + timeout: 10000 + }); + } + return HttpSeriveManager.axiosInstance + } + + private trackService = (res, config) => { + const { category, action } = config; + trackEvent(category, action, res.code === 0 ? 'ajax_success' : 'ajax_failed'); + }; + + public sendRequest = async (type: string, api: string, params?, config?) => { + const { trackEventConfig, ...otherConfig } = config; + let res; + switch (type) { + case 'get': + res = (await HttpSeriveManager.axiosInstance.get(api, { params, ...otherConfig })) as any; + break; + case 'post': + res = (await HttpSeriveManager.axiosInstance.post(api, params, otherConfig)) as any; + break; + case 'put': + res = (await HttpSeriveManager.axiosInstance.put(api, params, otherConfig)) as any; + break; + case 'delete': + res = (await HttpSeriveManager.axiosInstance.delete(api, params)) as any; + break; + default: + break; + } + + if (res && trackEventConfig) { + this.trackService(res, trackEventConfig); + } + return res; + }; +} \ No newline at end of file diff --git a/src/utils/chart/chart.ts b/src/utils/chart/chart.ts index 7e8177d4..51f34aa9 100644 --- a/src/utils/chart/chart.ts +++ b/src/utils/chart/chart.ts @@ -33,7 +33,8 @@ export const configDetailChart = ( sizes?: any; valueType?: VALUE_TYPE; isCard?: boolean; - }, + maxNum?: number; + } ): Chart => { chartInstance .axis('time', { @@ -93,8 +94,8 @@ export const configDetailChart = ( chartInstance.scale({ value: { min: 0, - max: 100, - tickInterval: 25, + max: options.maxNum || 100, + tickInterval: options.maxNum ? (options.maxNum % 10 + 10 ) / 5 : 25, }, }); break; diff --git a/src/utils/dashboard.ts b/src/utils/dashboard.ts index 78e5e985..3bb9ce53 100644 --- a/src/utils/dashboard.ts +++ b/src/utils/dashboard.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs'; import _ from 'lodash'; -import { ILineChartMetric, IStatRangeItem } from '@/utils/interface'; +import { ILineChartMetric, IStatRangeItem, MetricScene } from '@/utils/interface'; import { VALUE_TYPE } from '@/utils/promQL'; export const DETAIL_DEFAULT_RANGE = 60 * 60 * 24 * 1000; @@ -8,6 +8,7 @@ export const CARD_RANGE = 60 * 60 * 24 * 1000; export const CARD_POLLING_INTERVAL = 10000 * 1000; export const MAX_STEP_ALLOW = 11000; export const TIME_INTERVAL_OPTIONS = [5, 60, 600, 3600]; +export const AGGREGATION_OPTIONS = ['sum', 'rate', 'avg', 'p75', 'p95', 'p99', 'p999']; export const THRESHOLDS = { low: 60, @@ -21,33 +22,21 @@ export const CARD_HIGH_COLORS = 'rgba(230,113,113,1)'; export const getProperStep = (start: number, end: number) => { const hours = Math.round((end - start) / (3600 * 1000)); if (hours <= 1) { - return 7; + return 30; } if (hours <= 6) { // 6 hour - return 86; + return 30; } if (hours <= 12) { // 12hour - return 172; + return 40; } if (hours <= 24) { // 1 day - return 345; + return 60; } - if (hours <= 72) { - // 3 days - return 691; - } - if (hours <= 168) { - // 1 week - return 2419; - } - if (hours <= 336) { - // 2 week - return 4838; - } - return Math.round((end - start) / MAX_STEP_ALLOW); + return Math.ceil(hours / 24) * 60; }; export const renderUnit = type => { @@ -66,11 +55,6 @@ export const renderUnit = type => { } }; export const VERSION_REGEX = /\d+\.{1}\d+\.{1}\d+/; -// 0.0.1 means unknow version -export function getVersion(v: string) { - const match = v?.match(VERSION_REGEX); - return match ? match[0] : v; -} export const getBaseLineByUnit = (config: { baseLine: number; @@ -148,18 +132,28 @@ export const getProperByteDesc = (bytes: any, conversion: number) => { export const getDataByType = (payload: { data: IStatRangeItem[]; - type?: string; - name: string; + type?: string | string[]; + nameObj: { + name: string; + showName: (name: string) => string; + }; aliasConfig?: any; }) => { - const { name, type, data, aliasConfig } = payload; + const { nameObj, type, data, aliasConfig } = payload; + const { name, showName } = nameObj; const res = [] as ILineChartMetric[]; data.forEach(instance => { instance.values.forEach(([timstamps, value]) => { const _name = instance.metric[name]; - if ((type === 'all' && _name !== 'total') || _name === type) { + let shouldPush = false; + if (typeof type === 'string') { + shouldPush = (type === 'all' && _name !== 'total') || _name === type; + } else if (Array.isArray(type)) { + shouldPush = (type.includes('all') && _name !== 'total') || !!type.find(t => _name.includes(t)) + } + if (shouldPush) { res.push({ - type: aliasConfig && aliasConfig[_name] ? aliasConfig[_name] : _name, + type: showName(aliasConfig && aliasConfig[_name] ? aliasConfig[_name] : _name), value: Number(value), time: timstamps, }); @@ -169,6 +163,40 @@ export const getDataByType = (payload: { return res; }; +export let getDiskData = (payload: { + data: IStatRangeItem[]; + type?: string | string[]; + aliasConfig?: any; + nameObj: { + name: string; + showName: (name: string) => string; + }; +}) => { + const { type, data, nameObj } = payload; + const { name, showName } = nameObj; + const res = [] as ILineChartMetric[]; + data.forEach(instance => { + instance.values.forEach(([timstamps, value]) => { + const device = instance.metric['device']; + const _name = instance.metric[name]; + let shouldPush = false; + if (typeof type === 'string') { + shouldPush = (type === 'all' && _name !== 'total') || _name === type; + } else if (Array.isArray(type)) { + shouldPush = (type.includes('all') && _name !== 'total') || !!type.find(t => _name.includes(t)) + } + if (shouldPush) { + res.push({ + type: `${showName(_name)}-${device}`, + value: Number(value), + time: timstamps, + }); + } + }); + }); + return res; +} + export const getProperTickInterval = period => { switch (period) { // past one hour @@ -181,41 +209,51 @@ export const getProperTickInterval = period => { } }; +export enum TIME_OPTION_TYPE { + HOUR1 = '1hour', + HOUR6 = '6hour', + HOUR12 = '12hour', + DAY1 = '1day', + DAY3 = '3day', + DAY7 = '7day', + DAY14 = '14day', +} + export const TIMEOPTIONS = [ { - name: '1hour', + name: TIME_OPTION_TYPE.HOUR1, value: 60 * 60 * 1000, }, { - name: '6hour', + name: TIME_OPTION_TYPE.HOUR6, value: 60 * 60 * 6 * 1000, }, { - name: '12hour', + name: TIME_OPTION_TYPE.HOUR12, value: 60 * 60 * 12 * 1000, }, { - name: '1day', + name: TIME_OPTION_TYPE.DAY1, value: 60 * 60 * 24 * 1000, }, { - name: '3day', + name: TIME_OPTION_TYPE.DAY3, value: 60 * 60 * 24 * 3 * 1000, }, { - name: '7day', + name: TIME_OPTION_TYPE.DAY7, value: 60 * 60 * 24 * 7 * 1000, }, { - name: '14day', + name: TIME_OPTION_TYPE.DAY14, value: 60 * 60 * 24 * 14 * 1000, }, ]; -export const NEED_ADD_SUM_QUERYS = [ +export let NEED_ADD_SUM_QUERYS = [ // For Instanc 'memory_used', - 'memory_actual_used', + // 'memory_actual_used', 'memory_free', 'disk_used', 'disk_free', @@ -232,6 +270,17 @@ export const NEED_ADD_SUM_QUERYS = [ 'num_slow_queries', ]; +export const calcTimeRange = (timeRange: TIME_OPTION_TYPE | [number, number]): [number, number] => { + const end = Date.now(); + if (typeof timeRange === 'string') { + const value = TIMEOPTIONS.find(t => t.name === timeRange)?.value!; + return [end - value, end]; + } else if (typeof timeRange === 'object' && timeRange.length === 2) { + return timeRange; + } + throw new Error('timeRange is not valid'); +} + export enum MACHINE_TYPE { cpu = 'cpu', memory = 'memory', @@ -242,7 +291,7 @@ export enum MACHINE_TYPE { network = 'network', } -export const VERSIONS = ['v2.0.1', 'v2.5.1', 'v2.6.1', 'v3.0.0', 'v3.1.0']; +export const VERSIONS = ['v2.0.1', 'v2.5.1', 'v2.6.1', 'v3.0.0', 'v3.1.0', 'v3.2.0']; export const getDefaultTimeRange = (interval?: number) => { const end = Date.now(); @@ -292,3 +341,81 @@ export const getMaxNumAndLength = (payload: { } return { maxNum, maxNumLen }; }; + +export function compareVersion(v1, v2) { + // if not version format, return -1; + if (!v1.match(VERSION_REGEX) || !v2.match(VERSION_REGEX)) { + return -1; + } + v1 = v1.split('.'); + v2 = v2.split('.'); + const len = Math.max(v1.length, v2.length); + + while (v1.length < len) { + v1.push('0'); + } + while (v2.length < len) { + v2.push('0'); + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i], 10); + const num2 = parseInt(v2[i], 10); + + if (num1 > num2) { + return 1; + } + if (num1 < num2) { + return -1; + } + } + + return 0; +} + +export const UNKONW_VERSION = 'unknow'; + +// 0.0.1 means unknow version +export function getVersion(v: string) { + const match = v?.match(VERSION_REGEX); + return match ? match[0] : v; +} + +export let getMetricsUniqName = (scene: MetricScene) => { + if (scene === MetricScene.SERVICE) { + return { + name: 'instanceName', + showName: (name) => name + } + } + return { + name: 'instance', + showName: (name) => name + } +} + +export const getConfigData=(data)=>{ + let list = [] as any; + data.split('\n')?.forEach(item =>{ + const [name, value] =item.split('=') + if(name){ + list.push({name, value}) + } + }) + return list; +} + +export let getMachineRouterPath = (path: string, id?): string => `/clusters/${id}${path}`; + +export const updateService = (service: { + getMetricsUniqName: typeof getMetricsUniqName, + getMachineRouterPath: typeof getMachineRouterPath, + getDiskData: typeof getDiskData, + NEED_ADD_SUM_QUERYS: typeof NEED_ADD_SUM_QUERYS, +}) => { + getMetricsUniqName = service.getMetricsUniqName; + getMachineRouterPath = service.getMachineRouterPath; + getDiskData = service.getDiskData; + NEED_ADD_SUM_QUERYS = service.NEED_ADD_SUM_QUERYS; +} + diff --git a/src/utils/http.ts b/src/utils/http.ts index a12d3b54..c2a522fb 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -1,48 +1,58 @@ import { message as AntdMessage } from 'antd'; -import axios from 'axios'; +// import axios from 'axios'; import intl from 'react-intl-universal'; - +import { HttpSeriveManager } from './HttpServiceManager'; import { trackEvent } from './stat'; -import { store } from '@/store'; -const service = axios.create(); +const service = HttpSeriveManager._axiosInstance; + +export const registResponseInterceptor = (interceptorFn, store) => { + interceptorFn(store); +}; + +export const interceptorFn = (store) => { + service.interceptors.response.use( + (response: any) => { + const { code, message } = response.data; -service.interceptors.response.use( - (response: any) => { - const { code, message } = response.data; - let _code; - if ('code' in response.data) { - _code = code; - } else { - // response from prometheus api - _code = response.data.status === 'success' ? 0 : -1; - response.data.code = _code; - } - // if connection refused, login again - if ( - code === -1 && - message && - (message.includes('connection refused') || - message.includes('an existing connection was forcibly closed')) - ) { - AntdMessage.warning(intl.get('configServer.connectError')); - store.dispatch({ - type: 'app/asyncLogout', - }); - } else if (code === -1 && message) { - AntdMessage.warning(message); - } - return response.data; - }, - (error: any) => { - AntdMessage.error( - `${intl.get('common.requestError')}: ${error.response.status} ${ - error.response.statusText - }`, - ); - return error.response; - }, -); + let _code; + //HACK: get graph,storage server data + if(response.config?.url.includes('api-graph')||response.config?.url.includes('api-storage')){ + return response.data; + } + if ('code' in response.data) { + _code = code; + } else { + // response from prometheus api + _code = response.data.status === 'success' ? 0 : -1; + response.data.code = _code; + } + // if connection refused, login again + if ( + code === -1 && + message && + (message.includes('connection refused') || + message.includes('an existing connection was forcibly closed')) + ) { + AntdMessage.warning(intl.get('configServer.connectError')); + store.dispatch({ + type: 'app/asyncLogout', + }); + } else if (code === -1 && message) { + AntdMessage.warning(message); + } + return response.data; + }, + (error: any) => { + AntdMessage.error( + `${intl.get('common.requestError')}: ${error.response.status} ${ + error.response.statusText + }`, + ); + return error.response; + }, + ); +} const sendRequest = async (type: string, api: string, params?, config?) => { const { trackEventConfig, ...otherConfig } = config; @@ -77,22 +87,22 @@ const trackService = (res, config) => { const get = (api: string) => - (params?: object, config = {}) => - sendRequest('get', api, params, config); + (params?: object, config = {}) => + sendRequest('get', api, params, config); const post = (api: string) => - (params?: object, config = {} as any) => - sendRequest('post', api, params, config); + (params?: object, config = {} as any) => + sendRequest('post', api, params, config); const put = (api: string) => - (params?: object, config = {}) => - sendRequest('put', api, params, config); + (params?: object, config = {}) => + sendRequest('put', api, params, config); const _delete = (api: string) => - (params?: object, config = {}) => - sendRequest('delete', api, params, config); + (params?: object, config = {}) => + sendRequest('delete', api, params, config); export { get, post, put, _delete }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 6162b763..93e8a0dd 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,30 @@ +import { DashboardType } from './interface'; + /** * this folder for utils */ export * from './url'; + +export const isCommunityVersion = () => { + return VERSION_TYPE?.type === DashboardType.COMMUNTY; +} + +export const isEnterpriseVersion = () => { + return VERSION_TYPE?.type === DashboardType.ENTERPRISE; +} + +export const isCloudVersion = () => { + return VERSION_TYPE?.type === DashboardType.CLOUD; +} + +export const isPlayGroundVersion = () => { + return VERSION_TYPE?.type === DashboardType.PLAYGROUND; +} + +export const shouldCheckCluster = () => { + return isEnterpriseVersion() || isCloudVersion() || isPlayGroundVersion(); +} + +export const unique = (arr) => { + return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]); +} \ No newline at end of file diff --git a/src/utils/interface.ts b/src/utils/interface.ts index 20baac5e..3f99ea73 100644 --- a/src/utils/interface.ts +++ b/src/utils/interface.ts @@ -1,7 +1,10 @@ +import { TIME_OPTION_TYPE } from "./dashboard"; + export interface IMetric { instance: string; instanceName: string; device?: string; + mountpoint?: string; } export interface IStatRangeItem { metric: IMetric; @@ -31,3 +34,52 @@ export interface IServicePanelConfig { metricType: string; baseLine: number | undefined; } + +export interface IMetricType { + key: string; + value: string; +} + +export interface IMetricOption { + metric: string; + isSpaceMetric: boolean; + metricType: IMetricType[]; + valueType: string; +} + +export interface MetricsPanelValue { + timeRange: TIME_OPTION_TYPE | [number, number]; + instanceList: string[]; + frequency: number; +} + +export interface ServiceMetricsPanelValue extends MetricsPanelValue { + space: string; + metricType: string; + period: string; +} + +export interface DiskMetricInfo { + size: number; + device: string; + used: number; + mountpoint: string; + name: string; +} + +export enum DashboardType { + COMMUNTY = 'community', + ENTERPRISE = 'enterprise', + CLOUD = 'cloud', + PLAYGROUND = 'playground' +} + +export enum MetricScene { + MACHINE, + SERVICE, + CPU, + NETWORK, + DISK, + MEMORY, + LOAD, +} \ No newline at end of file diff --git a/src/utils/metric.ts b/src/utils/metric.ts index 1d2ecaa1..7df946de 100644 --- a/src/utils/metric.ts +++ b/src/utils/metric.ts @@ -1,7 +1,9 @@ import _ from 'loadsh'; import { VALUE_TYPE } from '@/utils/promQL'; +import { INTERVAL_FREQUENCY_LIST, SERVICE_QUERY_PERIOD } from './service'; +import { AGGREGATION_OPTIONS, TIME_OPTION_TYPE } from './dashboard'; -export const METRICS_DESCRIPTION = { +export const METRICS_DESCRIPTION: any = { num_queries: 'num_queries description', num_slow_queries: 'num_slow_queries description', query_latency_us: 'query_latency_us description', @@ -131,3 +133,18 @@ export const filterServiceMetrics = (payload: { }); return metrics; }; + +export const InitMetricsFilterValues: any = { + frequency: INTERVAL_FREQUENCY_LIST[0].value, + instanceList: ['all'], + timeRange: TIME_OPTION_TYPE.DAY1, + space: "", + period: SERVICE_QUERY_PERIOD, + metricType: AGGREGATION_OPTIONS[0] +}; + +export const InitMachineMetricsFilterValues: any = { + frequency: INTERVAL_FREQUENCY_LIST[0].value, + instanceList: ['all'], + timeRange: TIME_OPTION_TYPE.DAY1, +} \ No newline at end of file diff --git a/src/utils/promQL.ts b/src/utils/promQL.ts index 4253ae8c..97c661b2 100644 --- a/src/utils/promQL.ts +++ b/src/utils/promQL.ts @@ -11,118 +11,119 @@ export const enum VALUE_TYPE { numberSecond = 'numberSecond', } -export const SUPPORT_METRICS = { - cpu: [ - { - metric: 'cpu_utilization', - valueType: VALUE_TYPE.percentage, - }, - { - metric: 'cpu_idle', - valueType: VALUE_TYPE.percentage, - }, - { - metric: 'cpu_wait', - valueType: VALUE_TYPE.percentage, - }, - { - metric: 'cpu_user', - valueType: VALUE_TYPE.percentage, - }, - { - metric: 'cpu_system', - valueType: VALUE_TYPE.percentage, - }, - ], - memory: [ - { - metric: 'memory_utilization', - valueType: VALUE_TYPE.percentage, - }, - { - metric: 'memory_used', - valueType: VALUE_TYPE.byte, - }, - { - metric: 'memory_actual_used', - valueType: VALUE_TYPE.byte, - }, - { - metric: 'memory_free', - valueType: VALUE_TYPE.byte, - }, - ], - load: [ - { - metric: 'load_1m', - valueType: VALUE_TYPE.number, - }, - { - metric: 'load_5m', - valueType: VALUE_TYPE.number, - }, - { - metric: 'load_15m', - valueType: VALUE_TYPE.number, - }, - ], - disk: [ - { - metric: 'disk_used', - valueType: VALUE_TYPE.byte, - }, - { - metric: 'disk_free', - valueType: VALUE_TYPE.byte, - }, - { - metric: 'disk_readbytes', - valueType: VALUE_TYPE.byteSecond, - }, - { - metric: 'disk_writebytes', - valueType: VALUE_TYPE.byteSecond, - }, - { - metric: 'disk_readiops', - valueType: VALUE_TYPE.numberSecond, - }, - { - metric: 'disk_writeiops', - valueType: VALUE_TYPE.numberSecond, - }, - { - metric: 'inode_utilization', - valueType: VALUE_TYPE.percentage, - }, - ], - network: [ - { - metric: 'network_in_rate', - valueType: VALUE_TYPE.byteSecond, - }, - { - metric: 'network_out_rate', - valueType: VALUE_TYPE.byteSecond, - }, - { - metric: 'network_in_errs', - valueType: VALUE_TYPE.numberSecond, - }, - { - metric: 'network_out_errs', - valueType: VALUE_TYPE.numberSecond, - }, - { - metric: 'network_in_packets', - valueType: VALUE_TYPE.numberSecond, - }, - { - metric: 'network_out_packets', - valueType: VALUE_TYPE.numberSecond, - }, - ], -}; +export let SUPPORT_METRICS = + { + cpu: [ + { + metric: 'cpu_utilization', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'cpu_idle', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'cpu_wait', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'cpu_user', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'cpu_system', + valueType: VALUE_TYPE.percentage, + }, + ], + memory: [ + { + metric: 'memory_utilization', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'memory_used', + valueType: VALUE_TYPE.byte, + }, + { + metric: 'memory_free', + valueType: VALUE_TYPE.byte, + }, + ], + load: [ + { + metric: 'load_1m', + valueType: VALUE_TYPE.number, + }, + { + metric: 'load_5m', + valueType: VALUE_TYPE.number, + }, + { + metric: 'load_15m', + valueType: VALUE_TYPE.number, + }, + ], + disk: [ + { + metric: 'disk_used_percentage', + valueType: VALUE_TYPE.percentage, + }, + { + metric: 'disk_used', + valueType: VALUE_TYPE.byte, + }, + { + metric: 'disk_free', + valueType: VALUE_TYPE.byte, + }, + { + metric: 'disk_readbytes', + valueType: VALUE_TYPE.byteSecond, + }, + { + metric: 'disk_writebytes', + valueType: VALUE_TYPE.byteSecond, + }, + { + metric: 'disk_readiops', + valueType: VALUE_TYPE.numberSecond, + }, + { + metric: 'disk_writeiops', + valueType: VALUE_TYPE.numberSecond, + }, + { + metric: 'inode_utilization', + valueType: VALUE_TYPE.percentage, + }, + ], + network: [ + { + metric: 'network_in_rate', + valueType: VALUE_TYPE.byteSecond, + }, + { + metric: 'network_out_rate', + valueType: VALUE_TYPE.byteSecond, + }, + { + metric: 'network_in_errs', + valueType: VALUE_TYPE.numberSecond, + }, + { + metric: 'network_out_errs', + valueType: VALUE_TYPE.numberSecond, + }, + { + metric: 'network_in_packets', + valueType: VALUE_TYPE.numberSecond, + }, + { + metric: 'network_out_packets', + valueType: VALUE_TYPE.numberSecond, + }, + ], + }; export const SERVICE_SUPPORT_METRICS = { graph: [ @@ -222,29 +223,6 @@ export const SERVICE_SUPPORT_METRICS = { }, ], storage: [ - // Hack:atomic operaion is not guarateed in nebula now,thus it's will always return 0 unlesss user config it - // { - // metric: 'add_edges_atomic_latency_us', - // valueType: VALUE_TYPE.number, - // metricType: [ - // { - // key: 'avg', - // value: 'nebula_storaged_add_edges_atomic_latency_us_avg_' - // }, - // { - // key: 'p75', - // value: 'nebula_storaged_add_edges_atomic_latency_us_p75_' - // }, - // { - // key: 'p95', - // value: 'nebula_storaged_add_edges_atomic_latency_us_p95_' - // }, - // { - // key: 'p99', - // value: 'nebula_storaged_add_edges_atomic_latency_us_p99_' - // }, - // ], - // }, { metric: 'add_edges_latency_us', valueType: VALUE_TYPE.number, @@ -418,86 +396,52 @@ export const SERVICE_SUPPORT_METRICS = { ], }; -export const MAC_OS = { - // cpu relative: - cpu_utilization: - '100 * (1 - sum by (instance)(increase(node_cpu_seconds_total{mode="idle"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_idle: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="idle"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_wait: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="iowait"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_user: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="user"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_system: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="system"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - - // memory relative - memory_util: - '(1 - avg_over_time(node_memory_free_bytes[1m]) / avg_over_time(node_memory_total_bytes[1m]) )* 100', - memory_size: 'node_memory_total_bytes', - disk_usage_rate: - '(1 - (node_filesystem_avail_bytes{mountpoint="/",fstype!="rootfs"} ) / node_filesystem_size_bytes{mountpoint="/",fstype!="rootfs"})* 100', - disk_size: 'node_filesystem_size_bytes{mountpoint="/",fstype!="rootfs"}', - network_flow_down: - 'sum by(instance)(rate(node_network_receive_bytes_total{device=~"en[0-9]*"}[1m]))', - network_flow_up: - 'sum by(instance)(rate(node_network_transmit_bytes_total{device=~"en[0-9]*"}[1m]))', - node_load5: 'node_load5', -}; +export const getClusterPrefix = () => { + return 'nebula_cluster'; +} -export const LINUX = { - // cpu relative: - cpu_utilization: - '100 * (1 - sum by (instance)(increase(node_cpu_seconds_total{mode="idle"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_idle: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="idle"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_wait: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="iowait"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_user: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="user"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', - cpu_system: - '100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="system"}[5m])) / sum by (instance)(increase(node_cpu_seconds_total[5m])))', +export let LINUX = (cluster?) : any => { + const clusterSuffix1 = cluster ? `,${getClusterPrefix()}='${cluster}'` : ''; + const clusterSuffix2 = cluster ? `{${getClusterPrefix()}='${cluster}'}` : ''; + const diskPararms = 'fstype=~"ext.*|xfs",mountpoint !~".*pod.*"'; + return { + // cpu relative: + cpu_utilization: `100 * (1 - sum by (instance)(increase(node_cpu_seconds_total{mode="idle"${clusterSuffix1}}[1m])) / sum by (instance)(increase(node_cpu_seconds_total${clusterSuffix2}[1m])))`, + cpu_idle: `100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="idle"${clusterSuffix1}}[1m])) / sum by (instance)(increase(node_cpu_seconds_total${clusterSuffix2}[1m])))`, + cpu_wait: `100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="iowait"${clusterSuffix1}}[1m])) / sum by (instance)(increase(node_cpu_seconds_total${clusterSuffix2}[1m])))`, + cpu_user: `100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="user"${clusterSuffix1}}[1m])) / sum by (instance)(increase(node_cpu_seconds_total${clusterSuffix2}[1m])))`, + cpu_system: `100 * (sum by (instance)(increase(node_cpu_seconds_total{mode="system"${clusterSuffix1}}[1m])) / sum by (instance)(increase(node_cpu_seconds_total${clusterSuffix2}[1m])))`, - // memory relative: - memory_utilization: `(1 - (node_memory_MemFree_bytes+ node_memory_Buffers_bytes+ node_memory_Cached_bytes) / node_memory_MemTotal_bytes )* 100`, - memory_used: `node_memory_MemTotal_bytes - node_memory_MemFree_bytes- node_memory_Buffers_bytes - node_memory_Cached_bytes`, - memory_actual_used: `node_memory_MemTotal_bytes - node_memory_MemFree_bytes - node_memory_Buffers_bytes - node_memory_Cached_bytes`, - memory_free: `node_memory_MemFree_bytes`, - memory_size: `node_memory_MemTotal_bytes`, + // memory relative: + memory_utilization: `(1 - (node_memory_MemFree_bytes${clusterSuffix2}+ node_memory_Buffers_bytes${clusterSuffix2}+ node_memory_Cached_bytes${clusterSuffix2}) / node_memory_MemTotal_bytes${clusterSuffix2} )* 100`, + memory_used: `node_memory_MemTotal_bytes${clusterSuffix2} - node_memory_MemFree_bytes${clusterSuffix2}- node_memory_Buffers_bytes${clusterSuffix2} - node_memory_Cached_bytes${clusterSuffix2}`, + // memory_actual_used: `node_memory_MemTotal_bytes${clusterSuffix2} - node_memory_MemFree_bytes${clusterSuffix2} - node_memory_Buffers_bytes${clusterSuffix2} - node_memory_Cached_bytes${clusterSuffix2}`, + memory_free: `node_memory_MemFree_bytes${clusterSuffix2}`, + memory_size: `node_memory_MemTotal_bytes${clusterSuffix2}`, - // node load relative: - load_1m: 'node_load1', - load_5m: 'node_load5', - load_15m: 'node_load15', + // node load relative: + load_1m: `node_load1${clusterSuffix2}`, + load_5m: `node_load5${clusterSuffix2}`, + load_15m: `node_load15${clusterSuffix2}`, - // disk relative: - disk_used: - 'node_filesystem_size_bytes{mountpoint="/",fstype!="rootfs"} - node_filesystem_avail_bytes{mountpoint="/",fstype!="rootfs"}', - disk_free: 'node_filesystem_avail_bytes{mountpoint="/",fstype!="rootfs"}', - disk_readbytes: - 'irate(node_disk_read_bytes_total{device=~"(sd|nvme|hd)[a-z0-9]*"}[1m])', - disk_writebytes: - 'irate(node_disk_written_bytes_total{device=~"(sd|nvme|hd)[a-z0-9]*"}[1m])', - disk_readiops: - 'irate(node_disk_reads_completed_total{device=~"(sd|nvme|hd)[a-z0-9]*"}[1m])', - disk_writeiops: - 'irate(node_disk_writes_completed_total{device=~"(sd|nvme|hd)[a-z0-9]*"}[1m])', - inode_utilization: - '(1- (node_filesystem_files_free{mountpoint="/",fstype!="rootfs"}) / (node_filesystem_files{mountpoint="/",fstype!="rootfs"})) * 100', + // disk relative: + disk_used: `node_filesystem_size_bytes{${diskPararms}${clusterSuffix1}} - node_filesystem_free_bytes{${diskPararms}${clusterSuffix1}}`, + disk_free: `node_filesystem_avail_bytes{${diskPararms}${clusterSuffix1}}`, + disk_readbytes: `irate(node_disk_read_bytes_total{device=~"(sd|nvme|hd)[a-z0-9]*"${clusterSuffix1}}[1m])`, + disk_writebytes: `irate(node_disk_written_bytes_total{device=~"(sd|nvme|hd)[a-z0-9]*"${clusterSuffix1}}[1m])`, + disk_readiops: `irate(node_disk_reads_completed_total{device=~"(sd|nvme|hd)[a-z0-9]*"${clusterSuffix1}}[1m])`, + disk_writeiops: `irate(node_disk_writes_completed_total{device=~"(sd|nvme|hd)[a-z0-9]*"${clusterSuffix1}}[1m])`, + inode_utilization: `(1- (node_filesystem_files_free{${diskPararms}${clusterSuffix1}}) / (node_filesystem_files{mountpoint="/",fstype!="rootfs"${clusterSuffix1}})) * 100`, + disk_used_percentage: `(node_filesystem_size_bytes{${diskPararms}${clusterSuffix1}}-node_filesystem_free_bytes{${diskPararms}${clusterSuffix1}}) *100/(node_filesystem_avail_bytes {${diskPararms}${clusterSuffix1}}+(node_filesystem_size_bytes{${diskPararms}${clusterSuffix1}}-node_filesystem_free_bytes{${diskPararms}${clusterSuffix1}}))`, - disk_size: 'node_filesystem_size_bytes{mountpoint="/",fstype!="rootfs"}', - network_in_rate: - 'sum by(instance)(irate(node_network_receive_bytes_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', - network_out_rate: - 'sum by(instance)(irate(node_network_transmit_bytes_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', - network_in_errs: - 'sum by(instance)(irate(node_network_receive_errs_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', - network_out_errs: - 'sum by(instance)(irate(node_network_transmit_errs_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', - network_in_packets: - 'sum by(instance)(irate(node_network_receive_packets_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', - network_out_packets: - 'sum by(instance)(irate(node_network_transmit_packets_total{device=~"(eth|en)[a-z0-9]*"}[1m]))', + disk_size: `node_filesystem_size_bytes{${diskPararms}${clusterSuffix1}}`, + network_in_rate: `ceil(sum by(instance)(irate(node_network_receive_bytes_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + network_out_rate: `ceil(sum by(instance)(irate(node_network_transmit_bytes_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + network_in_errs: `ceil(sum by(instance)(irate(node_network_receive_errs_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + network_out_errs: `ceil(sum by(instance)(irate(node_network_transmit_errs_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + network_in_packets: `ceil(sum by(instance)(irate(node_network_receive_packets_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + network_out_packets: `ceil(sum by(instance)(irate(node_network_transmit_packets_total{device=~"(eth|en)[a-z0-9]*"${clusterSuffix1}}[1m])))`, + } }; export const NEBULA_COUNT = { @@ -505,3 +449,11 @@ export const NEBULA_COUNT = { storage: 'nebula_storaged_count', meta: 'nebula_metad_count', }; + +export const updatePromql = (service: { + SUPPORT_METRICS: typeof SUPPORT_METRICS, + LINUX: typeof LINUX +}) => { + SUPPORT_METRICS = service.SUPPORT_METRICS + LINUX = service.LINUX +} \ No newline at end of file diff --git a/src/utils/service.ts b/src/utils/service.ts index f91b0719..944967b8 100644 --- a/src/utils/service.ts +++ b/src/utils/service.ts @@ -4,6 +4,57 @@ export const SERVICE_POLLING_INTERVAL = 10 * 1000; export const SERVICE_QUERY_PERIOD = 10 * 60; export const SERVICE_DEFAULT_RANGE = 6 * 60 * 60 * 1000; +export enum INTERVAL_FREQUENCY_TYPE { + OFF = 'Off', + S5 = '5s', + S10 = '10s', + S15 = '15s', + S30 = '30s', + M1 = '1m', + M5 = '5m', + M15 = '15m', + M30 = '30m', +} + +export const INTERVAL_FREQUENCY_LIST = [ + { + type: INTERVAL_FREQUENCY_TYPE.OFF, + value: 0, + }, + { + type: INTERVAL_FREQUENCY_TYPE.S5, + value: 5 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.S10, + value: 10 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.S15, + value: 15 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.S30, + value: 30 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.M1, + value: 60 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.M5, + value: 5 * 60 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.M15, + value: 15 * 60 * 1000, + }, + { + type: INTERVAL_FREQUENCY_TYPE.M30, + value: 30 * 60 * 1000, + } +] + export const LINE_COLORS = [ '#4372FF', '#EB2F96', diff --git a/static/iconfont/iconfont.js b/static/iconfont/iconfont.js index fe3a9cb8..46813ca8 100644 --- a/static/iconfont/iconfont.js +++ b/static/iconfont/iconfont.js @@ -1 +1 @@ -!function(a){var l,o,h,v,m,i='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),z=function(a,l){l.parentNode.insertBefore(a,l)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function e(){m||(m=!0,h())}function n(){try{v.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}e()}l=function(){var a,l;(l=document.createElement("div")).innerHTML=i,i=null,(a=l.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",l=a,(a=document.body).firstChild?z(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),l()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(h=l,v=a.document,m=!1,n(),v.onreadystatechange=function(){"complete"==v.readyState&&(v.onreadystatechange=null,e())})}(window); \ No newline at end of file +!function(e){var t,n,d,o,i,a,r='';function c(){i||(i=!0,d())}t=function(){var e,t,n;(n=document.createElement("div")).innerHTML=r,r=null,(t=n.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(n=document.body).firstChild?(t=n.firstChild).parentNode.insertBefore(e,t):n.appendChild(e))},document.addEventListener?["complete","loaded","interactive"].indexOf(document.readyState)>-1?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(d=t,o=e.document,i=!1,(a=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(a,50)}c()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,c())})}(window); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 05710407..ecdccde9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ "baseUrl": ".", "paths": { "@assets/*": ["app/assets/*"], - "@/*": ["src/*"], + "@/*": ["src/*"] } } } \ No newline at end of file diff --git a/vendors/config-release.json b/vendors/config-release.json index c67bddcb..b0d2091f 100644 --- a/vendors/config-release.json +++ b/vendors/config-release.json @@ -2,14 +2,20 @@ "port": 7003, "proxy":{ "gateway":{ - "target": "http://localhost:8080" + "target": "nebula-http-gateway:8090" }, "prometheus":{ - "target": "192.168.8.157:9090" + "target": "prometheus:9090" + }, + "graph":{ + "target": "graphd:19669" + }, + "storage":{ + "target": "graphd:19779" } }, "nebulaServer": { - "ip": "192.168.8.143", + "ip": "graphd", "port": 9669 } } diff --git a/vendors/nebula-stats-exporter/config.yaml b/vendors/nebula-stats-exporter/config.yaml index 413c3ca3..92d38848 100644 --- a/vendors/nebula-stats-exporter/config.yaml +++ b/vendors/nebula-stats-exporter/config.yaml @@ -12,16 +12,4 @@ clusters: - name: storaged0 endpointIP: 192.168.8.143 endpointPort: 19779 - componentType: storaged - - name: metad1 - endpointIP: 192.168.8.167 - endpointPort: 19559 - componentType: metad - - name: graphd1 - endpointIP: 192.168.8.167 - endpointPort: 19669 - componentType: graphd - - name: storaged2 - endpointIP: 192.168.8.167 - endpointPort: 19779 componentType: storaged \ No newline at end of file diff --git a/vendors/prometheus/prometheus.yaml b/vendors/prometheus/prometheus.yaml index bbce22d2..a6edbcaf 100644 --- a/vendors/prometheus/prometheus.yaml +++ b/vendors/prometheus/prometheus.yaml @@ -5,10 +5,10 @@ scrape_configs: - job_name: 'node-exporter' static_configs: - targets: [ - '127.0.0.1:9100' # your node-exporter metrics endpoints + 'host.docker.internal:9100' # your node-exporter metrics endpoints ] - job_name: 'nebula-stats-exporter' static_configs: - targets: [ - '127.0.0.1:9200', # nebula-stats-exporter metrics endpoints + 'nebula-stats-exporter:9200', # nebula-stats-exporter metrics endpoints ] \ No newline at end of file