石玲燕 3 жил өмнө
parent
commit
8c3b7903bd

+ 7 - 5
src/pages/halberd/components/addTask/index.js

@@ -2,7 +2,7 @@ import React, { Component } from 'react'
 import { Modal, Button, Input, message, Upload } from 'antd'
 import { FormItem } from 'wptpc-design'
 import { yc } from '@/conf/config'
-
+const { TextArea } = Input
 class Index extends Component {
   state = {
     data: null,
@@ -297,14 +297,16 @@ class Index extends Component {
                   key={i}
                   style={{
                     display: 'flex',
-                    height: '40px',
+                    // height: '70px',
+                    margin: '5px 0',
                     alignItems: 'center',
                     flexWrap: 'wrap'
                   }}
                 >
-                  <Input
+                  <TextArea
                     value={d.url}
-                    style={{ width: 380 }}
+                    style={{ width: 450 }}
+                    autoSize={true}
                     placeholder="请输入压测的 URL"
                     isRequired
                     onChange={(e) =>
@@ -356,7 +358,7 @@ class Index extends Component {
       <Modal
         title={update ? '查看压测任务' : '添加压测任务'}
         visible={showModal}
-        width={940}
+        width={1100}
         onOk={this.onOk}
         okText={update ? '更新' : '确认'}
         onCancel={onCancel}

+ 118 - 32
src/pages/halberd/components/psReport/index.js

@@ -6,9 +6,41 @@ import s from './index.less'
 import { Descriptions, Divider } from 'antd'
 const Page = props => {
   const [data, setData] = React.useState({})
+  const [CVMQPS, setCVMQPS] = React.useState({
+    x: [],
+    y: []
+  })
 
   React.useEffect(() => {
-    const { data = {} } = props
+    const { data = {}, selectedRowKeys = [], dataSourceMap, showTitle } = props
+    const CVMQPS_x = []
+    const CVMQPS_y = []
+    const CVMQPS_xy = []
+    if (selectedRowKeys.length > 1 && showTitle) {
+      selectedRowKeys.forEach(item => {
+        const {
+          connects,
+          report: { summary_req_sec }
+        } = dataSourceMap[item]
+        CVMQPS_xy.push({
+          x: connects,
+          y: summary_req_sec
+        })
+      })
+      CVMQPS_xy.sort((m, n) => {
+        var a = m.x
+        var b = n.x
+        return a - b // 升序
+      })
+      CVMQPS_xy.forEach(item => {
+        CVMQPS_x.push(item.x)
+        CVMQPS_y.push(item.y)
+      })
+      setCVMQPS({
+        x: CVMQPS_x,
+        y: CVMQPS_y
+      })
+    }
     setData(data)
   }, [])
 
@@ -21,13 +53,57 @@ const Page = props => {
     showTitle = true,
     showLine = false
   } = props
-
-  function getOptions (title, xData = [], yData = [], {
-    yFormat = '{value}',
-    tooltipFormat,
-    yMax,
-    showAreaStyle
-  } = {}) {
+  function getQPSOptions (title, xData = [], yData = []) {
+    return {
+      title: {
+        text: title
+      },
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          label: {
+            show: true,
+            formatter: '链接数 {value} '
+          }
+        }
+      },
+      grid: {
+        left: '3%',
+        right: '8%',
+        bottom: '3%',
+        containLabel: true
+      },
+      toolbox: {
+        feature: {
+          saveAsImage: {}
+        }
+      },
+      xAxis: {
+        type: 'category',
+        name: '链接数',
+        boundaryGap: false,
+        data: xData
+      },
+      yAxis: {
+        type: 'value',
+        name: 'QPS'
+      },
+      series: [
+        {
+          name: 'QPS',
+          type: 'line',
+          stack: 'Total',
+          data: yData
+        }
+      ]
+    }
+  }
+  function getOptions (
+    title,
+    xData = [],
+    yData = [],
+    { yFormat = '{value}', tooltipFormat, yMax, showAreaStyle } = {}
+  ) {
     return {
       tooltip: {
         trigger: 'axis',
@@ -105,8 +181,8 @@ const Page = props => {
           itemStyle: {
             color: yData.length === 1 ? 'rgb(255, 70, 131)' : `hsl(${h}, 100%, 64%)`
           },
-          ...(
-            showAreaStyle ? {
+          ...(showAreaStyle
+            ? {
               areaStyle: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                   {
@@ -119,8 +195,8 @@ const Page = props => {
                   }
                 ])
               }
-            } : {}
-          ),
+            }
+            : {}),
           data: yItem.data
         }
       })
@@ -151,34 +227,39 @@ const Page = props => {
 
   return (
     <div>
-      {
-        showLine && (
-          <Divider />
-        )
-      }
-      {
+      {showLine && <Divider />}
+      {showTitle && (
+        <div className={s.title}>
+          {data.taskName}-压测报告
+          {CVMQPS.x.length > 0 && (
+            <ReactECharts option={getQPSOptions('QPS趋势图', CVMQPS.x, CVMQPS.y)} />
+          )}
+        </div>
+      )}
 
-        showTitle && (
-          <div className={s.title}>
-            {data.taskName}-压测报告
-          </div>
-        )
-      }
       <div className={s.descriptionWrap}>
         <Descriptions bordered>
           <Descriptions.Item label="压测参数" span={24}>
-            -t {data.threads} -c {data.connects} -d {data.duration} -timeout {data.timeout}</Descriptions.Item>
-          <Descriptions.Item label="服务器配置" span={8}>{get(data, 'monitor.infoResult')}</Descriptions.Item>
-          <Descriptions.Item label="部署类型" span={8}>{get(data, 'monitor.deployType')}</Descriptions.Item>
+            -t {data.threads} -c {data.connects} -d {data.duration} -timeout {data.timeout}
+          </Descriptions.Item>
+          <Descriptions.Item label="服务器配置" span={8}>
+            {get(data, 'monitor.infoResult')}
+          </Descriptions.Item>
+          <Descriptions.Item label="部署类型" span={8}>
+            {get(data, 'monitor.deployType')}
+          </Descriptions.Item>
           <Descriptions.Item label="QPS">{get(data, 'report.summary_req_sec')}</Descriptions.Item>
           <Descriptions.Item label="平均延时">{get(data, 'report.latency_mean')}</Descriptions.Item>
           <Descriptions.Item label="99% 延时">{get(data, 'report.latency_p99')}</Descriptions.Item>
         </Descriptions>
       </div>
       <ReactECharts
-        option={getOptions('压力持续图', get(data, 'pressure_data.cross'), [
-          { name: '数据', data: get(data, 'pressure_data.vertical') }
-        ], { showAreaStyle: true })}
+        option={getOptions(
+          '压力持续图',
+          get(data, 'pressure_data.cross'),
+          [{ name: '数据', data: get(data, 'pressure_data.vertical') }],
+          { showAreaStyle: true }
+        )}
       />
       <div className={s.chartWrap}>
         <div className={s.chartItem}>
@@ -224,7 +305,9 @@ const Page = props => {
             option={getOptions(
               '磁盘读取',
               getSeriesXData(get(data, 'monitor.metrics.node_disk_read_bytes_total') || {}),
-              getSeriesYData(get(data, 'monitor.metrics.node_disk_read_bytes_total') || {}, value => (value / 1024).toFixed(2)),
+              getSeriesYData(get(data, 'monitor.metrics.node_disk_read_bytes_total') || {}, value =>
+                (value / 1024).toFixed(2)
+              ),
               {
                 yFormat: function (value) {
                   return value + 'kb/s'
@@ -241,7 +324,10 @@ const Page = props => {
             option={getOptions(
               '磁盘写入',
               getSeriesXData(get(data, 'monitor.metrics.node_disk_written_bytes_total') || {}),
-              getSeriesYData(get(data, 'monitor.metrics.node_disk_written_bytes_total') || {}, value => (value / 1024).toFixed(2)),
+              getSeriesYData(
+                get(data, 'monitor.metrics.node_disk_written_bytes_total') || {},
+                value => (value / 1024).toFixed(2)
+              ),
               {
                 yFormat: function (value) {
                   return value + 'kb/s'

+ 103 - 24
src/pages/halberd/index.js

@@ -3,11 +3,21 @@ import { Button, Table, Badge, Popconfirm, Checkbox, Switch, message } from 'ant
 import AddTaskModal from './components/addTask'
 import StartTaskModal from './components/startTask'
 import ChangeSource from './components/changeSource'
-import router from 'umi/router'
 import { FilterTable } from 'wptpc-design'
 import { yc } from '@/conf/config'
 import Style from './index.less'
-import { taskBegin, taskStop, taskUpdate, taskAdd, taskCopy, caseAdd, caseDel, updateSource } from './service'
+import { connect } from 'dva'
+import {
+  taskBegin,
+  taskStop,
+  splitStop,
+  taskUpdate,
+  taskAdd,
+  taskCopy,
+  caseAdd,
+  caseDel,
+  updateSource
+} from './service'
 const apiUrl = `${yc}/schedule/task/list`
 
 const scheduleState = {
@@ -29,7 +39,10 @@ const scheduleState = {
     color: 'green'
   }
 }
-
+@connect(({ auth, user, loading }) => ({
+  auth,
+  user
+}))
 class Halberd extends React.PureComponent {
   state = {
     data: [],
@@ -39,6 +52,7 @@ class Halberd extends React.PureComponent {
     params: { detail: [] },
     copyNeedCase: false,
     copyNeedNew: false,
+    splitNeedCase: true,
     refreshChecked: false
   };
 
@@ -68,7 +82,7 @@ class Halberd extends React.PureComponent {
       title: '操作',
       key: '_id',
       render: value => {
-        const { copyNeedCase, copyNeedNew } = this.state
+        const { copyNeedCase, copyNeedNew, splitNeedCase } = this.state
         const titleNode = (
           <div>
             确定要复制吗?
@@ -98,17 +112,31 @@ class Halberd extends React.PureComponent {
             </div>
           </div>
         )
+
+        const splitTitleNode = (
+          <div>
+            确定要拆分吗?
+            <div>
+              <Checkbox
+                checked={splitNeedCase}
+                onChange={e => {
+                  this.setState({
+                    splitNeedCase: e.target.checked
+                  })
+                }}
+              >
+                复制 case
+              </Checkbox>
+            </div>
+          </div>
+        )
         return (
           <div>
             <ChangeSource
               onOk={this.handleChangeSource}
               params={value}
               trigger={
-                <Button
-                  type="primary"
-                  size={'small'}
-                  style={{ margin: '0 5px' }}
-                >
+                <Button type="primary" size={'small'} style={{ margin: '0 5px' }}>
                   更换数据源
                 </Button>
               }
@@ -127,7 +155,20 @@ class Halberd extends React.PureComponent {
                 复制任务
               </Button>
             </Popconfirm>
-
+            <Popconfirm
+              title={splitTitleNode}
+              onConfirm={() => {
+                this.splitTask(value._id, splitNeedCase)
+                this.clearSplitState()
+              }}
+              onCancel={this.clearSplitState}
+              okText="确定"
+              cancelText="取消"
+            >
+              <Button type="primary" size={'small'} style={{ margin: '0 5px' }}>
+                拆分任务
+              </Button>
+            </Popconfirm>
             <Button
               type="primary"
               size={'small'}
@@ -158,6 +199,7 @@ class Halberd extends React.PureComponent {
                 停止压测
               </Button>
             </Popconfirm>
+
             <Button
               type="primary"
               size={'small'}
@@ -221,6 +263,22 @@ class Halberd extends React.PureComponent {
     })
   };
 
+  /**
+   * 拆分任务
+   * @param {*} id
+   */
+  splitTask = (taskID, needCase) => {
+    splitStop({
+      taskID, // 需要拆分的任务id
+      needCase
+    }).then(res => {
+      const { code } = res
+      if (code === 0) {
+        this.refresh()
+      }
+    })
+  };
+
   /**
    * 打开查看详情弹窗
    * @param {*} id
@@ -258,7 +316,7 @@ class Halberd extends React.PureComponent {
    * @param {*} id
    */
   showViewReport = id => {
-    router.push(`/halberd/viewReport?id=${id}`)
+    window.open(`/halberd/viewReport?id=${id}`)
   };
 
   /**
@@ -313,7 +371,7 @@ class Halberd extends React.PureComponent {
    * @param {*} data
    */
   updateTask = data => {
-    const { name = '', getTokenUrl, detail = [], header = []  } = data
+    const { name = '', getTokenUrl, detail = [], header = [] } = data
     taskUpdate({
       id: data.id,
       name,
@@ -388,6 +446,15 @@ class Halberd extends React.PureComponent {
     })
   };
 
+  /**
+   * 恢复拆分状态
+   */
+  clearSplitState = () => {
+    this.setState({
+      splitNeedCase: true
+    })
+  };
+
   /**
    * 开始轮训
    */
@@ -492,8 +559,14 @@ class Halberd extends React.PureComponent {
     return <div>当前任务没有 case</div>
   };
 
-  render() {
-    const { addFormVisible, updateFormVisible, startTaskVisible, params, refreshChecked } = this.state
+  render () {
+    const {
+      addFormVisible,
+      updateFormVisible,
+      startTaskVisible,
+      params,
+      refreshChecked
+    } = this.state
     // filtertable的搜索项配置
     const filterSetting = {
       isClearSearch: true,
@@ -531,16 +604,22 @@ class Halberd extends React.PureComponent {
           <Button type="primary" onClick={this.showAddTask}>
             添加压测任务
           </Button>
-          <span style={{ float: 'right', marginRight: '120px' }}>定时刷新:&nbsp;<Switch checked={refreshChecked} onChange={(checked) => {
-            if (checked) {
-              this.startRefreshState()
-            } else {
-              this.stopRefreshState()
-            }
-            this.setState({
-              refreshChecked: checked
-            })
-          }} /></span>
+          <span style={{ float: 'right', marginRight: '120px' }}>
+            定时刷新:&nbsp;
+            <Switch
+              checked={refreshChecked}
+              onChange={checked => {
+                if (checked) {
+                  this.startRefreshState()
+                } else {
+                  this.stopRefreshState()
+                }
+                this.setState({
+                  refreshChecked: checked
+                })
+              }}
+            />
+          </span>
         </div>
         <FilterTable filterSetting={filterSetting} tableSetting={tableSetting} apiUrl={apiUrl} />
 

+ 5 - 0
src/pages/halberd/service.js

@@ -14,6 +14,11 @@ export async function taskStop (params) {
   const url = `${yc}/schedule/task/stop`
   return fetchApi(url, params)
 }
+// 拆分压测任务
+export async function splitStop (params) {
+  const url = `${yc}/schedule/task/split`
+  return fetchApi(url, params)
+}
 
 // 添加 case
 export async function caseAdd (params) {

+ 1 - 1
src/pages/halberd/viewReport/index.js

@@ -460,7 +460,7 @@ class Index extends Component {
             <div className="multiPsReportModal">
               {
                 selectedRowKeys.map((selectedRowKey, index) => (
-                  <PsReport data={dataSourceMap[selectedRowKey]} saveAsImage={false} showTitle={index === 0} showLine={index !== 0} />
+                  <PsReport data={dataSourceMap[selectedRowKey]} dataSourceMap={dataSourceMap} selectedRowKeys={selectedRowKeys} saveAsImage={false} showTitle={index === 0} showLine={index !== 0} />
                 ))
               }
             </div>