From 35b0cd59b8db6a34791807d5dde5d896a6bca7c8 Mon Sep 17 00:00:00 2001 From: "peng.peng" Date: Thu, 29 Jun 2023 12:59:37 +0800 Subject: [PATCH] =?UTF-8?q?(*)=E5=A4=A7=E5=B1=8F=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/.vscode/launch.json | 3 +- api/app/lib/controllers/backups/index.js | 2 + api/app/lib/controllers/homepage/index.js | 70 ++++++++++++++++++- api/app/lib/routes/homepage/index.js | 6 +- api/config.js | 8 ++- .../sections/backups/containers/backupTask.js | 4 +- .../sections/homePage/components/dataShare.js | 12 +++- .../sections/homePage/components/dataTop5.js | 37 ++++------ .../homePage/components/nodeResource.js | 11 ++- .../src/sections/homePage/components/util.js | 3 + 10 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 web/client/src/sections/homePage/components/util.js diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 21014f1..46c4981 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -17,7 +17,8 @@ // 研发 "-g postgres://FashionAdmin:123456@10.8.30.39:5432/GovernmentDataResourceCenter", // "-g postgres://FashionAdmin:123456@10.8.30.156:5432/gdrcenter", - "-b http://10.8.30.161:31420" + "-b http://10.8.30.161:31420", + "-s http://10.8.30.161:32258" ] }, { diff --git a/api/app/lib/controllers/backups/index.js b/api/app/lib/controllers/backups/index.js index e7ce69f..b2fa242 100644 --- a/api/app/lib/controllers/backups/index.js +++ b/api/app/lib/controllers/backups/index.js @@ -134,6 +134,7 @@ function restore(opts) { const { id, source, databases: { database, host, password, port, user } } = ctx.request.body await models.Backups.update({ state: '恢复中', + restoreStart: moment() }, { where: { id: id } }) //调用后端备份接口 const url = backupsUrl + `/restoreDB?dbHost=${host}&dbPort=${port}&user=${user}&password=${password}&dbName=${database}&backFileName=${source}`; @@ -142,6 +143,7 @@ function restore(opts) { models.Backups.update({ state: code == 200 ? '恢复成功' : '恢复失败', log: code == 200 ? '' : message, + restoreEnd: moment(), restoreDatabases: ctx.request.body.databases }, { where: { id: id } }) if (code != 200) ctx.fs.logger.error(`path: ${ctx.path}, error: ${message}`); diff --git a/api/app/lib/controllers/homepage/index.js b/api/app/lib/controllers/homepage/index.js index 4e1b9f7..84a9ae4 100644 --- a/api/app/lib/controllers/homepage/index.js +++ b/api/app/lib/controllers/homepage/index.js @@ -2,6 +2,7 @@ const moment = require('moment'); const diskinfo = require('diskinfo'); const os = require('os-utils'); +const request = require("superagent"); function getNodeResources(opts) { return async function (ctx, next) { @@ -90,8 +91,73 @@ function getDataTotalTop5(opts) { } + +async function queryToken_(ctx) { + let rslt = null; + try { + const { k8s } = ctx; + const params = { + grant_type: 'password', + username: 'admin', + password: 'Fashion123', + client_id: 'kubesphere', + client_secret: 'kubesphere', + } + const url = k8s + '/oauth/token' + rslt = await request.post(url).send(params) + .set('Content-Type', 'application/x-www-form-urlencoded') + + return (rslt || {}).body || null; + } catch (err) { + throw err + } + +} + +function mathRound(use, total) { + return Math.round(parseFloat(use) / parseFloat(total) * 1000) / 10 +} + +function getClusterInfo(opts) { + return async function (ctx, next) { + const { k8s } = opts; + let errMsg = { message: '获取节点资源失败' } + try { + const token = await queryToken_(opts); + const url = k8s + '/kapis/monitoring.kubesphere.io/v1alpha3/cluster?metrics_filter=cluster_cpu_usage%7Ccluster_cpu_total%7Ccluster_memory_usage_wo_cache%7Ccluster_memory_total%7Ccluster_disk_size_usage%7Ccluster_disk_size_capacity%7Ccluster_pod_running_count%7Ccluster_pod_quota%24' + let res = await request.get(url) + .set('Authorization', `Bearer ${token.access_token}`) + .set('Content-Type', 'application/json;charset=utf-8'); + if (res.body && res.body.results) { + let results = res.body.results; + let cpuUsage = ((results.find(s => s.metric_name == 'cluster_cpu_usage') || {}).data || {}).result[0].value[1]; + let cpuTotal = ((results.find(s => s.metric_name == 'cluster_cpu_total') || {}).data || {}).result[0].value[1]; + let diskUsage = ((results.find(s => s.metric_name == 'cluster_disk_size_usage') || {}).data || {}).result[0].value[1]; + let diskTotal = ((results.find(s => s.metric_name == 'cluster_disk_size_capacity') || {}).data || {}).result[0].value[1]; + let memoryUsage = ((results.find(s => s.metric_name == 'cluster_memory_usage_wo_cache') || {}).data || {}).result[0].value[1]; + let memoryTotal = ((results.find(s => s.metric_name == 'cluster_memory_total') || {}).data || {}).result[0].value[1]; + ctx.status = 200; + ctx.body = { + cpu: mathRound(cpuUsage, cpuTotal), + disk: mathRound(diskUsage, diskTotal), + memory: mathRound(memoryUsage, memoryTotal), + }; + } else { + ctx.status = 400; + ctx.body = errMsg; + } + + + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = errMsg + } + } +} + module.exports = { getNodeResources, - getDataTotalTop5 - + getDataTotalTop5, + getClusterInfo } diff --git a/api/app/lib/routes/homepage/index.js b/api/app/lib/routes/homepage/index.js index 682c55d..1b12fb6 100644 --- a/api/app/lib/routes/homepage/index.js +++ b/api/app/lib/routes/homepage/index.js @@ -6,8 +6,12 @@ module.exports = function (app, router, opts, AuthCode) { app.fs.api.logAttr['GET/homepage/node/resources'] = { content: '获取节点资源信息', visible: true }; router.get('/homepage/node/resources', backups.getNodeResources(opts)) - + app.fs.api.logAttr['GET/homepage/datatotal/top5'] = { content: '获取数据总量和top5', visible: true }; router.get('/homepage/datatotal/top5', backups.getDataTotalTop5(opts)) + app.fs.api.logAttr['GET/homepage/cluters'] = { content: '获取集群资源节点信息', visible: true }; + router.get('/homepage/datatotal/cluters', backups.getClusterInfo(opts)) + + }; diff --git a/api/config.js b/api/config.js index 9e14778..a4be807 100644 --- a/api/config.js +++ b/api/config.js @@ -11,6 +11,7 @@ const dev = process.env.NODE_ENV == 'development'; args.option(['p', 'port'], '启动端口'); args.option(['g', 'pg'], 'postgre 服务 URL'); args.option(['b', 'backups'], '后端数据库备份恢复接口地址'); +args.option(['s', 'kubesphere'], 'kubesphere地址'); const flags = args.parse(process.argv); @@ -23,7 +24,9 @@ const QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak; const QINIU_SK = process.env.ANXINCLOUD_QINIU_SECRETKEY || flags.qnsk; const BACKUPS_URL = process.env.BACKUPS_URL || flags.backups; -if (!DB || !BACKUPS_URL) { +const KUBESPHERE_URL = process.env.KUBESPHERE_URL || flags.kubesphere; + +if (!DB || !BACKUPS_URL || !KUBESPHERE_URL) { console.log('缺少启动参数,异常退出'); args.showHelp(); process.exit(-1); @@ -68,7 +71,8 @@ const product = { } }, pssaRequest: [], - backupsUrl: BACKUPS_URL + backupsUrl: BACKUPS_URL, + k8s: KUBESPHERE_URL } } ], diff --git a/web/client/src/sections/backups/containers/backupTask.js b/web/client/src/sections/backups/containers/backupTask.js index adf2242..669d192 100644 --- a/web/client/src/sections/backups/containers/backupTask.js +++ b/web/client/src/sections/backups/containers/backupTask.js @@ -96,17 +96,19 @@ function Member(props) { title: '恢复数据源', dataIndex: 'restoreDatabases', width: '8%', - render: (text, record) => record?.restoreDatabases?.database + render: (text, record) => record?.restoreDatabases?.displayName }, { title: '恢复开始时间', width: '7%', dataIndex: 'restoreStart', + render: (text, record) => { return record?.restoreStart ? moment(record?.restoreStart).format('YYYY-MM-DD HH:mm:ss') : '-' } }, { title: '恢复结束时间', width: '7%', dataIndex: 'restoreEnd', + render: (text, record) => { return record?.restoreEnd ? moment(record?.restoreEnd).format('YYYY-MM-DD HH:mm:ss') : '-' } }, { title: '异常日志', diff --git a/web/client/src/sections/homePage/components/dataShare.js b/web/client/src/sections/homePage/components/dataShare.js index 020b564..dcae604 100644 --- a/web/client/src/sections/homePage/components/dataShare.js +++ b/web/client/src/sections/homePage/components/dataShare.js @@ -1,8 +1,15 @@ import React, { useEffect, useState } from 'react' import Box from './public/table-card'; - +import { ApiTable, useFsRequest } from '$utils'; +import { mathRound } from './util'; function DataShare(props) { + const { data: dataTotal = {} } = useFsRequest({ + url: 'homepage/datatotal/top5', + pollingInterval: 1000 * 60, + cacheKey: 'datatotal', + }); + const renderItem = (s) => { return
@@ -14,8 +21,9 @@ function DataShare(props) {
} + const leftData = [ - { key: '1', data: 2000, unit: '条', title: '共享库数据总量' }, + { key: '1', data: mathRound(dataTotal?.total), unit: '万条', title: '共享库数据总量' }, { key: '2', data: 2000, unit: '次', title: '访问接口总次数' }, { key: '3', data: 2000, unit: '个', title: '访问接口用户总数' }] const rightData = [ diff --git a/web/client/src/sections/homePage/components/dataTop5.js b/web/client/src/sections/homePage/components/dataTop5.js index b9ff15a..37c102b 100644 --- a/web/client/src/sections/homePage/components/dataTop5.js +++ b/web/client/src/sections/homePage/components/dataTop5.js @@ -2,32 +2,25 @@ import React, { useEffect, useState } from 'react' import Box from './public/table-card'; import ReactEcharts from 'echarts-for-react'; import './style.less'; +import { useFsRequest } from '$utils'; +import { mathRound } from './util'; function DataTop5(props) { const { cardContentHeight } = props; + const { data: dataTotal = {} } = useFsRequest({ + url: 'homepage/datatotal/top5', + pollingInterval: 1000 * 60, + cacheKey: 'datatotal', + }); + const renderBody = () => { - let chartData = [ - { - name: '工商局', - value: 12245, - }, - { - name: '人社局', - value: 11211, - }, - { - name: '市政单位', - value: 11165, - }, - { - name: '住建局', - value: 11145, - }, - { - name: '环保局', - value: 11128, - }, - ] + let chartData = dataTotal?.top5?.map(x => { + return { + name: x?.dataSource?.resourceCatalog?.name, + value: mathRound(x.dbRecordCount), + } + }) || [] + let options = { xAxis: { splitLine: { diff --git a/web/client/src/sections/homePage/components/nodeResource.js b/web/client/src/sections/homePage/components/nodeResource.js index 981c240..101468d 100644 --- a/web/client/src/sections/homePage/components/nodeResource.js +++ b/web/client/src/sections/homePage/components/nodeResource.js @@ -1,22 +1,27 @@ import React, { useEffect, useState } from 'react' import Box from './public/table-card'; +import { ApiTable, useFsRequest } from '$utils'; import './style.less'; function NodeResource(props) { + const { data: cluters = {} } = useFsRequest({ + url: 'homepage/datatotal/cluters', + pollingInterval: 1000 * 10, + }); const renderBody = () => { return
-
97.2%
+
{cluters?.disk}%
硬盘
-
97.2%
+
{cluters?.memory}%
内存
-
97.2%
+
{cluters?.cpu}%
CPU
diff --git a/web/client/src/sections/homePage/components/util.js b/web/client/src/sections/homePage/components/util.js new file mode 100644 index 0000000..1a4c117 --- /dev/null +++ b/web/client/src/sections/homePage/components/util.js @@ -0,0 +1,3 @@ +export const mathRound = (number) => { + return number ? Math.round(number / 1000) / 10 : 0 +} \ No newline at end of file