feat(性能测试): 资源池增加cpu以及是否有任务执行的显示
--story=1013023 --user=宋天阳 【通用功能】选择资源池运行时支持查看资源池的使用状态以及各节点CPU使用率 https://www.tapd.cn/55049933/s/1424391
This commit is contained in:
parent
09e7e0b247
commit
d76e854e14
|
@ -1,6 +1,10 @@
|
||||||
import { get } from 'metersphere-frontend/src/plugins/request';
|
import {get, post} from 'metersphere-frontend/src/plugins/request';
|
||||||
|
|
||||||
export function getTestResourcePools() {
|
export function getTestResourcePools() {
|
||||||
let url = '/testresourcepool/list/quota/valid';
|
let url = '/testresourcepool/list/quota/valid';
|
||||||
return get(url);
|
return get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNodeOperationInfo(request) {
|
||||||
|
return post(`/prometheus/query/node-operation-info`, request)
|
||||||
|
}
|
||||||
|
|
|
@ -64,6 +64,11 @@
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:disabled="!item.api"
|
:disabled="!item.api"
|
||||||
:value="item.id">
|
:value="item.id">
|
||||||
|
<template v-slot>
|
||||||
|
<node-operation-label
|
||||||
|
:nodeName="item.name"
|
||||||
|
:node-operation-info="nodeInfo(item.id)"/>
|
||||||
|
</template>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,19 +88,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { apiScenarioEnvMap } from '@/api/scenario';
|
import {apiScenarioEnvMap} from '@/api/scenario';
|
||||||
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
|
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
|
||||||
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
|
import {ENV_TYPE} from 'metersphere-frontend/src/utils/constants';
|
||||||
import { strMapToObj } from 'metersphere-frontend/src/utils';
|
import {strMapToObj} from 'metersphere-frontend/src/utils';
|
||||||
import { getOwnerProjects, getProjectConfig } from '@/api/project';
|
import {getOwnerProjects, getProjectConfig} from '@/api/project';
|
||||||
import { getTestResourcePools } from '@/api/test-resource-pool';
|
import {getNodeOperationInfo, getTestResourcePools} from '@/api/test-resource-pool';
|
||||||
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
|
import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token';
|
||||||
import EnvSelectPopover from '@/business/automation/scenario/EnvSelectPopover';
|
import EnvSelectPopover from '@/business/automation/scenario/EnvSelectPopover';
|
||||||
import { getApiCaseEnvironments } from '@/api/api-test-case';
|
import {getApiCaseEnvironments} from '@/api/api-test-case';
|
||||||
|
import NodeOperationLabel from "metersphere-frontend/src/components/node/NodeOperationLabel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ApiRunMode',
|
name: 'ApiRunMode',
|
||||||
components: { MsDialogFooter, EnvSelectPopover },
|
components: {MsDialogFooter, EnvSelectPopover, NodeOperationLabel},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -116,6 +122,7 @@ export default {
|
||||||
projectList: [],
|
projectList: [],
|
||||||
projectIds: new Set(),
|
projectIds: new Set(),
|
||||||
caseIdEnvNameMap: {},
|
caseIdEnvNameMap: {},
|
||||||
|
nodeOperationInfo: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -127,6 +134,26 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
nodeInfo(nodeId) {
|
||||||
|
return this.nodeOperationInfo[nodeId];
|
||||||
|
},
|
||||||
|
selectNodeOperation() {
|
||||||
|
let nodeOperationInfoRequest = {nodeIds: []};
|
||||||
|
this.resourcePools.forEach(item => {
|
||||||
|
nodeOperationInfoRequest.nodeIds.push(item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
getNodeOperationInfo(nodeOperationInfoRequest)
|
||||||
|
.then(response => {
|
||||||
|
this.parseNodeOperationStatus(response.data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
parseNodeOperationStatus(nodeOperationData) {
|
||||||
|
this.nodeOperationInfo = {};
|
||||||
|
nodeOperationData.forEach(item => {
|
||||||
|
this.nodeOperationInfo[item.id] = item;
|
||||||
|
});
|
||||||
|
},
|
||||||
open() {
|
open() {
|
||||||
this.runModeVisible = true;
|
this.runModeVisible = true;
|
||||||
this.getResourcePools();
|
this.getResourcePools();
|
||||||
|
@ -173,6 +200,7 @@ export default {
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
this.result = getTestResourcePools().then((response) => {
|
this.result = getTestResourcePools().then((response) => {
|
||||||
this.resourcePools = response.data;
|
this.resourcePools = response.data;
|
||||||
|
this.selectNodeOperation();
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,6 +20,10 @@ const message = {
|
||||||
trash: "Trash",
|
trash: "Trash",
|
||||||
yes: "yes",
|
yes: "yes",
|
||||||
no: "no",
|
no: "no",
|
||||||
|
running: "Running",
|
||||||
|
idle: "idle",
|
||||||
|
running_status: "Running Status",
|
||||||
|
cpu_usage: "CPU Usage",
|
||||||
expand_all: "Expand all",
|
expand_all: "Expand all",
|
||||||
close_all: "Close all",
|
close_all: "Close all",
|
||||||
example: "Demo",
|
example: "Demo",
|
||||||
|
|
|
@ -20,6 +20,10 @@ const message = {
|
||||||
trash: "回收站",
|
trash: "回收站",
|
||||||
yes: "是",
|
yes: "是",
|
||||||
no: "否",
|
no: "否",
|
||||||
|
running: "运行中",
|
||||||
|
idle: "空闲",
|
||||||
|
running_status: "运行状态",
|
||||||
|
cpu_usage: "CPU 使用率",
|
||||||
expand_all: "一键展开",
|
expand_all: "一键展开",
|
||||||
close_all: "一键收起",
|
close_all: "一键收起",
|
||||||
example: "示例",
|
example: "示例",
|
||||||
|
|
|
@ -20,6 +20,10 @@ const message = {
|
||||||
trash: "回收站",
|
trash: "回收站",
|
||||||
yes: "是",
|
yes: "是",
|
||||||
no: "否",
|
no: "否",
|
||||||
|
running: "運行中",
|
||||||
|
idle: "空閑",
|
||||||
|
running_status: "運行狀態",
|
||||||
|
cpu_usage: "CPU 使用率",
|
||||||
expand_all: "一鍵展開",
|
expand_all: "一鍵展開",
|
||||||
close_all: "一鍵收起",
|
close_all: "一鍵收起",
|
||||||
example: "示例",
|
example: "示例",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package io.metersphere.controller;
|
||||||
|
|
||||||
|
import io.metersphere.dto.ResourcePoolOperationInfo;
|
||||||
|
import io.metersphere.request.NodeOperationSelectRequest;
|
||||||
|
import io.metersphere.service.PrometheusService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/prometheus")
|
||||||
|
public class PrometheusController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PrometheusService prometheusService;
|
||||||
|
|
||||||
|
@PostMapping("/query/node-operation-info")
|
||||||
|
public List<ResourcePoolOperationInfo> queryMetric(@RequestBody NodeOperationSelectRequest request) {
|
||||||
|
return prometheusService.queryNodeOperationInfo(request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package io.metersphere.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ResourcePoolOperationInfo {
|
||||||
|
private String id;
|
||||||
|
private String cpuUsage;
|
||||||
|
private int runningTask = 0;
|
||||||
|
Map<String, NodeOperationInfo> nodeOperationInfos = new HashMap<>();
|
||||||
|
|
||||||
|
public void addNodeOperationInfo(String taskResourceId, String ip, String port, String cpuUsage, int runningTask) {
|
||||||
|
NodeOperationInfo nodeOperationInfo = new NodeOperationInfo();
|
||||||
|
nodeOperationInfo.setIp(ip);
|
||||||
|
nodeOperationInfo.setPort(port);
|
||||||
|
nodeOperationInfo.setCpuUsage(cpuUsage);
|
||||||
|
nodeOperationInfo.setRunningTask(runningTask);
|
||||||
|
nodeOperationInfos.put(taskResourceId, nodeOperationInfo);
|
||||||
|
|
||||||
|
this.cpuUsage = cpuUsage;
|
||||||
|
this.runningTask += runningTask;
|
||||||
|
|
||||||
|
if (nodeOperationInfos.size() > 1) {
|
||||||
|
//多节点的情况下暂不处理CPU使用率
|
||||||
|
this.cpuUsage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class NodeOperationInfo {
|
||||||
|
private String ip;
|
||||||
|
private String port;
|
||||||
|
private String cpuUsage;
|
||||||
|
private int runningTask;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package io.metersphere.request;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NodeOperationSelectRequest {
|
||||||
|
private List<String> nodeIds;
|
||||||
|
}
|
|
@ -14,13 +14,13 @@ import io.metersphere.log.vo.DetailColumn;
|
||||||
import io.metersphere.log.vo.OperatingLogDetails;
|
import io.metersphere.log.vo.OperatingLogDetails;
|
||||||
import io.metersphere.log.vo.system.SystemReference;
|
import io.metersphere.log.vo.system.SystemReference;
|
||||||
import io.metersphere.request.resourcepool.QueryResourcePoolRequest;
|
import io.metersphere.request.resourcepool.QueryResourcePoolRequest;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.beanutils.BeanUtils;
|
import org.apache.commons.beanutils.BeanUtils;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -37,18 +37,18 @@ public class BaseTestResourcePoolService {
|
||||||
@Resource
|
@Resource
|
||||||
private TestResourceMapper testResourceMapper;
|
private TestResourceMapper testResourceMapper;
|
||||||
|
|
||||||
public List<TestResourcePoolDTO> listResourcePools(QueryResourcePoolRequest request) {
|
|
||||||
|
public List<TestResourcePoolDTO> listResourcePoolById(List<String> testResourcePoolId) {
|
||||||
TestResourcePoolExample example = new TestResourcePoolExample();
|
TestResourcePoolExample example = new TestResourcePoolExample();
|
||||||
TestResourcePoolExample.Criteria criteria = example.createCriteria();
|
TestResourcePoolExample.Criteria criteria = example.createCriteria();
|
||||||
if (StringUtils.isNotBlank(request.getName())) {
|
criteria.andIdIn(testResourcePoolId).andTypeEqualTo("NODE");
|
||||||
criteria.andNameLike(StringUtils.wrapIfMissing(request.getName(), "%"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(request.getStatus())) {
|
|
||||||
criteria.andStatusEqualTo(request.getStatus());
|
|
||||||
}
|
|
||||||
criteria.andStatusNotEqualTo(DELETE.name());
|
criteria.andStatusNotEqualTo(DELETE.name());
|
||||||
example.setOrderByClause("update_time desc");
|
|
||||||
List<TestResourcePool> testResourcePools = testResourcePoolMapper.selectByExample(example);
|
List<TestResourcePool> testResourcePools = testResourcePoolMapper.selectByExample(example);
|
||||||
|
return this.generateTestResourcePoolDTO(testResourcePools);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TestResourcePoolDTO> generateTestResourcePoolDTO(List<TestResourcePool> testResourcePools) {
|
||||||
List<TestResourcePoolDTO> testResourcePoolDTOS = new ArrayList<>();
|
List<TestResourcePoolDTO> testResourcePoolDTOS = new ArrayList<>();
|
||||||
testResourcePools.forEach(pool -> {
|
testResourcePools.forEach(pool -> {
|
||||||
TestResourceExample example2 = new TestResourceExample();
|
TestResourceExample example2 = new TestResourceExample();
|
||||||
|
@ -67,6 +67,21 @@ public class BaseTestResourcePoolService {
|
||||||
return testResourcePoolDTOS;
|
return testResourcePoolDTOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TestResourcePoolDTO> listResourcePools(QueryResourcePoolRequest request) {
|
||||||
|
TestResourcePoolExample example = new TestResourcePoolExample();
|
||||||
|
TestResourcePoolExample.Criteria criteria = example.createCriteria();
|
||||||
|
if (StringUtils.isNotBlank(request.getName())) {
|
||||||
|
criteria.andNameLike(StringUtils.wrapIfMissing(request.getName(), "%"));
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(request.getStatus())) {
|
||||||
|
criteria.andStatusEqualTo(request.getStatus());
|
||||||
|
}
|
||||||
|
criteria.andStatusNotEqualTo(DELETE.name());
|
||||||
|
example.setOrderByClause("update_time desc");
|
||||||
|
List<TestResourcePool> testResourcePools = testResourcePoolMapper.selectByExample(example);
|
||||||
|
return this.generateTestResourcePoolDTO(testResourcePools);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public TestResourcePool getResourcePool(String resourcePoolId) {
|
public TestResourcePool getResourcePool(String resourcePoolId) {
|
||||||
return testResourcePoolMapper.selectByPrimaryKey(resourcePoolId);
|
return testResourcePoolMapper.selectByPrimaryKey(resourcePoolId);
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
package io.metersphere.service;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.base.domain.TestResource;
|
||||||
|
import io.metersphere.commons.constants.ParamConstants;
|
||||||
|
import io.metersphere.commons.utils.CodingUtil;
|
||||||
|
import io.metersphere.commons.utils.JSON;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import io.metersphere.dto.ResourcePoolOperationInfo;
|
||||||
|
import io.metersphere.dto.TestResourcePoolDTO;
|
||||||
|
import io.metersphere.request.NodeOperationSelectRequest;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class PrometheusService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SystemParameterService systemParameterService;
|
||||||
|
@Resource
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
@Resource
|
||||||
|
private BaseTestResourcePoolService baseTestResourcePoolService;
|
||||||
|
|
||||||
|
public List<ResourcePoolOperationInfo> queryNodeOperationInfo(NodeOperationSelectRequest request) {
|
||||||
|
if (CollectionUtils.isEmpty(request.getNodeIds())) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
String host = systemParameterService.getValue(ParamConstants.BASE.PROMETHEUS_HOST.getValue());
|
||||||
|
String prometheusHost = StringUtils.isNotBlank(host) ? host : "http://ms-prometheus:9090";
|
||||||
|
List<TestResourcePoolDTO> testResourcePoolDTOS = baseTestResourcePoolService.listResourcePoolById(request.getNodeIds());
|
||||||
|
return this.queryNodeOperationInfoByPrometheus(prometheusHost, testResourcePoolDTOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourcePoolOperationInfo> queryNodeOperationInfoByPrometheus(String host, List<TestResourcePoolDTO> testResourcePoolDTOS) {
|
||||||
|
|
||||||
|
List<ResourcePoolOperationInfo> returnList = new ArrayList<>();
|
||||||
|
|
||||||
|
HttpHeaders headers;
|
||||||
|
try {
|
||||||
|
headers = new HttpHeaders();
|
||||||
|
headers.add("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
// 如果prometheus开启了认证,需要在请求头中添加认证信息
|
||||||
|
if (host.contains("@")) {
|
||||||
|
URL url = new URL(host);
|
||||||
|
// 获取认证信息部分
|
||||||
|
String userInfo = url.getUserInfo();
|
||||||
|
headers.add("Authorization", "Basic " + CodingUtil.base64Encoding(userInfo));
|
||||||
|
host = host.replace(userInfo + "@", "");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error("Get prometheus header fail.");
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecimalFormat decimalFormat = new DecimalFormat("#0.00");
|
||||||
|
decimalFormat.setMinimumFractionDigits(2);
|
||||||
|
decimalFormat.setMaximumFractionDigits(2);
|
||||||
|
|
||||||
|
for (TestResourcePoolDTO testResourcePoolDTO : testResourcePoolDTOS) {
|
||||||
|
ResourcePoolOperationInfo nodeOperationInfo = new ResourcePoolOperationInfo();
|
||||||
|
nodeOperationInfo.setId(testResourcePoolDTO.getId());
|
||||||
|
|
||||||
|
boolean queryCpuUsage = true;
|
||||||
|
if (testResourcePoolDTO.getResources().size() > 1) {
|
||||||
|
queryCpuUsage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cpuUsage = null;
|
||||||
|
int runningTask = 0;
|
||||||
|
|
||||||
|
for (TestResource testResource : testResourcePoolDTO.getResources()) {
|
||||||
|
String config = testResource.getConfiguration();
|
||||||
|
if (StringUtils.isNotBlank(config)) {
|
||||||
|
Map<String, Object> configMap = JSON.parseObject(config, Map.class);
|
||||||
|
String ip = String.valueOf(configMap.get("ip"));
|
||||||
|
String port = String.valueOf(configMap.get("port"));
|
||||||
|
String nodeId = ip + ":" + port;
|
||||||
|
if (queryCpuUsage) {
|
||||||
|
String cpuUsageQL = this.generatePromQL(new String[]{"system_cpu_usage"}, nodeId);
|
||||||
|
LogUtil.debug(host + "/api/v1/query?query=" + cpuUsageQL);
|
||||||
|
String cpuUsageDouble = this.runPromQL(headers, host, cpuUsageQL);
|
||||||
|
cpuUsage = decimalFormat.format(Double.parseDouble(cpuUsageDouble) * 100) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询任务数
|
||||||
|
List<String> taskSeriesNames = new ArrayList<>() {{
|
||||||
|
this.add("running_tasks_load_count");
|
||||||
|
this.add("running_tasks_api_count");
|
||||||
|
}};
|
||||||
|
String taskCountQL = this.generatePromQL(taskSeriesNames.toArray(new String[0]), nodeId);
|
||||||
|
String result = this.runPromQL(headers, host, taskCountQL);
|
||||||
|
if (StringUtils.isNotBlank(result)) {
|
||||||
|
runningTask += Integer.parseInt(result);
|
||||||
|
}
|
||||||
|
nodeOperationInfo.addNodeOperationInfo(String.valueOf(configMap.get("id")), ip, port, cpuUsage, runningTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MapUtils.isNotEmpty(nodeOperationInfo.getNodeOperationInfos())) {
|
||||||
|
returnList.add(nodeOperationInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generatePromQL(String[] series, String nodeId) {
|
||||||
|
StringBuilder promQL = new StringBuilder();
|
||||||
|
for (int i = 0; i < series.length; i++) {
|
||||||
|
String seriesName = series[i];
|
||||||
|
if (i > 0) {
|
||||||
|
promQL.append("+");
|
||||||
|
}
|
||||||
|
promQL.append(seriesName);
|
||||||
|
promQL.append("{");
|
||||||
|
promQL.append("instance=\"");
|
||||||
|
promQL.append(nodeId);
|
||||||
|
promQL.append("\"}");
|
||||||
|
}
|
||||||
|
return promQL.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String runPromQL(HttpHeaders headers, String host, String promQL) {
|
||||||
|
try {
|
||||||
|
MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
|
||||||
|
postParameters.add("query", promQL);
|
||||||
|
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(postParameters, headers);
|
||||||
|
return this.parseResultValue(restTemplate.postForObject(host + "/api/v1/query", httpEntity, Map.class));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error("query prometheus metric fail.");
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseResultValue(Map response) {
|
||||||
|
String value = null;
|
||||||
|
if (response != null && StringUtils.equals((String) response.get("status"), "success")) {
|
||||||
|
Map data = (Map) response.get("data");
|
||||||
|
List result = (List) data.get("result");
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(result)) {
|
||||||
|
Map resultObject = (Map) result.get(0);
|
||||||
|
List valueMetrics = (List) resultObject.get("value");
|
||||||
|
if (CollectionUtils.isNotEmpty(valueMetrics)) {
|
||||||
|
value = valueMetrics.get(1).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -237,3 +237,7 @@ export function exportScenarioJmx(condition) {
|
||||||
export function searchScenarioList(goPage, pageSize, condition) {
|
export function searchScenarioList(goPage, pageSize, condition) {
|
||||||
return post(`/api/automation/list/${goPage}/${pageSize}`, condition)
|
return post(`/api/automation/list/${goPage}/${pageSize}`, condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNodeOperationInfo(request) {
|
||||||
|
return post(`/prometheus/query/node-operation-info`, request)
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:disabled="!item.performance"
|
:disabled="!item.performance"
|
||||||
:value="item.id">
|
:value="item.id">
|
||||||
|
<template v-slot>
|
||||||
|
<node-operation-label
|
||||||
|
:nodeName="item.name"
|
||||||
|
:node-operation-info="nodeInfo(item.id)"/>
|
||||||
|
</template>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -219,22 +224,62 @@
|
||||||
:value="index">
|
:value="index">
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
<div style="margin-left: 5px;float: right">
|
||||||
|
<el-tag size="mini" v-if="nodeTaskCount(resourceNodes[threadGroup.resourceNodeIndex])===0"
|
||||||
|
style="color:#E5594B;background-color: #FFFFFF;border-color: #E5594B;margin-left: 5px;margin-right: 5px">
|
||||||
|
{{ $t("commons.idle") }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag size="mini" v-else-if="nodeTaskCount(resourceNodes[threadGroup.resourceNodeIndex])>0"
|
||||||
|
style="color:#89DB7E;background-color: #FFFFFF;border-color: #89DB7E;margin-left: 5px;margin-right: 5px">
|
||||||
|
{{ $t("commons.running") }}
|
||||||
|
</el-tag>
|
||||||
|
|
||||||
|
<span v-if="nodeTaskCount(resourceNodes[threadGroup.resourceNodeIndex]) !== -1">
|
||||||
|
{{
|
||||||
|
" " + $t("commons.cpu_usage") + " " + nodeCpuUsage(resourceNodes[threadGroup.resourceNodeIndex])
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-table class="adjust-table" :data="threadGroup.resourceNodes" :max-height="200">
|
<el-table class="adjust-table" :data="threadGroup.resourceNodes" :max-height="200">
|
||||||
<el-table-column type="index" width="50"/>
|
<el-table-column type="index" width="50"/>
|
||||||
<el-table-column prop="ip" label="IP"/>
|
<el-table-column prop="ip" label="IP"/>
|
||||||
|
<el-table-column prop="runStatus" :label="$t('commons.running_status')">
|
||||||
|
<template v-slot:default="{row}">
|
||||||
|
<el-tag size="mini" v-if="nodeTaskCount(row)===0"
|
||||||
|
style="color:#E5594B;background-color: #FFFFFF;border-color: #E5594B;margin-left: 5px;margin-right: 5px">
|
||||||
|
{{ $t("commons.idle") }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag size="mini" v-else-if="nodeTaskCount(row)>0"
|
||||||
|
style="color:#89DB7E;background-color: #FFFFFF;border-color: #89DB7E;margin-left: 5px;margin-right: 5px">
|
||||||
|
{{ $t("commons.running") }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"/>
|
<el-table-column prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"/>
|
||||||
<el-table-column prop="ratio" :label="$t('test_track.home.percentage')">
|
<el-table-column prop="ratio" :label="$t('test_track.home.percentage')" width="120px">
|
||||||
<template v-slot:default="{row}">
|
<template v-slot:default="{row}">
|
||||||
<el-input-number size="mini" v-model="row.ratio"
|
<el-input-number size="mini" v-model="row.ratio"
|
||||||
v-if="rampUpTimeVisible"
|
v-if="rampUpTimeVisible"
|
||||||
@change="customNodeChange(threadGroup)"
|
@change="customNodeChange(threadGroup)"
|
||||||
|
style="width: 100px"
|
||||||
:min="0" :step=".1" controls-position="right"
|
:min="0" :step=".1" controls-position="right"
|
||||||
:max="1"></el-input-number>
|
:max="1"></el-input-number>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="cpuUsage" :label="$t('commons.cpu_usage')">
|
||||||
|
<template v-slot:default="{row}">
|
||||||
|
{{ nodeCpuUsage(row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="refresh" width="50px">
|
||||||
|
<template v-slot:header>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="refreshNodeOperation" circle></el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -252,8 +297,9 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||||
|
import NodeOperationLabel from "metersphere-frontend/src/components/node/NodeOperationLabel";
|
||||||
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
|
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
|
||||||
import {getJmxContent, getLoadConfig, getResourcePools} from "../../../api/performance";
|
import {getJmxContent, getLoadConfig, getNodeOperationInfo, getResourcePools} from "../../../api/performance";
|
||||||
|
|
||||||
const HANDLER = "handler";
|
const HANDLER = "handler";
|
||||||
const THREAD_GROUP_TYPE = "tgType";
|
const THREAD_GROUP_TYPE = "tgType";
|
||||||
|
@ -288,7 +334,7 @@ const hexToRgb = function (hex) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PerformancePressureConfig",
|
name: "PerformancePressureConfig",
|
||||||
components: {MsChart},
|
components: {MsChart, NodeOperationLabel},
|
||||||
props: {
|
props: {
|
||||||
test: {
|
test: {
|
||||||
type: Object
|
type: Object
|
||||||
|
@ -333,6 +379,7 @@ export default {
|
||||||
autoStop: false,
|
autoStop: false,
|
||||||
autoStopDelay: 30,
|
autoStopDelay: 30,
|
||||||
rampUpTimeVisible: true,
|
rampUpTimeVisible: true,
|
||||||
|
nodeOperationInfo: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -344,7 +391,7 @@ export default {
|
||||||
{value: 'stoptest', label: this.$t('load_test.stoptest')},
|
{value: 'stoptest', label: this.$t('load_test.stoptest')},
|
||||||
{value: 'stoptestnow', label: this.$t('load_test.stoptestnow')},
|
{value: 'stoptestnow', label: this.$t('load_test.stoptestnow')},
|
||||||
];
|
];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.testId) {
|
if (this.testId) {
|
||||||
|
@ -395,6 +442,36 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
refreshNodeOperation() {
|
||||||
|
let nodeOperationInfoRequest = {nodeIds: []};
|
||||||
|
this.resourcePools.forEach(item => {
|
||||||
|
nodeOperationInfoRequest.nodeIds.push(item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
getNodeOperationInfo(nodeOperationInfoRequest)
|
||||||
|
.then(response => {
|
||||||
|
this.parseNodeOperationStatus(response.data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
nodeCpuUsage(row) {
|
||||||
|
let nodeInfo = this.nodeOperationInfo[this.resourcePool];
|
||||||
|
if (nodeInfo) {
|
||||||
|
return this.nodeOperationInfo[this.resourcePool].nodeOperationInfos[row.id].cpuUsage;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeTaskCount(row) {
|
||||||
|
let nodeInfo = this.nodeOperationInfo[this.resourcePool];
|
||||||
|
if (nodeInfo) {
|
||||||
|
return this.nodeOperationInfo[this.resourcePool].nodeOperationInfos[row.id].runningTask;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeInfo(nodeId) {
|
||||||
|
return this.nodeOperationInfo[nodeId];
|
||||||
|
},
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
getResourcePools(this.isShare)
|
getResourcePools(this.isShare)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
@ -408,9 +485,24 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nodeOperationInfoRequest = {nodeIds: []};
|
||||||
|
this.resourcePools.forEach(item => {
|
||||||
|
nodeOperationInfoRequest.nodeIds.push(item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
getNodeOperationInfo(nodeOperationInfoRequest)
|
||||||
|
.then(response => {
|
||||||
|
this.parseNodeOperationStatus(response.data);
|
||||||
|
});
|
||||||
this.resourcePoolChange();
|
this.resourcePoolChange();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
parseNodeOperationStatus(nodeOperationData) {
|
||||||
|
this.nodeOperationInfo = {};
|
||||||
|
nodeOperationData.forEach(item => {
|
||||||
|
this.nodeOperationInfo[item.id] = item;
|
||||||
|
});
|
||||||
|
},
|
||||||
getLoadConfig() {
|
getLoadConfig() {
|
||||||
this.result.loading = true;
|
this.result.loading = true;
|
||||||
getLoadConfig(this.testId, this.reportId, this.isShare)
|
getLoadConfig(this.testId, this.reportId, this.isShare)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {get, post} from "metersphere-frontend/src/plugins/request"
|
import {get, post} from "metersphere-frontend/src/plugins/request"
|
||||||
import {hasLicense} from "metersphere-frontend/src/utils/permission";
|
|
||||||
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
||||||
|
|
||||||
export function getProject(id) {
|
export function getProject(id) {
|
||||||
|
@ -38,6 +37,10 @@ export function getProjectConfig(projectId, type) {
|
||||||
return get(url);
|
return get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNodeOperationInfo(request) {
|
||||||
|
return post(`/prometheus/query/node-operation-info`, request)
|
||||||
|
}
|
||||||
|
|
||||||
export function apiTestReRun(condition) {
|
export function apiTestReRun(condition) {
|
||||||
return post('/api/test/exec/rerun', condition);
|
return post('/api/test/exec/rerun', condition);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,11 @@
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
>
|
>
|
||||||
|
<template v-slot>
|
||||||
|
<node-operation-label
|
||||||
|
:nodeName="item.name"
|
||||||
|
:node-operation-info="nodeInfo(item.id)"/>
|
||||||
|
</template>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -155,25 +160,19 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||||
import { strMapToObj } from "metersphere-frontend/src/utils";
|
import {strMapToObj} from "metersphere-frontend/src/utils";
|
||||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||||
import { ENV_TYPE } from "metersphere-frontend/src/utils/constants";
|
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
|
||||||
import {
|
import {getCurrentProjectID, getOwnerProjects,} from "@/business/utils/sdk-utils";
|
||||||
getCurrentProjectID,
|
import {getQuotaValidResourcePools} from "@/api/remote/resource-pool";
|
||||||
getOwnerProjects,
|
|
||||||
} from "@/business/utils/sdk-utils";
|
|
||||||
import { getQuotaValidResourcePools } from "@/api/remote/resource-pool";
|
|
||||||
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
||||||
import { getApiCaseEnv } from "@/api/remote/plan/test-plan-api-case";
|
import {getApiCaseEnv} from "@/api/remote/plan/test-plan-api-case";
|
||||||
import {
|
import {getApiScenarioEnv, getPlanCaseEnv, getPlanCaseProjectIds,} from "@/api/remote/plan/test-plan";
|
||||||
getApiScenarioEnv,
|
|
||||||
getPlanCaseEnv,
|
|
||||||
getPlanCaseProjectIds,
|
|
||||||
} from "@/api/remote/plan/test-plan";
|
|
||||||
import EnvGroupWithOption from "../env/EnvGroupWithOption";
|
import EnvGroupWithOption from "../env/EnvGroupWithOption";
|
||||||
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
|
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
|
||||||
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
|
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
|
||||||
import { getProjectConfig } from "@/api/project";
|
import {getNodeOperationInfo, getProjectConfig} from "@/api/project";
|
||||||
|
import NodeOperationLabel from "metersphere-frontend/src/components/node/NodeOperationLabel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsTestPlanRunModeWithEnv",
|
name: "MsTestPlanRunModeWithEnv",
|
||||||
|
@ -184,6 +183,7 @@ export default {
|
||||||
EnvGroupWithOption,
|
EnvGroupWithOption,
|
||||||
EnvironmentGroup,
|
EnvironmentGroup,
|
||||||
EnvSelectPopover,
|
EnvSelectPopover,
|
||||||
|
NodeOperationLabel,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
ENV_TYPE() {
|
ENV_TYPE() {
|
||||||
|
@ -229,6 +229,7 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
value: "confirmAndRun",
|
value: "confirmAndRun",
|
||||||
|
nodeOperationInfo: {},
|
||||||
browsers: [
|
browsers: [
|
||||||
{
|
{
|
||||||
label: this.$t("chrome"),
|
label: this.$t("chrome"),
|
||||||
|
@ -263,6 +264,26 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
nodeInfo(nodeId) {
|
||||||
|
return this.nodeOperationInfo[nodeId];
|
||||||
|
},
|
||||||
|
selectNodeOperation() {
|
||||||
|
let nodeOperationInfoRequest = {nodeIds: []};
|
||||||
|
this.resourcePools.forEach(item => {
|
||||||
|
nodeOperationInfoRequest.nodeIds.push(item.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
getNodeOperationInfo(nodeOperationInfoRequest)
|
||||||
|
.then(response => {
|
||||||
|
this.parseNodeOperationStatus(response.data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
parseNodeOperationStatus(nodeOperationData) {
|
||||||
|
this.nodeOperationInfo = {};
|
||||||
|
nodeOperationData.forEach(item => {
|
||||||
|
this.nodeOperationInfo[item.id] = item;
|
||||||
|
});
|
||||||
|
},
|
||||||
open(testType, runModeConfig) {
|
open(testType, runModeConfig) {
|
||||||
this.defaultEnvMap = {};
|
this.defaultEnvMap = {};
|
||||||
if (this.type === "plan") {
|
if (this.type === "plan") {
|
||||||
|
@ -350,6 +371,7 @@ export default {
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
getQuotaValidResourcePools().then((response) => {
|
getQuotaValidResourcePools().then((response) => {
|
||||||
this.resourcePools = response.data;
|
this.resourcePools = response.data;
|
||||||
|
this.selectNodeOperation();
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue