已合并
实例监控-330版本代码合入 #66
Louisyzh创建于 2023年2月21日
实例监控-330版本代码合入 #66
已合并
Louisyzh创建于 2023年2月21日
master合入到master
79 个文件变更+8592-9479
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/DataSourceConfig.java+36-0
@@ -0,0 +1,36 @@
1+package com.nctigba.observability.instance.config;
2+ 
3+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
4+import com.gitee.starblues.bootstrap.PluginContextHolder;
5+import com.gitee.starblues.spring.environment.EnvironmentProvider;
6+import org.springframework.boot.context.properties.ConfigurationProperties;
7+import org.springframework.boot.jdbc.DataSourceBuilder;
8+import org.springframework.context.annotation.Bean;
9+import org.springframework.context.annotation.Configuration;
10+ 
11+import javax.sql.DataSource;
12+ 
13+/**
14+ * @author LZW
15+ * @date 2023/1/5
16+ */
17+@Configuration
18+public class DataSourceConfig {
19+ @Bean
20+ @ConfigurationProperties(prefix = "spring.datasource")
21+ public DataSource dataSource() {
22+ EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider();
23+ // read config from dataKit platform
24+ String url = environmentProvider.getString("spring.datasource.url");
25+ String username = environmentProvider.getString("spring.datasource.username");
26+ String password = environmentProvider.getString("spring.datasource.password");
27+ String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name");
28+ 
29+ DataSource primary = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username)
30+ .password(password).build();
31+ var d=new DynamicRoutingDataSource();
32+ d.addDataSource("primary", primary);
33+ d.setPrimary("primary");
34+ return d;
35+ }
36+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/EnvironmentController.java+61-0
@@ -0,0 +1,61 @@
1+package com.nctigba.observability.instance.controller;
2+ 
3+import java.util.List;
4+import java.util.stream.Collectors;
5+ 
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO;
8+import org.opengauss.admin.system.plugin.facade.HostFacade;
9+import org.springframework.beans.factory.annotation.Autowired;
10+import org.springframework.web.bind.annotation.GetMapping;
11+import org.springframework.web.bind.annotation.RequestMapping;
12+import org.springframework.web.bind.annotation.RestController;
13+ 
14+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
17+import com.nctigba.observability.instance.entity.NctigbaEnv;
18+import com.nctigba.observability.instance.entity.NctigbaEnv.type;
19+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
20+import com.nctigba.observability.instance.service.ClusterManager;
21+ 
22+@RestController
23+@RequestMapping("/observability/v1/environment")
24+public class EnvironmentController {
25+ @Autowired
26+ private NctigbaEnvMapper envMapper;
27+ @Autowired
28+ @AutowiredType(Type.PLUGIN_MAIN)
29+ private HostFacade hostFacade;
30+ @Autowired
31+ private ClusterManager clusterManager;
32+ 
33+ @GetMapping("/prometheus")
34+ public List<NctigbaEnv> listPrometheus() {
35+ List<NctigbaEnv> env = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, type.PROMETHEUS));
36+ env.forEach(e->{
37+ e.setHost(hostFacade.getById(e.getHostid()));
38+ });
39+ return env;
40+ }
41+ 
42+ @GetMapping("/exporter")
43+ public List<OpsClusterVO> listExporter() {
44+ var env = envMapper.selectList(Wrappers.<NctigbaEnv>lambdaQuery().in(NctigbaEnv::getType,
45+ List.of(type.NODE_EXPORTER, type.OPENGAUSS_EXPORTER)));
46+ var hosts = env.stream().map(NctigbaEnv::getHostid).collect(Collectors.toSet());
47+ var clusters = clusterManager.getAllOpsCluster();
48+ return clusters.stream().filter(c->{
49+ var nodes = c.getClusterNodes().stream().filter(n->{
50+ return hosts.contains(n.getHostId());
51+ }).collect(Collectors.toList());
52+ c.setClusterNodes(nodes);
53+ return nodes.size() > 0;
54+ }).collect(Collectors.toList());
55+ }
56+ 
57+ @GetMapping("/hosts")
58+ public List<OpsHostEntity> hosts() {
59+ return hostFacade.listAll();
60+ }
61+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/Installer.java+81-0
@@ -0,0 +1,81 @@
1+package com.nctigba.observability.instance.controller;
2+ 
3+import java.io.IOException;
4+ 
5+import javax.websocket.Session;
6+ 
7+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
8+import org.opengauss.admin.common.core.handler.ops.cache.TaskManager;
9+import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager;
10+import org.opengauss.admin.system.plugin.extract.SocketExtract;
11+import org.opengauss.admin.system.plugin.facade.WsFacade;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.stereotype.Service;
14+ 
15+import com.gitee.starblues.annotation.Extract;
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
18+import com.nctigba.observability.instance.listener.PluginListener;
19+import com.nctigba.observability.instance.service.ExporterService;
20+import com.nctigba.observability.instance.service.PrometheusService;
21+ 
22+import cn.hutool.core.thread.ThreadUtil;
23+import cn.hutool.json.JSONUtil;
24+import lombok.extern.slf4j.Slf4j;
25+ 
26+@Slf4j
27+@Service
28+@Extract(bus = PluginListener.pluginId)
29+public class Installer implements SocketExtract {
30+ @Autowired
31+ @AutowiredType(Type.PLUGIN_MAIN)
32+ private WsConnectorManager wsConnectorManager;
33+ @Autowired
34+ private PrometheusService prometheusService;
35+ @Autowired
36+ private ExporterService exporterService;
37+ @Autowired
38+ @AutowiredType(Type.PLUGIN_MAIN)
39+ private WsFacade wsFacade;
40+ 
41+ @Override
42+ public void onOpen(String pluginId, String sessionId, Session session) {
43+ wsConnectorManager.register(sessionId, new WsSession(session, sessionId));
44+ System.out.println("连接成功。。。。。。。。");
45+ }
46+ 
47+ @Override
48+ public void processMessage(String sessionId, String message) {
49+ ThreadUtil.execute(() -> {
50+ try {
51+ var obj = JSONUtil.parseObj(message);
52+ var session = wsConnectorManager.getSession(sessionId)
53+ .orElseThrow(() -> new RuntimeException("websocket session not exist"));
54+ switch (obj.getStr("key")) {
55+ case "prometheus":
56+ prometheusService.install(session, obj.getStr("hostId"), obj.getStr("rootPassword"));
57+ break;
58+ case "exporter":
59+ exporterService.install(session, obj.getStr("nodeId"), obj.getStr("rootPassword"));
60+ }
61+ } catch (Exception e) {
62+ e.printStackTrace();
63+ wsFacade.sendMessage(PluginListener.pluginId, sessionId, e.toString());
64+ }
65+ });
66+ System.out.println("接收到消息并处理。。。。。。。。" + message);
67+ }
68+ 
69+ @Override
70+ public void onClose(String pluginId, String sessionId) {
71+ wsConnectorManager.getSession(sessionId).ifPresent(wsSession -> {
72+ try {
73+ wsSession.getSession().close();
74+ } catch (IOException e) {
75+ log.error("close websocket session fail", e);
76+ }
77+ });
78+ TaskManager.remove(sessionId).ifPresent(future -> future.cancel(true));
79+ wsConnectorManager.remove(sessionId);
80+ }
81+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/MonitoringController.java+2-1
@@ -217,7 +217,8 @@ public class MonitoringController {
217 return AppResult.ok("").addData(map);217 return AppResult.ok("").addData(map);
218 }218 }
219 219 
220- @GetMapping(value = "/server")220+ @SuppressWarnings("deprecation")
221+ @GetMapping(value = "/server")
221 public AppResult process(@RequestParam("id") String id) {222 public AppResult process(@RequestParam("id") String id) {
222 OpsClusterNodeVOSub node = topSQLService.clusterNode(id);223 OpsClusterNodeVOSub node = topSQLService.clusterNode(id);
223 SSHOperator ssh = SSHPoolManager.getSSHOperator(node.getPrivateIp(), node.getHostPort(), "root",224 SSHOperator ssh = SSHPoolManager.getSSHOperator(node.getPrivateIp(), node.getHostPort(), "root",
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ParamInfoController.java+51-0
@@ -0,0 +1,51 @@
1+package com.nctigba.observability.instance.controller;
2+ 
3+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
4+import com.nctigba.observability.instance.dto.param.DatabaseParamDTO;
5+import com.nctigba.observability.instance.dto.param.OsParamDTO;
6+import com.nctigba.observability.instance.model.param.ParamQuery;
7+import com.nctigba.observability.instance.service.ParamInfoService;
8+import io.swagger.annotations.ApiOperation;
9+import lombok.RequiredArgsConstructor;
10+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.web.bind.annotation.GetMapping;
13+import org.springframework.web.bind.annotation.RequestMapping;
14+import org.springframework.web.bind.annotation.RestController;
15+ 
16+import java.util.List;
17+ 
18+ 
19+/**
20+ * ParamInfo
21+ *
22+ * @author luomeng@ncti-gba.cn
23+ * @since 2023/01/30 15:00
24+ */
25+@RestController
26+@RequestMapping("/observability/v1/param")
27+@RequiredArgsConstructor
28+public class ParamInfoController {
29+ 
30+ private final ParamInfoService paramInfoService;
31+ 
32+ @Autowired
33+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
34+ protected EncryptionUtils encryptionUtils;
35+ 
36+ @ApiOperation("Database Param Info")
37+ @GetMapping(value = "/databaseParamInfo")
38+ public List<DatabaseParamDTO> databaseParamInfo(ParamQuery paramQuery) {
39+ return paramInfoService.getDatabaseParamInfo(paramQuery);
40+ }
41+ 
42+ @ApiOperation("Os Param Info")
43+ @GetMapping(value = "/osParamInfo")
44+ public List<OsParamDTO> osParamInfo(ParamQuery paramQuery) {
45+ if(paramQuery.getPassword()!=null && !"".equals(paramQuery.getPassword())){
46+ String password=encryptionUtils.decrypt(paramQuery.getPassword());
47+ paramQuery.setPassword(password);
48+ }
49+ return paramInfoService.getOsParamInfo(paramQuery);
50+ }
51+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/WdrController.java+105-0
@@ -0,0 +1,105 @@
1+package com.nctigba.observability.instance.controller;
2+ 
3+import java.beans.PropertyEditorSupport;
4+import java.util.Date;
5+ 
6+import javax.servlet.http.HttpServletResponse;
7+ 
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.utils.DateUtils;
10+import org.opengauss.admin.common.utils.ServletUtils;
11+import org.opengauss.admin.common.utils.StringUtils;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.format.annotation.DateTimeFormat;
14+import org.springframework.validation.annotation.Validated;
15+import org.springframework.web.bind.WebDataBinder;
16+import org.springframework.web.bind.annotation.DeleteMapping;
17+import org.springframework.web.bind.annotation.GetMapping;
18+import org.springframework.web.bind.annotation.InitBinder;
19+import org.springframework.web.bind.annotation.PathVariable;
20+import org.springframework.web.bind.annotation.PostMapping;
21+import org.springframework.web.bind.annotation.RequestBody;
22+import org.springframework.web.bind.annotation.RequestMapping;
23+import org.springframework.web.bind.annotation.RequestParam;
24+import org.springframework.web.bind.annotation.RestController;
25+ 
26+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
27+import com.nctigba.observability.instance.entity.OpsWdrEntity;
28+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
29+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrTypeEnum;
30+import com.nctigba.observability.instance.model.WdrGeneratorBody;
31+import com.nctigba.observability.instance.service.OpsWdrService;
32+ 
33+/**
34+ * @author lhf
35+ * @date 2022/10/13 15:14
36+ **/
37+@RestController
38+@RequestMapping("/wdr")
39+public class WdrController {
40+ @InitBinder
41+ public void initBinder(WebDataBinder binder) {
42+ // Date format
43+ binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
44+ @Override
45+ public void setAsText(String text) {
46+ setValue(DateUtils.parseDate(text));
47+ }
48+ });
49+ }
50+ 
51+ @Autowired
52+ private OpsWdrService wdrService;
53+ 
54+ @GetMapping("/listSnapshot")
55+ public Page<?> listSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {
56+ return wdrService.listSnapshot(startPage(), clusterId, hostId);
57+ }
58+ 
59+ @GetMapping("/createSnapshot")
60+ public AjaxResult createSnapshot(@RequestParam String clusterId, @RequestParam String hostId) {
61+ wdrService.createSnapshot(clusterId, hostId);
62+ return AjaxResult.success();
63+ }
64+ 
65+ @GetMapping("/list")
66+ public Page<OpsWdrEntity> list(@RequestParam String clusterId,
67+ @RequestParam(required = false, value = "wdrScope") WdrScopeEnum wdrScope,
68+ @RequestParam(required = false, value = "wdrType") WdrTypeEnum wdrType,
69+ @RequestParam(required = false, value = "hostId") String hostId,
70+ @RequestParam(required = false, value = "start") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date start,
71+ @RequestParam(required = false, value = "end") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date end) {
72+ return wdrService.listWdr(startPage(), clusterId, wdrScope, wdrType, hostId, start, end);
73+ }
74+ 
75+ @DeleteMapping("/del/{id}")
76+ public AjaxResult del(@PathVariable("id") String id) {
77+ wdrService.del(id);
78+ return AjaxResult.success();
79+ }
80+ 
81+ @PostMapping("/generate")
82+ public AjaxResult generate(@RequestBody @Validated WdrGeneratorBody wdrGeneratorBody) {
83+ wdrService.generate(wdrGeneratorBody);
84+ return AjaxResult.success();
85+ }
86+ 
87+ @GetMapping("/downloadWdr")
88+ public void downloadWdr(@RequestParam String wdrId, HttpServletResponse response) {
89+ wdrService.downloadWdr(wdrId, response);
90+ }
91+ 
92+ @SuppressWarnings("rawtypes")
93+ protected Page startPage() {
94+ Page page = new Page();
95+ Integer pageNum = ServletUtils.getParameterToInt("pageNum");
96+ Integer pageSize = ServletUtils.getParameterToInt("pageSize");
97+ if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) {
98+ page.setCurrent((long) pageNum);
99+ page.setSize((long) pageSize);
100+ page.setOptimizeCountSql(false);
101+ page.setMaxLimit(500L);
102+ }
103+ return page;
104+ }
105+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/param/DatabaseParamDTO.java+88-0
@@ -0,0 +1,88 @@
1+package com.nctigba.observability.instance.dto.param;
2+ 
3+public class DatabaseParamDTO {
4+ private String seqNo;
5+ private String classify;
6+ private String paramName;
7+ private String paramDetail;
8+ private String actualValue;
9+ private String suggestValue;
10+ private String defaultValue;
11+ private String unit;
12+ private String suggestExplain;
13+ 
14+ public DatabaseParamDTO() {
15+ }
16+ 
17+ public String getSeqNo() {
18+ return seqNo;
19+ }
20+ 
21+ public void setSeqNo(String seqNo) {
22+ this.seqNo = seqNo;
23+ }
24+ 
25+ public String getClassify() {
26+ return classify;
27+ }
28+ 
29+ public void setClassify(String classify) {
30+ this.classify = classify;
31+ }
32+ 
33+ public String getParamName() {
34+ return paramName;
35+ }
36+ 
37+ public void setParamName(String paramName) {
38+ this.paramName = paramName;
39+ }
40+ 
41+ public String getParamDetail() {
42+ return paramDetail;
43+ }
44+ 
45+ public void setParamDetail(String paramDetail) {
46+ this.paramDetail = paramDetail;
47+ }
48+ 
49+ public String getActualValue() {
50+ return actualValue;
51+ }
52+ 
53+ public void setActualValue(String actualValue) {
54+ this.actualValue = actualValue;
55+ }
56+ 
57+ public String getSuggestValue() {
58+ return suggestValue;
59+ }
60+ 
61+ public void setSuggestValue(String suggestValue) {
62+ this.suggestValue = suggestValue;
63+ }
64+ 
65+ public String getUnit() {
66+ return unit;
67+ }
68+ 
69+ public String getDefaultValue() {
70+ return defaultValue;
71+ }
72+ 
73+ public void setDefaultValue(String defaultValue) {
74+ this.defaultValue = defaultValue;
75+ }
76+ 
77+ public void setUnit(String unit) {
78+ this.unit = unit;
79+ }
80+ 
81+ public String getSuggestExplain() {
82+ return suggestExplain;
83+ }
84+ 
85+ public void setSuggestExplain(String suggestExplain) {
86+ this.suggestExplain = suggestExplain;
87+ }
88+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/param/OsParamDTO.java+88-0
@@ -0,0 +1,88 @@
1+package com.nctigba.observability.instance.dto.param;
2+ 
3+public class OsParamDTO {
4+ private String seqNo;
5+ private String classify;
6+ private String paramName;
7+ private String paramDetail;
8+ private String actualValue;
9+ private String suggestValue;
10+ private String defaultValue;
11+ private String unit;
12+ private String suggestExplain;
13+ 
14+ public OsParamDTO() {
15+ }
16+ 
17+ public String getSeqNo() {
18+ return seqNo;
19+ }
20+ 
21+ public void setSeqNo(String seqNo) {
22+ this.seqNo = seqNo;
23+ }
24+ 
25+ public String getClassify() {
26+ return classify;
27+ }
28+ 
29+ public void setClassify(String classify) {
30+ this.classify = classify;
31+ }
32+ 
33+ public String getParamName() {
34+ return paramName;
35+ }
36+ 
37+ public void setParamName(String paramName) {
38+ this.paramName = paramName;
39+ }
40+ 
41+ public String getParamDetail() {
42+ return paramDetail;
43+ }
44+ 
45+ public void setParamDetail(String paramDetail) {
46+ this.paramDetail = paramDetail;
47+ }
48+ 
49+ public String getActualValue() {
50+ return actualValue;
51+ }
52+ 
53+ public void setActualValue(String actualValue) {
54+ this.actualValue = actualValue;
55+ }
56+ 
57+ public String getSuggestValue() {
58+ return suggestValue;
59+ }
60+ 
61+ public void setSuggestValue(String suggestValue) {
62+ this.suggestValue = suggestValue;
63+ }
64+ 
65+ public String getUnit() {
66+ return unit;
67+ }
68+ 
69+ public String getDefaultValue() {
70+ return defaultValue;
71+ }
72+ 
73+ public void setDefaultValue(String defaultValue) {
74+ this.defaultValue = defaultValue;
75+ }
76+ 
77+ public void setUnit(String unit) {
78+ this.unit = unit;
79+ }
80+ 
81+ public String getSuggestExplain() {
82+ return suggestExplain;
83+ }
84+ 
85+ public void setSuggestExplain(String suggestExplain) {
86+ this.suggestExplain = suggestExplain;
87+ }
88+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/NctigbaEnv.java+35-0
@@ -0,0 +1,35 @@
1+package com.nctigba.observability.instance.entity;
2+ 
3+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
4+ 
5+import com.baomidou.mybatisplus.annotation.IdType;
6+import com.baomidou.mybatisplus.annotation.TableField;
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+ 
10+import lombok.Data;
11+import lombok.experimental.Accessors;
12+ 
13+@Data
14+@Accessors(chain = true)
15+@TableName("nctigba_env")
16+public class NctigbaEnv {
17+ @TableId(type = IdType.ASSIGN_UUID)
18+ String id;
19+ String hostid;
20+ type type;
21+ String username;
22+ String path;
23+ Integer port;
24+ @TableField(exist = false)
25+ OpsHostEntity host;
26+ 
27+ public enum type {
28+ PROMETHEUS,NODE_EXPORTER,OPENGAUSS_EXPORTER,
29+ ELASTICSEARCH,FILEBEAT,
30+ AGENT,
31+ PROMETHEUS_PKG,NODE_EXPORTER_PKG,OPENGAUSS_EXPORTER_PKG,
32+ ELASTICSEARCH_PKG,FILEBEAT_PKG,
33+ AGENT_PKG,
34+ }
35+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/OpsWdrEntity.java+94-0
@@ -0,0 +1,94 @@
1+package com.nctigba.observability.instance.entity;
2+ 
3+import java.util.Date;
4+import java.util.HashMap;
5+import java.util.Map;
6+ 
7+import com.baomidou.mybatisplus.annotation.FieldFill;
8+import com.baomidou.mybatisplus.annotation.TableField;
9+import com.baomidou.mybatisplus.annotation.TableId;
10+import com.baomidou.mybatisplus.annotation.TableName;
11+import com.fasterxml.jackson.annotation.JsonFormat;
12+ 
13+import lombok.Data;
14+ 
15+/**
16+ * @author lhf
17+ * @date 2022/10/13 15:03
18+ **/
19+@Data
20+@TableName("ops_wdr")
21+public class OpsWdrEntity {
22+ @TableId
23+ private String wdrId;
24+ private WdrScopeEnum scope;
25+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
26+ private Date reportAt;
27+ private WdrTypeEnum reportType;
28+ private String reportName;
29+ private String reportPath;
30+ private String clusterId;
31+ private String nodeId;
32+ private String hostId;
33+ private String userId;
34+ private String startSnapshotId;
35+ private String endSnapshotId;
36+ 
37+ /**
38+ * search value
39+ */
40+ @TableField(exist = false)
41+ private String searchValue;
42+ 
43+ /**
44+ * creator
45+ */
46+ @TableField(fill = FieldFill.INSERT)
47+ private String createBy;
48+ 
49+ /**
50+ * create time
51+ */
52+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
53+ @TableField(fill = FieldFill.INSERT)
54+ private Date createTime;
55+ 
56+ /**
57+ * updater
58+ */
59+ @TableField(fill = FieldFill.INSERT_UPDATE)
60+ private String updateBy;
61+ 
62+ /**
63+ * update time
64+ */
65+ @TableField(fill = FieldFill.INSERT_UPDATE)
66+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
67+ private Date updateTime;
68+ 
69+ /**
70+ * remark
71+ */
72+ private String remark;
73+ 
74+ @TableField(exist = false)
75+ private Map<String, Object> params;
76+ 
77+ public Map<String, Object> getParams() {
78+ if (params == null) {
79+ params = new HashMap<>(4);
80+ }
81+ return params;
82+ }
83+ 
84+ public enum WdrScopeEnum {
85+ CLUSTER,
86+ NODE;
87+ }
88+ 
89+ public enum WdrTypeEnum {
90+ DETAIL,
91+ SUMMARY,
92+ ALL;
93+ }
94+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/monitoring/NormalMonitoringHandler.java+332-304
@@ -15,7 +15,8 @@ import java.util.stream.Collectors;
15 15 
16import org.apache.commons.lang3.ObjectUtils;16import org.apache.commons.lang3.ObjectUtils;
17import org.apache.commons.lang3.StringUtils;17import org.apache.commons.lang3.StringUtils;
18-import org.springframework.beans.factory.annotation.Value;18+import org.opengauss.admin.system.plugin.facade.HostFacade;
19+import org.springframework.beans.factory.annotation.Autowired;
19import org.springframework.stereotype.Component;20import org.springframework.stereotype.Component;
20import org.springframework.util.CollectionUtils;21import org.springframework.util.CollectionUtils;
21import org.springframework.web.util.UriComponentsBuilder;22import org.springframework.web.util.UriComponentsBuilder;
@@ -24,10 +25,15 @@ import com.alibaba.fastjson.JSON;
24import com.alibaba.fastjson.JSONArray;25import com.alibaba.fastjson.JSONArray;
25import com.alibaba.fastjson.JSONObject;26import com.alibaba.fastjson.JSONObject;
26import com.alibaba.fastjson.TypeReference;27import com.alibaba.fastjson.TypeReference;
28+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
29+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
27import com.nctigba.common.web.exception.CustomException;30import com.nctigba.common.web.exception.CustomException;
28import com.nctigba.common.web.exception.CustomExceptionEnum;31import com.nctigba.common.web.exception.CustomExceptionEnum;
29import com.nctigba.observability.instance.constants.MonitoringConstants;32import com.nctigba.observability.instance.constants.MonitoringConstants;
30import com.nctigba.observability.instance.constants.MonitoringType;33import com.nctigba.observability.instance.constants.MonitoringType;
34+import com.nctigba.observability.instance.entity.NctigbaEnv;
35+import com.nctigba.observability.instance.entity.NctigbaEnv.type;
36+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
31import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;37import com.nctigba.observability.instance.model.monitoring.MonitoringMetric;
32import com.nctigba.observability.instance.model.monitoring.MonitoringParam;38import com.nctigba.observability.instance.model.monitoring.MonitoringParam;
33import com.nctigba.observability.instance.util.HttpUtils;39import com.nctigba.observability.instance.util.HttpUtils;
@@ -35,321 +41,343 @@ import com.nctigba.observability.instance.util.HttpUtils;
35import cn.hutool.core.map.MapUtil;41import cn.hutool.core.map.MapUtil;
36import lombok.extern.slf4j.Slf4j;42import lombok.extern.slf4j.Slf4j;
37 43 
38- 
39@Slf4j44@Slf4j
40@Component45@Component
41public class NormalMonitoringHandler implements MonitoringHandler {46public class NormalMonitoringHandler implements MonitoringHandler {
42- @Value("${prometheus.server.url}")47+ @Autowired
43- private String prometheusUrl;48+ protected NctigbaEnvMapper envMapper;
44- 49+ @Autowired
45- @Override50+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
46- public String getMonitorType() {51+ protected HostFacade hostFacade;
47- return MonitoringType.DEFAULT.getMonitoringType();
48- }
49 52 
50- /**53+ private String getPrometheusUrl() {
51- * Querying prometheus data for a period of time54+ var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, type.PROMETHEUS));
52- *55+ if (env == null)
53- * @param query Indicator parameters56+ throw new RuntimeException("Prometheus not found");
54- * @param start start time57+ var host = hostFacade.getById(env.getHostid());
55- * @param end End time58+ return "http://" + host.getPublicIp() + ":" + env.getPort();
56- * @param step step59+ }
57- * @return List<MonitoringMetric>
58- */
59- @Override
60- public List<MonitoringMetric> rangeQuery(String query, String start, String end, String step) {
61- List<MonitoringMetric> monitoringMetricList = null;
62- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(prometheusUrl + MonitoringConstants.PROMETHEUS_QUERY_RANGE);
63- builder.queryParam("query", query);
64- builder.queryParam("start", start);
65- builder.queryParam("end", end);
66- builder.queryParam("step", step);
67- String url = builder.build().encode().toUriString();
68- log.info("request url:[{}]", url);
69- try {
70- String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
71- JSONObject responseJson = JSONObject.parseObject(response);
72- if ("success".equals(responseJson.get("status"))) {
73- JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
74- monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
75- } else {
76- log.info("query prometheus range data failed ! please check the log, the error message is:{}", response);
77- throw new CustomException(CustomExceptionEnum.INTERNAL_SERVER_ERROR);
78- }
79- } catch (CustomException e) {
80- log.error(e.getMessage());
81- throw new CustomException("create URI failed");
82- }
83- return monitoringMetricList;
84- }
85 60 
86- /**61+ @Override
87- * Query prometheus data at a specified time62+ public String getMonitorType() {
88- *63+ return MonitoringType.DEFAULT.getMonitoringType();
89- * @param query Indicator parameters64+ }
90- * @param time Specify the timestamp. The default is the current system time of prometheus
91- * @return List<MonitoringMetric>
92- */
93- @Override
94- public List<MonitoringMetric> pointQuery(String query, String time) {
95- List<MonitoringMetric> monitoringMetricList;
96- String baseUrl = prometheusUrl + MonitoringConstants.PROMETHEUS_QUERY_POINT;
97- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
98- builder.queryParam("query", query);
99- if (StringUtils.isNotEmpty(time)) {
100- builder.queryParam("time", time);
101- }
102- String url = builder.build().encode().toUriString();
103- log.info("request url:[{}]", url);
104- try {
105- String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
106- JSONObject responseJson = JSONObject.parseObject(response);
107- if ("success".equals(responseJson.get("status"))) {
108- JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
109- monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
110- } else {
111- log.info("query prometheus range data failed ! please check the log, the error message is:{}", response);
112- throw new CustomException(CustomExceptionEnum.INTERNAL_SERVER_ERROR);
113- }
114- } catch (CustomException e) {
115- log.error(e.getMessage());
116- throw new CustomException("create URI failed");
117- }
118- return monitoringMetricList;
119- }
120 65 
121- @Override66+ /**
122- public List<Object> metricToTable(List<MonitoringMetric> metricList, MonitoringParam param) {67+ * Querying prometheus data for a period of time
123- log.info("Monitoring data starts to be converted into tabular data");68+ *
124- List<Object> tableList = null;69+ * @param query Indicator parameters
125- // Get timestamp data in descending order70+ * @param start start time
126- Map<String, List<Object>> timeMetricMap = transToTimeList(metricList);71+ * @param end End time
127- if (MapUtil.isEmpty(timeMetricMap)) {72+ * @param step step
128- log.error("prometheus to table: data is empty!");73+ * @return List<MonitoringMetric>
129- throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);74+ */
130- }75+ @Override
131- List<Object> data = new ArrayList<>();76+ public List<MonitoringMetric> rangeQuery(String query, String start, String end, String step) {
132- List<String> columnNames = new ArrayList<>();77+ List<MonitoringMetric> monitoringMetricList = null;
133- for (List<Object> timeMetricList : timeMetricMap.values()) {78+ UriComponentsBuilder builder = UriComponentsBuilder
134- List<Map<String, Object>> timeMertic = JSON.parseObject(JSON.toJSONString(timeMetricList), new TypeReference<List<Map<String, Object>>>() {79+ .fromHttpUrl(getPrometheusUrl() + MonitoringConstants.PROMETHEUS_QUERY_RANGE);
135- });80+ builder.queryParam("query", query);
136- ArrayList<ArrayList<String>> result = new ArrayList<>();81+ builder.queryParam("start", start);
137- // Record the metricData length82+ builder.queryParam("end", end);
138- int max = 0;83+ builder.queryParam("step", step);
139- for (Map<String, Object> timeMetricObj : timeMertic) {84+ String url = builder.build().encode().toUriString();
140- ArrayList<String> metricData = JSON.parseObject(timeMetricObj.get("metricData").toString(), new TypeReference<ArrayList<String>>() {85+ log.info("request url:[{}]", url);
141- });86+ try {
142- if (ObjectUtils.isNotEmpty(metricData) && metricData.size() > 0) {87+ String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
143- max = Math.max(max, metricData.size());88+ JSONObject responseJson = JSONObject.parseObject(response);
144- String title = timeMetricObj.get("metricName").toString();89+ if ("success".equals(responseJson.get("status"))) {
145- if (!columnNames.contains(title)) {90+ JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
146- columnNames.add(title);91+ monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
147- }92+ } else {
148- result.add(metricData);93+ log.info("query prometheus range data failed ! please check the log, the error message is:{}",
149- }94+ response);
150- }95+ throw new CustomException(CustomExceptionEnum.INTERNAL_SERVER_ERROR);
151- // Store the converted structure into data in turn96+ }
152- for (int i = 0; i < max; i++) {97+ } catch (CustomException e) {
153- Map<String, Object> map = new HashMap<>();98+ log.error(e.getMessage());
154- for (int j = 0; j < columnNames.size(); j++) {99+ throw new CustomException("create URI failed");
155- String title = columnNames.get(j);100+ }
156- map.put(title, result.get(j).get(i));101+ return monitoringMetricList;
157- }102+ }
158- data.add(map);
159- }
160- }
161- tableList = data;
162- // Determine whether to sort
163- if (StringUtils.isNotEmpty(param.getField())) {
164- log.info("Start of monitoring data sorting");
165- List<Map<String, String>> tableSortList = this.sortList(tableList, param);
166- JSONArray jsonArray = new JSONArray();
167- jsonArray.addAll(tableSortList);
168- log.info("Monitoring data sorting completed");
169- return jsonArray.toJavaList(Object.class);
170- }
171- log.info("Monitoring data completion converted to tabular data");
172- return tableList;
173- }
174 103 
175- private Map<String, List<Object>> transToTimeList(List<MonitoringMetric> metricList) {104+ /**
176- if (CollectionUtils.isEmpty(metricList)) {105+ * Query prometheus data at a specified time
177- log.error("transToTimeList: prometheus data is empty!");106+ *
178- throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);107+ * @param query Indicator parameters
179- }108+ * @param time Specify the timestamp. The default is the current system time of
180- // 1. Processing of original data and extracting duplicates__ name__ Value Data109+ * prometheus
181- Map<String, List<MonitoringMetric>> metricMap = new HashMap<>();110+ * @return List<MonitoringMetric>
182- for (MonitoringMetric metric : metricList) {111+ */
183- String metricName = metric.getMetric().getString("__name__");112+ @Override
184- List<MonitoringMetric> list;113+ public List<MonitoringMetric> pointQuery(String query, String time) {
185- if (metricMap.containsKey(metricName)) {114+ List<MonitoringMetric> monitoringMetricList;
186- list = metricMap.get(metricName);115+ String baseUrl = getPrometheusUrl() + MonitoringConstants.PROMETHEUS_QUERY_POINT;
187- } else {116+ UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl);
188- list = new ArrayList<>();117+ builder.queryParam("query", query);
189- }118+ if (StringUtils.isNotEmpty(time)) {
190- list.add(metric);119+ builder.queryParam("time", time);
191- metricMap.put(metricName, list);120+ }
192- }121+ String url = builder.build().encode().toUriString();
193- // 2. Process the data and return it in the form of a map. The key is a timestamp, the value is a metric list, and the timeMetricMap is used to store the processed data122+ log.info("request url:[{}]", url);
194- if (metricMap.isEmpty()) {123+ try {
195- log.error("The first processing of the raw data results in a null result!");124+ String response = HttpUtils.sendGet(url.replace("+", "%2B"), "");
196- throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);125+ JSONObject responseJson = JSONObject.parseObject(response);
197- }126+ if ("success".equals(responseJson.get("status"))) {
198- Map<String, List<Object>> timeMetricMap = new HashMap<>();127+ JSONObject dataJson = JSONObject.parseObject(responseJson.getString("data"));
199- for (List<MonitoringMetric> metrics : metricMap.values()) {128+ monitoringMetricList = JSON.parseArray(dataJson.getString("result"), MonitoringMetric.class);
200- for (MonitoringMetric metric : metrics) {129+ } else {
201- // Get the corresponding field130+ log.info("query prometheus range data failed ! please check the log, the error message is:{}",
202- String metricName = metric.getMetric().getString("__name__");131+ response);
203- String warningMsg = metric.getMetric().getString("warning_msg");132+ throw new CustomException(CustomExceptionEnum.INTERNAL_SERVER_ERROR);
204- Object metricData = JSON.parse(metric.getMetric().getString("table"));133+ }
205- JSONArray values = metric.getValues();134+ } catch (CustomException e) {
206- for (Object value : values) {135+ log.error(e.getMessage());
207- JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());136+ throw new CustomException("create URI failed");
208- List<Object> list;137+ }
209- String curTimeStamp = valueArray.get(0).toString();138+ return monitoringMetricList;
210- if (timeMetricMap.containsKey(curTimeStamp)) {139+ }
211- list = timeMetricMap.get(curTimeStamp);
212- } else {
213- list = new ArrayList<>();
214- }
215- Map<String, Object> map = new HashMap<>();
216- map.put("metricData", metricData);
217- map.put("metricName", metricName);
218- map.put("warning_msg", warningMsg);
219- list.add(map);
220- timeMetricMap.put(curTimeStamp, list);
221- }
222- }
223- }
224- // Sort in descending order
225- LinkedHashMap<String, List<Object>> result = new LinkedHashMap<>();
226- timeMetricMap.entrySet().stream()
227- .sorted((c1, c2) -> c2.getKey().compareTo(c1.getKey()))
228- .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
229- return result;
230- }
231 140 
232- @Override141+ @Override
233- public List<Object> metricToLine(List<MonitoringMetric> metricList, MonitoringParam param) {142+ public List<Object> metricToTable(List<MonitoringMetric> metricList, MonitoringParam param) {
234- log.info("Monitoring data starts to be converted into line data");143+ log.info("Monitoring data starts to be converted into tabular data");
235- if (CollectionUtils.isEmpty(metricList)) {144+ List<Object> tableList = null;
236- return Collections.singletonList(metricList);145+ // Get timestamp data in descending order
237- }146+ Map<String, List<Object>> timeMetricMap = transToTimeList(metricList);
238- List<Map<String, Object>> result = new ArrayList<>();147+ if (MapUtil.isEmpty(timeMetricMap)) {
239- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");148+ log.error("prometheus to table: data is empty!");
240- for (MonitoringMetric metric : metricList) {149+ throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);
241- // Index name150+ }
242- String metricName = metric.getMetric().getString("__name__");151+ List<Object> data = new ArrayList<>();
243- if (StringUtils.isNotBlank(param.getLegendName())) {152+ List<String> columnNames = new ArrayList<>();
244- metricName = metric.getMetric().getString(param.getLegendName());153+ for (List<Object> timeMetricList : timeMetricMap.values()) {
245- }154+ List<Map<String, Object>> timeMertic = JSON.parseObject(JSON.toJSONString(timeMetricList),
246- JSONArray lineValues = metric.getValues();155+ new TypeReference<List<Map<String, Object>>>() {
247- Map<String, Object> item = new HashMap<>();156+ });
248- // Get data of time and value157+ ArrayList<ArrayList<String>> result = new ArrayList<>();
249- List<String> timeList = new ArrayList<>();158+ // Record the metricData length
250- List<String> dataList = new ArrayList<>();159+ int max = 0;
251- for (Object value : lineValues) {160+ for (Map<String, Object> timeMetricObj : timeMertic) {
252- JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());161+ ArrayList<String> metricData = JSON.parseObject(timeMetricObj.get("metricData").toString(),
253- if (valueArray.size() > 1) {162+ new TypeReference<ArrayList<String>>() {
254- // Convert timestamp to date format163+ });
255- String time = simpleDateFormat.format(new Date(Long.parseLong(valueArray.get(0).toString()) * 1000));164+ if (ObjectUtils.isNotEmpty(metricData) && metricData.size() > 0) {
256- timeList.add(time);165+ max = Math.max(max, metricData.size());
257- dataList.add(valueArray.get(1).toString());166+ String title = timeMetricObj.get("metricName").toString();
258- }167+ if (!columnNames.contains(title)) {
259- }168+ columnNames.add(title);
169+ }
170+ result.add(metricData);
171+ }
172+ }
173+ // Store the converted structure into data in turn
174+ for (int i = 0; i < max; i++) {
175+ Map<String, Object> map = new HashMap<>();
176+ for (int j = 0; j < columnNames.size(); j++) {
177+ String title = columnNames.get(j);
178+ map.put(title, result.get(j).get(i));
179+ }
180+ data.add(map);
181+ }
182+ }
183+ tableList = data;
184+ // Determine whether to sort
185+ if (StringUtils.isNotEmpty(param.getField())) {
186+ log.info("Start of monitoring data sorting");
187+ List<Map<String, String>> tableSortList = this.sortList(tableList, param);
188+ JSONArray jsonArray = new JSONArray();
189+ jsonArray.addAll(tableSortList);
190+ log.info("Monitoring data sorting completed");
191+ return jsonArray.toJavaList(Object.class);
192+ }
193+ log.info("Monitoring data completion converted to tabular data");
194+ return tableList;
195+ }
260 196 
261- // Encapsulated into the echarts data197+ private Map<String, List<Object>> transToTimeList(List<MonitoringMetric> metricList) {
262- item.put("name", metricName);198+ if (CollectionUtils.isEmpty(metricList)) {
263- item.put("data", dataList);199+ log.error("transToTimeList: prometheus data is empty!");
264- item.put("time", timeList);200+ throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);
265- item.put("type", "line");201+ }
202+ // 1. Processing of original data and extracting duplicates__ name__ Value Data
203+ Map<String, List<MonitoringMetric>> metricMap = new HashMap<>();
204+ for (MonitoringMetric metric : metricList) {
205+ String metricName = metric.getMetric().getString("__name__");
206+ List<MonitoringMetric> list;
207+ if (metricMap.containsKey(metricName)) {
208+ list = metricMap.get(metricName);
209+ } else {
210+ list = new ArrayList<>();
211+ }
212+ list.add(metric);
213+ metricMap.put(metricName, list);
214+ }
215+ // 2. Process the data and return it in the form of a map. The key is a
216+ // timestamp, the value is a metric list, and the timeMetricMap is used to store
217+ // the processed data
218+ if (metricMap.isEmpty()) {
219+ log.error("The first processing of the raw data results in a null result!");
220+ throw new CustomException(CustomExceptionEnum.MONITORING_ACCESS_DATA_ERROR);
221+ }
222+ Map<String, List<Object>> timeMetricMap = new HashMap<>();
223+ for (List<MonitoringMetric> metrics : metricMap.values()) {
224+ for (MonitoringMetric metric : metrics) {
225+ // Get the corresponding field
226+ String metricName = metric.getMetric().getString("__name__");
227+ String warningMsg = metric.getMetric().getString("warning_msg");
228+ Object metricData = JSON.parse(metric.getMetric().getString("table"));
229+ JSONArray values = metric.getValues();
230+ for (Object value : values) {
231+ JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());
232+ List<Object> list;
233+ String curTimeStamp = valueArray.get(0).toString();
234+ if (timeMetricMap.containsKey(curTimeStamp)) {
235+ list = timeMetricMap.get(curTimeStamp);
236+ } else {
237+ list = new ArrayList<>();
238+ }
239+ Map<String, Object> map = new HashMap<>();
240+ map.put("metricData", metricData);
241+ map.put("metricName", metricName);
242+ map.put("warning_msg", warningMsg);
243+ list.add(map);
244+ timeMetricMap.put(curTimeStamp, list);
245+ }
246+ }
247+ }
248+ // Sort in descending order
249+ LinkedHashMap<String, List<Object>> result = new LinkedHashMap<>();
250+ timeMetricMap.entrySet().stream().sorted((c1, c2) -> c2.getKey().compareTo(c1.getKey()))
251+ .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
252+ return result;
253+ }
266 254 
267- result.add(item);255+ @Override
268- }256+ public List<Object> metricToLine(List<MonitoringMetric> metricList, MonitoringParam param) {
269- log.info("Monitoring data completed convert to line data");257+ log.info("Monitoring data starts to be converted into line data");
270- return Collections.singletonList(result);258+ if (CollectionUtils.isEmpty(metricList)) {
271- }259+ return Collections.singletonList(metricList);
260+ }
261+ List<Map<String, Object>> result = new ArrayList<>();
262+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
263+ for (MonitoringMetric metric : metricList) {
264+ // Index name
265+ String metricName = metric.getMetric().getString("__name__");
266+ if (StringUtils.isNotBlank(param.getLegendName())) {
267+ metricName = metric.getMetric().getString(param.getLegendName());
268+ }
269+ JSONArray lineValues = metric.getValues();
270+ Map<String, Object> item = new HashMap<>();
271+ // Get data of time and value
272+ List<String> timeList = new ArrayList<>();
273+ List<String> dataList = new ArrayList<>();
274+ for (Object value : lineValues) {
275+ JSONArray valueArray = JSONArray.parseArray(JSONObject.toJSON(value).toString());
276+ if (valueArray.size() > 1) {
277+ // Convert timestamp to date format
278+ String time = simpleDateFormat
279+ .format(new Date(Long.parseLong(valueArray.get(0).toString()) * 1000));
280+ timeList.add(time);
281+ dataList.add(valueArray.get(1).toString());
282+ }
283+ }
272 284 
273- @Override285+ // Encapsulated into the echarts data
274- public List<Map<String, String>> sortList(List<Object> tableList, MonitoringParam param) {286+ item.put("name", metricName);
275- String field = param.getField();287+ item.put("data", dataList);
276- if (StringUtils.isEmpty(field)) {288+ item.put("time", timeList);
277- log.error("field cannot be null!");289+ item.put("type", "line");
278- throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "field cannot be null!");
279- }
280- if (StringUtils.isEmpty(param.getOrder())) {
281- log.error("order cannot be null!");
282- throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "order cannot be null!");
283- }
284- if (ObjectUtils.isEmpty(tableList)) {
285- log.error("result is null!");
286- throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "result cannot be null!");
287- }
288- // Sort by field
289- List<Map<String, String>> listMapSort = JSON.parseObject(JSON.toJSONString(tableList), new TypeReference<List<Map<String, String>>>() {
290- });
291- // Sorting is divided into numerical type and time interval type
292- String firstValue = listMapSort.get(0).get(field);
293- if (firstValue.contains(":")) {
294- // Interval type
295- listMapSort = listMapSort.stream().sorted((x, y) -> (int) (resolutionInterval(y.get(field)) - resolutionInterval(x.get(field)))
296- ).collect(Collectors.toList());
297 290 
298- } else {291+ result.add(item);
299- // Digital292+ }
300- listMapSort = listMapSort.stream().sorted((x, y) ->293+ log.info("Monitoring data completed convert to line data");
301- BigDecimal.valueOf(Double.parseDouble(y.get(field))).compareTo(BigDecimal.valueOf(Double.parseDouble(x.get(field))))294+ return Collections.singletonList(result);
302- ).collect(Collectors.toList());295+ }
303- }
304- // De duplication according to filter
305- if (StringUtils.isNotEmpty(param.getFilter())) {
306- listMapSort = distinctByKey(listMapSort, param.getFilter());
307- }
308- // Intercept the first 10 lines of listMapSort
309- int listMapSortLength = listMapSort.size();
310- if (listMapSortLength <= 10) {
311- return listMapSort;
312- }
313- // Desc in reverse order, the default is positive order
314- if (!"desc".equalsIgnoreCase(param.getOrder())) {
315- Collections.reverse(listMapSort);
316- }
317- log.info("monitoring data sort finish! sortType:{}", param.getOrder());
318- return listMapSort.subList(0, 10);
319- }
320 296 
321- private List<Map<String, String>> distinctByKey(List<Map<String, String>> listMapSort, String filter) {297+ @Override
322- Set<String> set = new HashSet<>();298+ public List<Map<String, String>> sortList(List<Object> tableList, MonitoringParam param) {
323- List<Map<String, String>> newListMapSort = new ArrayList<>();299+ String field = param.getField();
324- for (Map<String, String> stringStringMap : listMapSort) {300+ if (StringUtils.isEmpty(field)) {
325- String text = stringStringMap.get(filter);301+ log.error("field cannot be null!");
326- if (!set.contains(text)) {302+ throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "field cannot be null!");
327- set.add(text);303+ }
328- newListMapSort.add(stringStringMap);304+ if (StringUtils.isEmpty(param.getOrder())) {
329- }305+ log.error("order cannot be null!");
330- }306+ throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "order cannot be null!");
331- log.info("monitoring data distinct finish! distinct filed:{}", filter);307+ }
332- return newListMapSort;308+ if (ObjectUtils.isEmpty(tableList)) {
333- }309+ log.error("result is null!");
310+ throw new CustomException(CustomExceptionEnum.PARAM_INVALID_ERROR, "result cannot be null!");
311+ }
312+ // Sort by field
313+ List<Map<String, String>> listMapSort = JSON.parseObject(JSON.toJSONString(tableList),
314+ new TypeReference<List<Map<String, String>>>() {
315+ });
316+ // Sorting is divided into numerical type and time interval type
317+ String firstValue = listMapSort.get(0).get(field);
318+ if (firstValue.contains(":")) {
319+ // Interval type
320+ listMapSort = listMapSort.stream()
321+ .sorted((x, y) -> (int) (resolutionInterval(y.get(field)) - resolutionInterval(x.get(field))))
322+ .collect(Collectors.toList());
334 323 
335- private long resolutionInterval(String interval) {324+ } else {
336- if (StringUtils.isEmpty(interval)) {325+ // Digital
337- return 0;326+ listMapSort = listMapSort.stream()
338- }327+ .sorted((x, y) -> BigDecimal.valueOf(Double.parseDouble(y.get(field)))
339- long stamp = 0;328+ .compareTo(BigDecimal.valueOf(Double.parseDouble(x.get(field)))))
340- String time = interval;329+ .collect(Collectors.toList());
341- if (interval.contains(",")) {330+ }
342- String[] intervalArr = interval.split(",");331+ // De duplication according to filter
343- String timeDay = intervalArr[0].trim();332+ if (StringUtils.isNotEmpty(param.getFilter())) {
344- time = intervalArr[1].trim();333+ listMapSort = distinctByKey(listMapSort, param.getFilter());
345- long timeDayNumber = Long.parseLong(timeDay.substring(0, timeDay.indexOf("day")).trim());334+ }
346- stamp = stamp + timeDayNumber * 86400;335+ // Intercept the first 10 lines of listMapSort
347- }336+ int listMapSortLength = listMapSort.size();
348- if (interval.contains(".")) {337+ if (listMapSortLength <= 10) {
349- String[] timeArr = time.split(".")[0].split(":");338+ return listMapSort;
350- int timeCount = Integer.parseInt(timeArr[0]) * 3600 + Integer.parseInt(timeArr[1]) * 60 + Integer.parseInt(timeArr[2]);339+ }
351- stamp = stamp + timeCount;340+ // Desc in reverse order, the default is positive order
352- }341+ if (!"desc".equalsIgnoreCase(param.getOrder())) {
353- return stamp;342+ Collections.reverse(listMapSort);
354- }343+ }
344+ log.info("monitoring data sort finish! sortType:{}", param.getOrder());
345+ return listMapSort.subList(0, 10);
346+ }
347+ 
348+ private List<Map<String, String>> distinctByKey(List<Map<String, String>> listMapSort, String filter) {
349+ Set<String> set = new HashSet<>();
350+ List<Map<String, String>> newListMapSort = new ArrayList<>();
351+ for (Map<String, String> stringStringMap : listMapSort) {
352+ String text = stringStringMap.get(filter);
353+ if (!set.contains(text)) {
354+ set.add(text);
355+ newListMapSort.add(stringStringMap);
356+ }
357+ }
358+ log.info("monitoring data distinct finish! distinct filed:{}", filter);
359+ return newListMapSort;
360+ }
361+ 
362+ private long resolutionInterval(String interval) {
363+ if (StringUtils.isEmpty(interval)) {
364+ return 0;
365+ }
366+ long stamp = 0;
367+ String time = interval;
368+ if (interval.contains(",")) {
369+ String[] intervalArr = interval.split(",");
370+ String timeDay = intervalArr[0].trim();
371+ time = intervalArr[1].trim();
372+ long timeDayNumber = Long.parseLong(timeDay.substring(0, timeDay.indexOf("day")).trim());
373+ stamp = stamp + timeDayNumber * 86400;
374+ }
375+ if (interval.contains(".")) {
376+ String[] timeArr = time.split(".")[0].split(":");
377+ int timeCount = Integer.parseInt(timeArr[0]) * 3600 + Integer.parseInt(timeArr[1]) * 60
378+ + Integer.parseInt(timeArr[2]);
379+ stamp = stamp + timeCount;
380+ }
381+ return stamp;
382+ }
355}383}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/listener/PluginListener.java+1-1
@@ -18,10 +18,10 @@ import org.springframework.context.event.ContextRefreshedEvent;
18 * @date: 2022/12/4 15:0518 * @date: 2022/12/4 15:05
19 */19 */
20public class PluginListener implements ApplicationListener<ApplicationEvent> {20public class PluginListener implements ApplicationListener<ApplicationEvent> {
21+ public static final String pluginId = "observability-instance";
21 22 
22 @Override23 @Override
23 public void onApplicationEvent(ApplicationEvent event) {24 public void onApplicationEvent(ApplicationEvent event) {
24- String pluginId = "observability-instance";
25 if (event instanceof ApplicationEnvironmentPreparedEvent) {25 if (event instanceof ApplicationEnvironmentPreparedEvent) {
26 } else if (event instanceof ApplicationPreparedEvent) {26 } else if (event instanceof ApplicationPreparedEvent) {
27 } else if (event instanceof ContextRefreshedEvent) {27 } else if (event instanceof ContextRefreshedEvent) {
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/NctigbaEnvMapper.java+10-0
@@ -0,0 +1,10 @@
1+package com.nctigba.observability.instance.mapper;
2+ 
3+import org.apache.ibatis.annotations.Mapper;
4+ 
5+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
6+import com.nctigba.observability.instance.entity.NctigbaEnv;
7+ 
8+@Mapper
9+public interface NctigbaEnvMapper extends BaseMapper<NctigbaEnv>{
10+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/OpsWdrMapper.java+10-0
@@ -0,0 +1,10 @@
1+package com.nctigba.observability.instance.mapper;
2+ 
3+import org.apache.ibatis.annotations.Mapper;
4+ 
5+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
6+import com.nctigba.observability.instance.entity.OpsWdrEntity;
7+ 
8+@Mapper
9+public interface OpsWdrMapper extends BaseMapper<OpsWdrEntity> {
10+}
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ServerInfoMapper.java+1-2
@@ -14,5 +14,4 @@ import com.nctigba.observability.instance.entity.ServerInfoEntity;
14 */14 */
15@Mapper15@Mapper
16public interface ServerInfoMapper extends BaseMapper<ServerInfoEntity> {16public interface ServerInfoMapper extends BaseMapper<ServerInfoEntity> {
17- 17+}
18-}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/WdrGeneratorBody.java+24-0
@@ -0,0 +1,24 @@
1+package com.nctigba.observability.instance.model;
2+ 
3+import javax.validation.constraints.NotBlank;
4+import javax.validation.constraints.NotNull;
5+ 
6+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
7+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrTypeEnum;
8+ 
9+import lombok.Data;
10+ 
11+@Data
12+public class WdrGeneratorBody {
13+ @NotBlank(message = "The cluster ID cannot be empty")
14+ private String clusterId;
15+ @NotNull(message = "The report scope cannot be empty")
16+ private WdrScopeEnum scope;
17+ private String hostId;
18+ @NotNull(message = "The report type cannot be empty")
19+ private WdrTypeEnum type;
20+ @NotNull(message = "start Snapshot ID cannot be empty")
21+ private String startId;
22+ @NotNull(message = "end Snapshot ID cannot be empty")
23+ private String endId;
24+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/WdrSnapshotVO.java+20-0
@@ -0,0 +1,20 @@
1+package com.nctigba.observability.instance.model;
2+ 
3+import java.util.Date;
4+ 
5+import com.baomidou.mybatisplus.annotation.TableField;
6+import com.baomidou.mybatisplus.annotation.TableId;
7+import com.baomidou.mybatisplus.annotation.TableName;
8+ 
9+import lombok.Data;
10+ 
11+@Data
12+@TableName(value = "snapshot.snapshot", autoResultMap = true)
13+public class WdrSnapshotVO {
14+ @TableId("snapshot_id")
15+ private Integer snapshotId;
16+ @TableField("start_ts")
17+ private Date startTs;
18+ @TableField("end_ts")
19+ private Date endTs;
20+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/DatabaseParamData.java+58-0
@@ -0,0 +1,58 @@
1+package com.nctigba.observability.instance.model.param;
2+ 
3+import lombok.Getter;
4+import lombok.NoArgsConstructor;
5+ 
6+@Getter
7+@NoArgsConstructor
8+public enum DatabaseParamData {
9+ MaxProcessMemory("数据库","max_process_memory","设置一个数据库节点可用的最大物理内存",
10+ "2*1024*1024~INT_MAX","12582912","KB","数据库节点上该数值需要根据系统物理内存及单节点部署主数据库节点个数决定"),
11+ WorkMem("数据库","work_mem","",
12+ "","","KB","判断执行作业可下盘算子是否已使用内存量触发下盘点"),
13+ PagewriterSleep("数据库","pagewriter_sleep","",
14+ "0~3600000","2000ms","毫秒","设置用于增量检查点打开后,pagewrite线程每隔pagewriter_sleep的时间刷一批脏页下盘"),
15+ BgwriterDelay("数据库","bgwriter_delay","",
16+ "10~10000","2s","毫秒","设置后端写进程写“脏”共享缓冲区之间的时间间隔"),
17+ BgwriterThreadNum("数据库","bgwriter_thread_num","",
18+ "","","",""),
19+ MaxIoCapacity("数据库","max_io_capacity","设置后端写进程批量刷页每秒的IO上限",
20+ "30720~10485760","512000","KB",""),
21+ LogMinDurationStatement("数据库","log_min_duration_statement","当某条语句的持续时间大于或者等于特定的毫秒数时,log_min_duration_statement参数用于控制记录每条完成语句的持续时间",
22+ "","30min","毫秒",""),
23+ LogDuration("数据库","log_duration","控制记录每个已完成SQL语句的执行时间",
24+ "","on","布尔型",""),
25+ TrackStmtStatLevel("数据库","track_stmt_stat_level","控制语句执行跟踪的级别",
26+ "","OFF,L0","字符型",""),
27+ TrackStmtRetentionTime("数据库","track_stmt_retention_time","组合参数,控制全量/慢SQL记录的保留时间",
28+ "","3600,604800","字符型",""),
29+ EnableThreadPool("数据库","enable_thread_pool","控制是否使用线程池功能",
30+ "","off","布尔型",""),
31+ ThreadPoolAttr("数据库","thread_pool_attr","用于控制线程池功能的详细属性",
32+ "","16, 2, (nobind)","字符型",""),
33+ LogStatement("数据库","log_statement","控制记录SQL语句",
34+ "","none","枚举类型",""),
35+ LogErrorVerbosity("数据库","log_error_verbosity","控制服务器日志中每条记录的消息写入的详细度",
36+ "","default","枚举类型",""),
37+ LogMinMessages("数据库","log_min_messages","控制写到服务器日志文件中的消息级别",
38+ "","warning","枚举类型",""),
39+ LogMinErrorStatement("数据库","log_min_error_statement","控制在服务器日志中记录错误的SQL语句",
40+ "","error","枚举类型","");
41+ private String classify;
42+ private String paramName;
43+ private String paramDetail;
44+ private String suggestValue;
45+ private String defaultValue;
46+ private String unit;
47+ private String suggestExplain;
48+ 
49+ DatabaseParamData(String classify, String paramName, String paramDetail, String suggestValue, String defaultValue, String unit, String suggestExplain) {
50+ this.classify=classify;
51+ this.paramName=paramName;
52+ this.paramDetail=paramDetail;
53+ this.suggestValue=suggestValue;
54+ this.defaultValue=defaultValue;
55+ this.unit=unit;
56+ this.suggestExplain=suggestExplain;
57+ }
58+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/OsParamData.java+93-0
@@ -0,0 +1,93 @@
1+package com.nctigba.observability.instance.model.param;
2+ 
3+import lombok.Getter;
4+import lombok.NoArgsConstructor;
5+ 
6+@Getter
7+@NoArgsConstructor
8+public enum OsParamData {
9+ tcpMaxTwBuckets("操作系统","net.ipv4.tcp_max_tw_buckets","表示同时保持TIME_WAIT状态的TCP/IP连接最大数量。如果超过所配置的取值,TIME_WAIT将立刻被释放并打印警告信息。",
10+ "10000","180000","数目","系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。(事实上做NAT的时候最好可以适当地增加该值)"),
11+ tcpTwReuse("操作系统","net.ipv4.tcp_tw_reuse","允许将TIME-WAIT状态的sockets重新用于新的TCP连接。",
12+ "1","0","布尔值","表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接(这个对快速重启动某些服务,而启动后提示端口已经被使用的情形非常有帮助)"),
13+ tcpTwRecycle("操作系统","net.ipv4.tcp_tw_recycle","表示开启TCP连接中TIME-WAIT状态sockets的快速回收。",
14+ "1","0","布尔值","打开快速 TIME-WAIT sockets 回收。除非得到技术专家的建议或要求﹐请不要随意修改这个值。(做NAT的时候,建议打开它)"),
15+ tcpKeepaliveTime("操作系统","net.ipv4.tcp_keepalive_time","表示当keepalive启用的时候,TCP发送keepalive消息的频度。",
16+ "30","7200","秒","TCP发送keepalive探测消息的间隔时间(秒),用于确认TCP连接是否有效。防止两边建立连接但不发送数据的攻击"),
17+ tcpKeepaliveProbes("操作系统","net.ipv4.tcp_keepalive_probes","在认定连接失效之前,发送TCP的keepalive探测包数量。这个值乘以tcp_keepalive_intvl之后决定了一个连接发送了keepalive之后可以有多少时间没有回应。",
18+ "9","9","秒","TCP发送keepalive探测消息的间隔时间(秒),用于确认TCP连接是否有效"),
19+ tcpKeepaliveIntvl("操作系统","net.ipv4.tcp_keepalive_intvl","当探测没有确认时,重新发送探测的频度。",
20+ "30","75","秒","探测消息未获得响应时,重发该消息的间隔时间(秒)。默认值为75秒。 (对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值)"),
21+ tcpRetries1("操作系统","net.ipv4.tcp_retries1","在连接建立过程中TCP协议最大重试次数。",
22+ "5","3","数目","放弃回应一个TCP连接请求前﹐需要进行多少次重试。RFC规定最低的数值是3"),
23+ tcpSynRetries("操作系统","net.ipv4.tcp_syn_retries","TCP协议SYN报文最大重试次数。",
24+ "5","5","数目","对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1决定的)"),
25+ tcpSynackRetries("操作系统","net.ipv4.tcp_synack_retries","TCP协议SYN应答报文最大重试次数。",
26+ "5","5","数目","对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间"),
27+ tcpRetries2("操作系统","net.ipv4.tcp_retries2","控制内核向已经建立连接的远程主机重新发送数据的次数,低值可以更早的检测到与远程主机失效的连接,因此服务器可以更快的释放该连接。",
28+ "12","15","数目","在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒).(这个值根据目前的网络设置,可以适当地改小,我的网络内修改为了5)"),
29+ overcommitMemory("操作系统","vm.overcommit_memory","控制在做内存分配的时候,内核的检查方式。",
30+ "0","0","字典值","vm.overcommit_memory文件指定了内核针对内存分配的策略,其值可以是0、1、2\n" +
31+ "0: (默认)表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。0 即是启发式的overcommitting handle,会尽量减少swap的使用,root可以分配比一般用户略多的内存\n" +
32+ "1: 表示内核允许分配所有的物理内存,而不管当前的内存状态如何,允许超过CommitLimit,直至内存用完为止。在数据库服务器上不建议设置为1,从而尽量避免使用swap.\n" +
33+ "2: 表示不允许超过CommitLimit值"),
34+ tcpRmem("操作系统","net.ipv4.tcp_rmem","TCP协议接收端缓冲区的可用内存大小。分无压力、有压力、和压力大三个区间,单位为页面。",
35+ "8192 250000 16777216","409687380174760(4k)","字节","接收缓存设置同tcp_wmem"),
36+ tcpWmem("操作系统","net.ipv4.tcp_wmem","TCP协议发送端缓冲区的可用内存大小。分无压力、有压力、和压力大三个区间,单位为页面。",
37+ "8192 250000 16777216","409616384131072(4k)","字节","发送缓存设置min:为TCP socket预留用于发送缓冲的内存最小值。"),
38+ wmemMax("操作系统","net.core.wmem_max","socket发送端缓冲区大小的最大值。",
39+ "21299200","129024","字节","最大的TCP数据发送缓冲"),
40+ rmemMax("操作系统","net.core.rmem_max","socket接收端缓冲区大小的最大值。",
41+ "21299200","129024","字节","最大的TCP数据接收缓冲"),
42+ wmemDefault("操作系统","net.core.wmem_default","socket发送端缓冲区大小的默认值。",
43+ "21299200","129024","字节","默认的发送窗口大小"),
44+ rmemDefault("操作系统","net.core.rmem_default","socket接收端缓冲区大小的默认值。",
45+ "21299200","129024","字节","默认的接收窗口大小"),
46+ ipLocalPortRange("操作系统","net.ipv4.ip_local_port_range","物理机可用临时端口范围。",
47+ "26000-65535","3276861000","字节","表示用于向外连接的端口范围,默认比较小,这个范围同样会间接用于NAT表规模"),
48+ sem("操作系统","kernel.sem","内核信号量参数设置大小。",
49+ "250 6400000 1000 25600","250 32000 32 128","字节",""),
50+ minFreeKbytes("操作系统","vm.min_free_kbytes","保证物理内存有足够空闲空间,防止突发性换页。",
51+ "系统总内存的5%","724","字节",""),
52+ somaxconn("操作系统","net.core.somaxconn","定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数。",
53+ "65535","128","数目","用来限制监听(LISTEN)队列最大数据包的数量,超过这个数量就会导致链接超时或者触发重传机制。web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。对繁忙的服务器,增加该值有助于网络性能"),
54+ tcpSyncookies("操作系统","net.ipv4.tcp_syncookies","当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击。",
55+ "1","0","布尔值","只有在内核编译时选择了CONFIG_SYNCOOKIES时才会发生作用。当出现syn等候队列出现溢出时象对方发送syncookies。目的是为了防止syn flood攻击"),
56+ netdevMaxBacklog("操作系统","net.core.netdev_max_backlog","在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。",
57+ "65535","1000","数目","队列长度"),
58+ tcpMaxSynBacklog("操作系统","net.ipv4.tcp_max_syn_backlog","记录的那些尚未收到客户端确认信息的连接请求的最大值。",
59+ "65535","1024","数目","对于那些依然还未获得客户端确认的连接请求﹐需要保存在队列中最大数目。对于超过 128Mb 内存的系统﹐默认值是 1024 ﹐低于 128Mb 的则为 128。如果服务器经常出现过载﹐可以尝试增加这个数字。警告﹗假如您将此值设为大于 1024﹐最好修改include/net/tcp.h里面的TCP_SYNQ_HSIZE﹐以保持TCP_SYNQ_HSIZE*16(SYN Flood攻击利用TCP协议散布握手的缺陷,伪造虚假源IP地址发送大量TCP-SYN半打开连接到目标系统,最终导致目标系统Socket队列资源耗尽而无法接受新的连接。为了应付这种攻击,现代Unix系统中普遍采用多连接队列处理的方式来缓冲(而不是解决)这种攻击,是用一个基本队列处理正常的完全连接应用(Connect()和Accept() ),是用另一个队列单独存放半打开连接。这种双队列处理方式和其他一些系统内核措施(例如Syn-Cookies/Caches)联合应用时,能够比较有效的缓解小规模的SYN Flood攻击(事实证明)"),
60+ tcpFinTimeout("操作系统","net.ipv4.tcp_fin_timeout","系统默认的超时时间。",
61+ "60","60","秒","对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡"),
62+ shmall("操作系统","kernel.shmall","内核可用的共享内存总量。",
63+ "1152921504606840000","2097152","字节",""),
64+ shmmax("操作系统","kernel.shmmax","内核参数定义单个共享内存段的最大值。",
65+ "18446744073709500000","33554432","字节",""),
66+ tcpSack("操作系统","net.ipv4.tcp_sack","启用有选择的应答,通过有选择地应答乱序接受到的报文来提高性能,让发送者只发送丢失的报文段(对于广域网来说)这个选项应该启用,但是会增加对CPU的占用。",
67+ "1","1","布尔值","使用 Selective ACK﹐它可以用来查找特定的遗失的数据报--- 因此有助于快速恢复状态。该文件表示是否启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段)。(对于广域网通信来说这个选项应该启用,但是这会增加对 CPU 的占用"),
68+ tcpTimestamps("操作系统","net.ipv4.tcp_timestamps","TCP时间戳(会在TCP包头增加12节),以一种比重发超时更精确的方式(参考RFC 1323)来启用对RTT的计算,启用可以实现更好的性能。",
69+ "1","1","布尔值","Timestamps 用在其它一些东西中﹐可以防范那些伪造的sequence 号码。一条1G的宽带线路或许会重遇到带 out-of-line数值的旧sequence 号码(假如它是由于上次产生的)。Timestamp 会让它知道这是个 '旧封包'。(该文件表示是否启用以一种比超时重发更精确的方法(RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。)"),
70+ extfragThreshold("操作系统","vm.extfrag_threshold","系统内存不够用时,linux会为当前系统内存碎片情况打分,如果超过vm.extfrag_threshold的值,kswapd就会触发memory compaction。所以这个值设置的接近1000,说明系统在内存碎片的处理倾向于把旧的页换出,以符合申请的需要,而设置接近0,表示系统在内存碎片的处理倾向做memory compaction。",
71+ "500","500","",""),
72+ overcommitRatio("操作系统","vm.overcommit_ratio","系统使用绝不过量使用内存的算法时,系统整个内存地址空间不得超过swap+RAM值的此参数百分比,当vm.overcommit_memory=2时此参数生效。",
73+ "90","50","百分数","这个参数值只有在vm.overcommit_memory=2的情况下,这个参数才会生效"),
74+ mtu("操作系统","MTU","节点网卡最大传输单元。OS默认值为1500,调整为8192可以提升SCTP协议数据收发的性能。",
75+ "8192","1500","bytes","");
76+ private String classify;
77+ private String paramName;
78+ private String paramDetail;
79+ private String suggestValue;
80+ private String defaultValue;
81+ private String unit;
82+ private String suggestExplain;
83+ 
84+ OsParamData(String classify, String paramName, String paramDetail, String suggestValue, String defaultValue, String unit, String suggestExplain) {
85+ this.classify=classify;
86+ this.paramName=paramName;
87+ this.paramDetail=paramDetail;
88+ this.suggestValue=suggestValue;
89+ this.defaultValue=defaultValue;
90+ this.unit=unit;
91+ this.suggestExplain=suggestExplain;
92+ }
93+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/ParamQuery.java+14-0
@@ -0,0 +1,14 @@
1+package com.nctigba.observability.instance.model.param;
2+ 
3+import io.swagger.annotations.ApiModelProperty;
4+import lombok.Data;
5+ 
6+@Data
7+public class ParamQuery {
8+ @ApiModelProperty("Param Name")
9+ private String paramName;
10+ @ApiModelProperty("nodeId")
11+ private String nodeId;
12+ @ApiModelProperty("password")
13+ private String password;
14+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/AbstractInstaller.java+106-0
@@ -0,0 +1,106 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.net.http.WebSocket;
4+import java.util.List;
5+ 
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
7+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
8+import org.opengauss.admin.common.core.domain.model.ops.HostUserBody;
9+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
10+import org.opengauss.admin.common.utils.ops.WsUtil;
11+import org.opengauss.admin.system.plugin.facade.HostFacade;
12+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
13+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
14+import org.springframework.beans.factory.annotation.Autowired;
15+ 
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import com.gitee.starblues.bootstrap.annotation.AutowiredType.Type;
18+import com.nctigba.observability.instance.mapper.NctigbaEnvMapper;
19+import com.nctigba.observability.instance.service.AbstractInstaller.Step.status;
20+ 
21+import cn.hutool.core.util.StrUtil;
22+import cn.hutool.json.JSONUtil;
23+import lombok.Data;
24+import lombok.NoArgsConstructor;
25+ 
26+public abstract class AbstractInstaller {
27+ protected static final String TAR = ".tar.gz";
28+ protected static final String ZIP = ".zip";
29+ @Autowired
30+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
31+ protected HostFacade hostFacade;
32+ @Autowired
33+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
34+ protected EncryptionUtils encryptionUtils;
35+ @Autowired
36+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
37+ protected HostUserFacade hostUserFacade;
38+ @Autowired
39+ protected NctigbaEnvMapper envMapper;
40+ @Autowired
41+ @AutowiredType(Type.PLUGIN_MAIN)
42+ protected WsUtil wsUtil;
43+ 
44+ protected String arch(String str) {
45+ return "aarch64".equals(str) ? "arm64" : "amd64";
46+ }
47+ 
48+ protected OpsHostUserEntity getUser(OpsHostEntity hostEntity, String username, String rootPassword) {
49+ var user = hostUserFacade.listHostUserByHostId(hostEntity.getHostId()).stream().filter(e -> {
50+ return username.equals(e.getUsername());
51+ }).findFirst().orElse(null);
52+ if (user == null && rootPassword != null) {
53+ var body = new HostUserBody();
54+ body.setHostId(hostEntity.getHostId());
55+ body.setPassword(encryptionUtils.encrypt(StrUtil.uuid()));
56+ body.setRootPassword(rootPassword);
57+ body.setUsername(username);
58+ hostUserFacade.add(body);
59+ user = hostUserFacade.listHostUserByHostId(hostEntity.getHostId()).stream().filter(e -> {
60+ return username.equals(e.getUsername());
61+ }).findFirst().orElse(null);
62+ }
63+ if (user == null)
64+ throw new RuntimeException("user not found");
65+ return user;
66+ }
67+ 
68+ /**
69+ * change current step to {@code DONE} and next step to {@code DOING}, send to
70+ * {@link WebSocket}
71+ */
72+ protected int nextStep(WsSession wsSession, List<Step> steps, int curr) {
73+ steps.get(curr).setState(status.DONE);
74+ curr++;
75+ sendMsg(wsSession, steps, curr, status.DOING);
76+ return curr;
77+ }
78+ 
79+ /**
80+ * change current step to {@code DONE}, send to {@link WebSocket}
81+ */
82+ protected synchronized void sendMsg(WsSession wsSession, List<Step> steps, int curr, status state) {
83+ steps.get(curr).setState(state);
84+ wsUtil.sendText(wsSession, JSONUtil.toJsonStr(steps));
85+ }
86+ 
87+ @Data
88+ @NoArgsConstructor
89+ public static class Step {
90+ String name;
91+ status state = status.TODO;
92+ String msg;
93+ 
94+ public Step(String name) {
95+ this.name = name;
96+ }
97+ 
98+ public enum status {
99+ TODO,
100+ DOING,
101+ DONE,
102+ SKIP,
103+ ERROR
104+ }
105+ }
106+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterOpsProvider.java+26-0
@@ -0,0 +1,26 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
8+ 
9+import com.jcraft.jsch.Session;
10+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
11+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
12+ 
13+/**
14+ * Cluster Installation Service Provider Specification
15+ *
16+ * @author lhf
17+ * @date 2022/8/12 09:09
18+ **/
19+public interface ClusterOpsProvider {
20+ OpenGaussVersionEnum version();
21+ 
22+ OpenGaussSupportOSEnum os();
23+ 
24+ void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
25+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath);
26+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterOpsProviderManager.java+65-0
@@ -0,0 +1,65 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.util.Optional;
4+import java.util.concurrent.ConcurrentHashMap;
5+ 
6+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
7+import org.springframework.stereotype.Component;
8+ 
9+import lombok.AllArgsConstructor;
10+import lombok.Getter;
11+ 
12+@Component
13+public class ClusterOpsProviderManager {
14+ private static final ConcurrentHashMap<String, ClusterOpsProvider> REGISTRY = new ConcurrentHashMap<>();
15+ 
16+ public static void registry(OpenGaussVersionEnum version, ClusterOpsProvider provider) {
17+ registry(version, OpenGaussSupportOSEnum.CENTOS_X86_64, provider);
18+ 
19+ }
20+ 
21+ public static void registry(OpenGaussVersionEnum version, OpenGaussSupportOSEnum os, ClusterOpsProvider provider) {
22+ REGISTRY.put(os.name() + version.name(), provider);
23+ }
24+ 
25+ public Optional<ClusterOpsProvider> provider(OpenGaussVersionEnum version, OpenGaussSupportOSEnum os) {
26+ if (os == null) {
27+ os = OpenGaussSupportOSEnum.CENTOS_X86_64;
28+ }
29+ return Optional.ofNullable(REGISTRY.get(os.name() + version.name()));
30+ }
31+ 
32+ @Getter
33+ @AllArgsConstructor
34+ public enum OpenGaussSupportOSEnum {
35+ CENTOS_X86_64("centos", "openGauss-3.0.0-CentOS-64bit.tar.bz2",
36+ "https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.0.0/x86/openGauss-3.0.0-CentOS-64bit.tar.bz2"),
37+ OPENEULER_ARCH64("openEuler", "openGauss-3.0.0-openEuler-64bit.tar.bz2",
38+ "https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.0.0/arm/openGauss-3.0.0-openEuler-64bit.tar.bz2"),
39+ OPENEULER_X86_64("openEuler", "openGauss-3.0.0-openEuler-64bit.tar.bz2",
40+ "https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.0.0/x86_openEuler/openGauss-3.0.0-openEuler-64bit.tar.bz2");
41+ 
42+ private String osId;
43+ private String installPackageName;
44+ private String installPackageResourceUrl;
45+ 
46+ public static OpenGaussSupportOSEnum of(String osInfo, String osVersionInfo, String cpuArchInfo) {
47+ if ("centos".equalsIgnoreCase(osInfo) && "x86_64".equalsIgnoreCase(cpuArchInfo)) {
48+ return CENTOS_X86_64;
49+ }
50+ 
51+ if ("openEuler".equalsIgnoreCase(osInfo) && "aarch64".equalsIgnoreCase(cpuArchInfo)) {
52+ return OPENEULER_ARCH64;
53+ }
54+ 
55+ if ("openEuler".equalsIgnoreCase(osInfo) && "x86_64".equalsIgnoreCase(cpuArchInfo)) {
56+ return OPENEULER_X86_64;
57+ }
58+ return CENTOS_X86_64;
59+ }
60+ 
61+ public boolean match(String os) {
62+ return this.getOsId().equalsIgnoreCase(os);
63+ }
64+ }
65+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ExporterService.java+185-0
@@ -0,0 +1,185 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.io.File;
4+import java.io.FileOutputStream;
5+import java.io.IOException;
6+import java.text.MessageFormat;
7+import java.util.Arrays;
8+import java.util.Map;
9+ 
10+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
11+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
12+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.core.io.ResourceLoader;
15+import org.springframework.stereotype.Service;
16+ 
17+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
18+import com.nctigba.observability.instance.entity.NctigbaEnv;
19+import com.nctigba.observability.instance.entity.NctigbaEnv.type;
20+import com.nctigba.observability.instance.service.AbstractInstaller.Step.status;
21+import com.nctigba.observability.instance.service.ClusterManager.OpsClusterNodeVOSub;
22+import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig;
23+import com.nctigba.observability.instance.service.PrometheusService.prometheusConfig.job;
24+import com.nctigba.observability.instance.util.SshSession;
25+import com.nctigba.observability.instance.util.SshSession.command;
26+import com.nctigba.observability.instance.util.YamlUtil;
27+ 
28+import cn.hutool.core.io.FileUtil;
29+import cn.hutool.core.io.IoUtil;
30+import cn.hutool.core.util.URLUtil;
31+import cn.hutool.http.HttpUtil;
32+import cn.hutool.json.JSONUtil;
33+ 
34+@Service
35+public class ExporterService extends AbstractInstaller {
36+ private static final String EXPORTER_USER = "exporters";
37+ private static final String NODE_EXPORTER_PATH = "https://github.com/prometheus/node_exporter/releases/download/v1.3.1/";
38+ private static final String NODE_EXPORTER_NAME = "node_exporter-1.3.1.linux-";
39+ private static final String OPENGAUSS_EXPORTER_PATH = "https://gitee.com/opengauss/openGauss-prometheus-exporter/releases/download/v1.0.0/";
40+ private static final String OPENGAUSS_EXPORTER_NAME = "opengauss_exporter_1.0.0_linux_";
41+ 
42+ @Autowired
43+ private ClusterManager clusterManager;
44+ @Autowired
45+ private ResourceLoader loader;
46+ 
47+ public void install(WsSession wsSession, String nodeId, String rootPassword) {
48+ // @formatter:off
49+ var steps = Arrays.asList(
50+ new Step("初始化"),
51+ new Step("检查prometheus环境存在"),
52+ new Step("连接主机"),
53+ new Step("检查安装用户"),
54+ new Step("安装nodeExporter"),
55+ new Step("安装opengaussExporter"),
56+ new Step("刷新prometheus配置"),
57+ new Step("安装完成"));
58+ // @formatter:on
59+ int curr = 0;
60+ 
61+ curr = nextStep(wsSession, steps, curr);
62+ var promEnv = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, type.PROMETHEUS));
63+ if (promEnv == null)
64+ throw new RuntimeException("prometheus not exists");
65+ 
66+ curr = nextStep(wsSession, steps, curr);
67+ var node = clusterManager.getOpsNodeById(nodeId);
68+ if (node == null)
69+ throw new RuntimeException("node not found");
70+ curr = nextStep(wsSession, steps, curr);
71+ var hostId = node.getHostId();
72+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
73+ if (hostEntity == null)
74+ throw new RuntimeException("host not found");
75+ var user = getUser(hostEntity, EXPORTER_USER, rootPassword);
76+ try (var session = SshSession.connect(hostEntity.getPublicIp(), hostEntity.getPort(), EXPORTER_USER,
77+ encryptionUtils.decrypt(user.getPassword()));) {
78+ 
79+ curr = nextStep(wsSession, steps, curr);
80+ var nodeEnv = nodeExporter(hostId, user, session);
81+ 
82+ curr = nextStep(wsSession, steps, curr);
83+ var gaussEnv = opengaussExporter(node, hostId, hostEntity, user, session);
84+ 
85+ curr = nextStep(wsSession, steps, curr);
86+ // 修改prometheus,重启
87+ var promeHost = hostFacade.getById(promEnv.getHostid());
88+ var promUser = hostUserFacade.listHostUserByHostId(promEnv.getHostid()).stream()
89+ .filter(p -> p.getHostId().equals(hostId) && p.getUsername().equals(promEnv.getUsername()))
90+ .findFirst().orElseThrow(
91+ () -> new RuntimeException("The node information corresponding to the host is not found"));
92+ // reload prometheus
93+ try (var promSession = SshSession.connect(promeHost.getPublicIp(), promeHost.getPort(),
94+ promEnv.getUsername(), encryptionUtils.decrypt(promUser.getPassword()));) {
95+ var promYmlStr = promSession.execute("cat " + promEnv.getPath() + "/prometheus.yml");
96+ var conf = YamlUtil.loadAs(promYmlStr, prometheusConfig.class);
97+ conf.scrape_configs
98+ .add(generateConfig("opengauss", nodeId, node.getPublicIp() + ":" + gaussEnv.getPort()));
99+ conf.scrape_configs.add(generateConfig("node", nodeId, node.getPublicIp() + ":" + nodeEnv.getPort()));
100+ var prometheusConfigFile = File.createTempFile("prom", ".tmp");
101+ FileUtil.appendUtf8String(YamlUtil.dump(conf), prometheusConfigFile);
102+ promSession.execute("rm " + promEnv.getPath() + "/prometheus.yml");
103+ promSession.upload(prometheusConfigFile.getAbsolutePath(), promEnv.getPath() + "/prometheus.yml");
104+ prometheusConfigFile.delete();
105+ }
106+ 
107+ // curl -X POST http://IP/-/reload
108+ var res = HttpUtil.post("http://" + promeHost.getPublicIp() + ":9090/-/reload", "");
109+ if ("Lifecycle API is not enabled.".equals(res)) {
110+ // TODO 未开启在线刷新
111+ }
112+ curr = nextStep(wsSession, steps, curr);
113+ sendMsg(wsSession, steps, curr, status.DONE);
114+ } catch (IOException e) {
115+ e.printStackTrace();
116+ steps.get(curr).setState(status.ERROR);
117+ wsUtil.sendText(wsSession, JSONUtil.toJsonStr(steps));
118+ }
119+ }
120+ 
121+ private static job generateConfig(String name, String nodeId, String host) {
122+ var con = new job.conf();
123+ con.setLabels(Map.of("instance", nodeId));
124+ con.setTargets(Arrays.asList(host));
125+ var job = new job();
126+ job.setStatic_configs(Arrays.asList(con));
127+ job.setJob_name(name);
128+ return job;
129+ }
130+ 
131+ private NctigbaEnv nodeExporter(String hostId, OpsHostUserEntity user, SshSession session) throws IOException {
132+ var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getHostid, hostId)
133+ .eq(NctigbaEnv::getType, type.NODE_EXPORTER));
134+ if (env != null)
135+ return env;
136+ var arch = session.execute(command.ARCH);
137+ String name = NODE_EXPORTER_NAME + arch(arch);
138+ String tar = name + TAR;
139+ if (!session.test(command.STAT.parse(name))) {
140+ if (!session.test(command.STAT.parse(tar)))
141+ session.execute(command.WGET.parse(NODE_EXPORTER_PATH + tar));
142+ session.execute(command.TAR.parse(tar));
143+ }
144+ session.execute("cd " + name + " && nohup ./node_exporter --collector.systemd 2>&1 & \r", false);
145+ var nodeEnv = new NctigbaEnv().setHostid(hostId).setPort(9100).setUsername(user.getUsername())
146+ .setType(type.NODE_EXPORTER).setPath(name);
147+ envMapper.insert(nodeEnv);
148+ // 验证
149+ return nodeEnv;
150+ }
151+ 
152+ private NctigbaEnv opengaussExporter(OpsClusterNodeVOSub node, String hostId, OpsHostEntity hostEntity,
153+ OpsHostUserEntity user, SshSession session) throws IOException {
154+ var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getHostid, hostId)
155+ .eq(NctigbaEnv::getType, type.OPENGAUSS_EXPORTER));
156+ if (env != null)
157+ return env;
158+ var arch = session.execute(command.ARCH);
159+ String name = OPENGAUSS_EXPORTER_NAME + arch(arch);
160+ String zip = name + ZIP;
161+ if (!session.test(command.STAT.parse("opengauss_exporter"))) {
162+ if (!session.test(command.STAT.parse(zip)))
163+ session.execute(command.WGET.parse(OPENGAUSS_EXPORTER_PATH + zip));
164+ session.execute(command.UNZIP.parse(zip));
165+ }
166+ 
167+ File f = File.createTempFile("og_exporter", "yml");
168+ var in = loader.getResource("og_exporter.yml").getInputStream();
169+ IoUtil.copy(in, new FileOutputStream(f));
170+ session.upload(f.getCanonicalPath(), "og_exporter.yml");
171+ f.delete();
172+ 
173+ // 启动
174+ var url = MessageFormat.format("postgresql://{0}:{1}@{2}:{3,number,#}/{4}", node.getDbUser(),
175+ URLUtil.encodeAll(node.getDbUserPassword()), hostEntity.getPublicIp(), node.getDbPort(),
176+ node.getDbName());
177+ session.execute("export DATA_SOURCE_NAME='" + url
178+ + "' && nohup ./opengauss_exporter --config=og_exporter.yml 2>&1 & \r", false);
179+ var gaussEnv = new NctigbaEnv().setHostid(hostId).setPort(9187).setUsername(user.getUsername())
180+ .setType(type.OPENGAUSS_EXPORTER).setPath("./");
181+ envMapper.insert(gaussEnv);
182+ // 验证
183+ return gaussEnv;
184+ }
185+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/OpsWdrService.java+418-0
@@ -0,0 +1,418 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.text.MessageFormat;
4+import java.util.ArrayList;
5+import java.util.Date;
6+import java.util.HashMap;
7+import java.util.List;
8+import java.util.Map;
9+import java.util.Objects;
10+ 
11+import javax.servlet.http.HttpServletResponse;
12+ 
13+import org.apache.commons.lang3.StringUtils;
14+import org.opengauss.admin.common.constant.ops.SshCommandConstants;
15+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
16+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
17+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
18+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
19+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
20+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
21+import org.opengauss.admin.common.exception.ops.OpsException;
22+import org.opengauss.admin.common.utils.ServletUtils;
23+import org.opengauss.admin.common.utils.ops.JschUtil;
24+import org.opengauss.admin.system.plugin.facade.HostFacade;
25+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
26+import org.opengauss.admin.system.service.ops.IOpsClusterNodeService;
27+import org.opengauss.admin.system.service.ops.IOpsClusterService;
28+import org.springframework.beans.factory.annotation.Autowired;
29+import org.springframework.stereotype.Service;
30+import org.springframework.transaction.annotation.Transactional;
31+ 
32+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
33+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
34+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
35+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
36+import com.jcraft.jsch.Session;
37+import com.nctigba.observability.instance.entity.OpsWdrEntity;
38+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
39+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrTypeEnum;
40+import com.nctigba.observability.instance.mapper.OpsWdrMapper;
41+import com.nctigba.observability.instance.model.WdrGeneratorBody;
42+import com.nctigba.observability.instance.model.WdrSnapshotVO;
43+ 
44+import cn.hutool.core.collection.CollUtil;
45+import cn.hutool.core.util.StrUtil;
46+import lombok.extern.slf4j.Slf4j;
47+ 
48+/**
49+ * @author lhf
50+ * @date 2022/10/13 15:14
51+ **/
52+@Slf4j
53+@Service
54+public class OpsWdrService extends ServiceImpl<OpsWdrMapper, OpsWdrEntity> {
55+ @Autowired
56+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
57+ private IOpsClusterService opsClusterService;
58+ @Autowired
59+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
60+ private IOpsClusterNodeService opsClusterNodeService;
61+ @Autowired
62+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
63+ private HostFacade hostFacade;
64+ @Autowired
65+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
66+ private HostUserFacade hostUserFacade;
67+ @Autowired
68+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
69+ private JschUtil jschUtil;
70+ @Autowired
71+ private ClusterOpsProviderManager clusterOpsProviderManager;
72+ @Autowired
73+ private ClusterManager clusterManager;
74+ 
75+ @SuppressWarnings({ "unchecked", "rawtypes" })
76+ public Page<OpsWdrEntity> listWdr(Page page, String clusterId, WdrScopeEnum wdrScope, WdrTypeEnum wdrType,
77+ String hostId, Date start, Date end) {
78+ var wrapper = Wrappers.lambdaQuery(OpsWdrEntity.class).eq(OpsWdrEntity::getClusterId, clusterId)
79+ .eq(Objects.nonNull(wdrScope), OpsWdrEntity::getScope,
80+ Objects.nonNull(wdrScope) ? wdrScope.name() : StrUtil.EMPTY)
81+ .eq(Objects.nonNull(wdrType), OpsWdrEntity::getReportType,
82+ Objects.nonNull(wdrType) ? wdrType.name() : StrUtil.EMPTY)
83+ .eq(StrUtil.isNotEmpty(hostId), OpsWdrEntity::getHostId, hostId)
84+ .ge(Objects.nonNull(start), OpsWdrEntity::getReportAt, start)
85+ .le(Objects.nonNull(end), OpsWdrEntity::getReportAt, end);
86+ page.setTotal(getBaseMapper().selectCount(wrapper));
87+ page.setRecords(getBaseMapper().selectList(wrapper.orderByDesc(OpsWdrEntity::getCreateTime)
88+ .last(" limit " + (page.getCurrent() - 1) * page.getSize() + "," + page.getSize())));
89+ return page;
90+ }
91+ 
92+ @Transactional(rollbackFor = Exception.class)
93+ public void generate(WdrGeneratorBody wdrGeneratorBody) {
94+ String clusterId = wdrGeneratorBody.getClusterId();
95+ OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId);
96+ 
97+ if (Objects.isNull(clusterEntity)) {
98+ throw new OpsException("Cluster information does not exist");
99+ }
100+ 
101+ List<OpsClusterNodeEntity> opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId);
102+ if (CollUtil.isEmpty(opsClusterNodeEntities)) {
103+ throw new OpsException("Cluster node information does not exist");
104+ }
105+ 
106+ WdrScopeEnum scope = wdrGeneratorBody.getScope();
107+ if (WdrScopeEnum.CLUSTER == scope) {
108+ generateClusterWdr(clusterEntity, opsClusterNodeEntities, wdrGeneratorBody.getType(),
109+ wdrGeneratorBody.getStartId(), wdrGeneratorBody.getEndId());
110+ } else {
111+ generateNodeWdr(clusterEntity, opsClusterNodeEntities, wdrGeneratorBody.getType(),
112+ wdrGeneratorBody.getHostId(), wdrGeneratorBody.getStartId(), wdrGeneratorBody.getEndId());
113+ }
114+ }
115+ 
116+ @SuppressWarnings({ "rawtypes", "unchecked" })
117+ public Page listSnapshot(Page page, String clusterId, String hostId) {
118+ var connection = clusterManager.getConnectionByClusterHost(clusterId, hostId);
119+ String sqlCount = "select count(*) from snapshot.snapshot";
120+ try (var statement = connection.createStatement(); var rs = statement.executeQuery(sqlCount);) {
121+ rs.next();
122+ page.setTotal(rs.getLong(1));
123+ } catch (Exception e) {
124+ log.error("Query snapshot record exception", e);
125+ }
126+ String sql = "select * from snapshot.snapshot";
127+ var orderby = ServletUtils.getParameter("orderby");
128+ if (StringUtils.isNotBlank(orderby))
129+ sql += " order by " + orderby;
130+ sql += " limit " + (page.getCurrent() - 1) * page.getSize() + "," + page.getSize();
131+ var res = new ArrayList<>();
132+ try (var statement = connection.createStatement(); var rs = statement.executeQuery(sql);) {
133+ while (rs.next()) {
134+ var vo = new WdrSnapshotVO();
135+ vo.setSnapshotId(rs.getInt("snapshot_id"));
136+ vo.setStartTs(rs.getDate("start_ts"));
137+ vo.setEndTs(rs.getDate("end_ts"));
138+ res.add(vo);
139+ }
140+ } catch (Exception e) {
141+ log.error("Query snapshot record exception", e);
142+ }
143+ page.setRecords(res);
144+ return page;
145+ }
146+ 
147+ public void createSnapshot(String clusterId, String hostId) {
148+ OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId);
149+ if (Objects.isNull(clusterEntity)) {
150+ throw new OpsException("Cluster information does not exist");
151+ }
152+ 
153+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
154+ if (Objects.isNull(hostEntity)) {
155+ throw new OpsException("host information does not exist");
156+ }
157+ 
158+ List<OpsClusterNodeEntity> opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId);
159+ if (CollUtil.isEmpty(opsClusterNodeEntities)) {
160+ throw new OpsException("Cluster node information is empty");
161+ }
162+ 
163+ OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream()
164+ .filter(node -> node.getHostId().equals(hostId)).findFirst()
165+ .orElseThrow(() -> new OpsException("Cluster node configuration not found"));
166+ 
167+ String installUserId = nodeEntity.getInstallUserId();
168+ OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId);
169+ if (Objects.isNull(userEntity)) {
170+ throw new OpsException("Installation user information does not exist");
171+ }
172+ 
173+ Session session = jschUtil
174+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(),
175+ decrypt(userEntity.getPassword()))
176+ .orElseThrow(() -> new OpsException("Failed to establish session with host"));
177+ 
178+ try {
179+ clusterOpsProviderManager.provider(clusterEntity.getVersion(), null)
180+ .orElseThrow(() -> new OpsException("The current version does not support"))
181+ .enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null);
182+ String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN,
183+ String.valueOf(clusterEntity.getPort()));
184+ Map<String, List<String>> response = new HashMap<>();
185+ List<String> responseList = new ArrayList<>();
186+ String sql = "select create_wdr_snapshot();\n\n\\q";
187+ 
188+ responseList.add(sql);
189+ 
190+ response.put("openGauss=#", responseList);
191+ JschResult jschResult = jschUtil.executeCommandWithSerialResponse(clientLoginOpenGauss, session, response);
192+ if (0 != jschResult.getExitCode()) {
193+ log.error("Generate wdr snapshot exception, exit code: {}, log: {}", jschResult.getExitCode(),
194+ jschResult.getResult());
195+ throw new OpsException("Generate wdr snapshot exception");
196+ }
197+ 
198+ } catch (Exception e) {
199+ log.error("Generate wdr snapshot exception", e);
200+ throw new OpsException("Generate wdr snapshot exception");
201+ } finally {
202+ if (Objects.nonNull(session) && session.isConnected()) {
203+ session.disconnect();
204+ }
205+ }
206+ }
207+ 
208+ private String decrypt(String password) {
209+ System.out.println("OpsWdrService.");
210+ System.out.println(password);
211+ return password;
212+ }
213+ 
214+ @Transactional(rollbackFor = Exception.class)
215+ public void del(String id) {
216+ OpsWdrEntity wdrEntity = getById(id);
217+ if (Objects.isNull(wdrEntity)) {
218+ throw new OpsException("The record to delete does not exist");
219+ }
220+ 
221+ String hostId = wdrEntity.getHostId();
222+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
223+ if (Objects.isNull(hostEntity)) {
224+ throw new OpsException("host information does not exist");
225+ }
226+ 
227+ List<OpsHostUserEntity> hostUserList = hostUserFacade.listHostUserByHostId(hostId);
228+ if (CollUtil.isEmpty(hostUserList)) {
229+ throw new OpsException("Host user information does not exist");
230+ }
231+ 
232+ OpsHostUserEntity installUser = hostUserList.stream()
233+ .filter(userEntity -> !"root".equals(userEntity.getUsername())).findFirst()
234+ .orElseThrow(() -> new OpsException("No installation user information found"));
235+ 
236+ Session session = jschUtil
237+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUser.getUsername(),
238+ decrypt(installUser.getPassword()))
239+ .orElseThrow(() -> new OpsException("Failed to establish connection with host"));
240+ 
241+ try {
242+ rmFile(session, wdrEntity.getReportPath(), wdrEntity.getReportName());
243+ } finally {
244+ if (Objects.nonNull(session) && session.isConnected()) {
245+ session.disconnect();
246+ }
247+ }
248+ 
249+ removeById(id);
250+ }
251+ 
252+ public void downloadWdr(String wdrId, HttpServletResponse response) {
253+ OpsWdrEntity wdrEntity = getById(wdrId);
254+ if (Objects.isNull(wdrEntity)) {
255+ throw new OpsException("wdr information not found");
256+ }
257+ 
258+ String hostId = wdrEntity.getHostId();
259+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
260+ if (Objects.isNull(hostEntity)) {
261+ throw new OpsException("host information not found");
262+ }
263+ 
264+ OpsHostUserEntity hostUserEntity = hostUserFacade.getById(wdrEntity.getUserId());
265+ if (Objects.isNull(hostUserEntity)) {
266+ throw new OpsException("No user information was found to generate the report");
267+ }
268+ 
269+ Session session = jschUtil
270+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), hostUserEntity.getUsername(),
271+ decrypt(hostUserEntity.getPassword()))
272+ .orElseThrow(() -> new OpsException("user failed to establish connection"));
273+ 
274+ try {
275+ jschUtil.download(session, wdrEntity.getReportPath(), wdrEntity.getReportName(), response);
276+ } finally {
277+ if (Objects.nonNull(session) && session.isConnected()) {
278+ session.disconnect();
279+ }
280+ }
281+ }
282+ 
283+ private void rmFile(Session session, String reportPath, String reportName) {
284+ String command = MessageFormat.format(SshCommandConstants.DEL_FILE, reportPath + "/" + reportName);
285+ try {
286+ JschResult jschResult = jschUtil.executeCommand(command, session);
287+ if (0 != jschResult.getExitCode()) {
288+ log.error("delete wdr failed,code:{},msg:{}", jschResult.getExitCode(), jschResult.getResult());
289+ }
290+ } catch (Exception e) {
291+ log.error("delete wdr failed");
292+ }
293+ }
294+ 
295+ private void generateNodeWdr(OpsClusterEntity clusterEntity, List<OpsClusterNodeEntity> opsClusterNodeEntities,
296+ WdrTypeEnum type, String hostId, String startId, String endId) {
297+ OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream()
298+ .filter(node -> node.getHostId().equals(hostId)).findFirst()
299+ .orElseThrow(() -> new OpsException("Cluster node configuration not found"));
300+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
301+ if (Objects.isNull(hostEntity)) {
302+ throw new OpsException("host information does not exist");
303+ }
304+ 
305+ String installUserId = nodeEntity.getInstallUserId();
306+ OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId);
307+ if (Objects.isNull(userEntity)) {
308+ throw new OpsException("Installation user information does not exist");
309+ }
310+ 
311+ Session session = jschUtil
312+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(),
313+ decrypt(userEntity.getPassword()))
314+ .orElseThrow(() -> new OpsException("Failed to establish session with host"));
315+ try {
316+ clusterOpsProviderManager.provider(clusterEntity.getVersion(), null)
317+ .orElseThrow(() -> new OpsException("The current version does not support"))
318+ .enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null);
319+ String wdrPath = "/home/" + userEntity.getUsername();
320+ String wdrName = "WDR-" + StrUtil.uuid() + ".html";
321+ doGenerate(wdrPath, wdrName, startId, endId, WdrScopeEnum.CLUSTER, type, session, clusterEntity.getPort());
322+ OpsWdrEntity opsWdrEntity = new OpsWdrEntity();
323+ opsWdrEntity.setScope(WdrScopeEnum.CLUSTER);
324+ opsWdrEntity.setReportAt(new Date());
325+ opsWdrEntity.setReportType(type);
326+ opsWdrEntity.setReportName(wdrName);
327+ opsWdrEntity.setReportPath(wdrPath);
328+ opsWdrEntity.setStartSnapshotId(startId);
329+ opsWdrEntity.setEndSnapshotId(endId);
330+ opsWdrEntity.setClusterId(clusterEntity.getClusterId());
331+ opsWdrEntity.setUserId(userEntity.getHostUserId());
332+ save(opsWdrEntity);
333+ } finally {
334+ if (Objects.nonNull(session) && session.isConnected()) {
335+ session.disconnect();
336+ }
337+ }
338+ }
339+ 
340+ private void generateClusterWdr(OpsClusterEntity clusterEntity, List<OpsClusterNodeEntity> opsClusterNodeEntities,
341+ WdrTypeEnum type, String startId, String endId) {
342+ OpsClusterNodeEntity masterNodeEntity = opsClusterNodeEntities.stream()
343+ .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst()
344+ .orElseThrow(() -> new OpsException("Cluster master configuration not found"));
345+ String hostId = masterNodeEntity.getHostId();
346+ OpsHostEntity hostEntity = hostFacade.getById(hostId);
347+ if (Objects.isNull(hostEntity)) {
348+ throw new OpsException("host information does not exist");
349+ }
350+ 
351+ String installUserId = masterNodeEntity.getInstallUserId();
352+ OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId);
353+ if (Objects.isNull(userEntity)) {
354+ throw new OpsException("Installation user information does not exist");
355+ }
356+ 
357+ Session session = jschUtil
358+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(),
359+ decrypt(userEntity.getPassword()))
360+ .orElseThrow(() -> new OpsException("Failed to establish session with host"));
361+ 
362+ try {
363+ clusterOpsProviderManager.provider(clusterEntity.getVersion(), null)
364+ .orElseThrow(() -> new OpsException("The current version does not support"))
365+ .enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null);
366+ 
367+ String wdrPath = "/home/" + userEntity.getUsername();
368+ String wdrName = "WDR-" + StrUtil.uuid() + ".html";
369+ doGenerate(wdrPath, wdrName, startId, endId, WdrScopeEnum.CLUSTER, type, session, clusterEntity.getPort());
370+ 
371+ OpsWdrEntity opsWdrEntity = new OpsWdrEntity();
372+ opsWdrEntity.setScope(WdrScopeEnum.CLUSTER);
373+ opsWdrEntity.setHostId(masterNodeEntity.getHostId());
374+ opsWdrEntity.setReportAt(new Date());
375+ opsWdrEntity.setReportType(type);
376+ opsWdrEntity.setReportName(wdrName);
377+ opsWdrEntity.setReportPath(wdrPath);
378+ opsWdrEntity.setStartSnapshotId(startId);
379+ opsWdrEntity.setEndSnapshotId(endId);
380+ opsWdrEntity.setClusterId(clusterEntity.getClusterId());
381+ opsWdrEntity.setUserId(userEntity.getHostUserId());
382+ save(opsWdrEntity);
383+ } finally {
384+ if (Objects.nonNull(session) && session.isConnected()) {
385+ session.disconnect();
386+ }
387+ }
388+ }
389+ 
390+ private void doGenerate(String wdrPath, String wdrName, String startId, String endId, WdrScopeEnum scope,
391+ WdrTypeEnum type, Session session, Integer port) {
392+ String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port));
393+ try {
394+ Map<String, List<String>> response = new HashMap<>();
395+ List<String> responseList = new ArrayList<>();
396+ String startSql = "\\a \\t \\o " + wdrPath + "/" + wdrName + "\n";
397+ String generateSql = "select generate_wdr_report('" + startId + "', '" + endId + "', '"
398+ + type.name().toLowerCase() + "', '" + scope.name().toLowerCase() + "'); \n";
399+ String endSql = "\\o \\a \\t \n \\q";
400+ 
401+ responseList.add(startSql);
402+ responseList.add(generateSql);
403+ responseList.add(endSql);
404+ 
405+ response.put("openGauss=#", responseList);
406+ JschResult jschResult = jschUtil.executeCommandWithSerialResponse(clientLoginOpenGauss, session, response);
407+ if (0 != jschResult.getExitCode()) {
408+ log.error("Generated wdr exception, exit code: {}, log: {}", jschResult.getExitCode(),
409+ jschResult.getResult());
410+ throw new OpsException("generate wdr exception");
411+ }
412+ 
413+ } catch (Exception e) {
414+ log.error("generate wdr exception", e);
415+ throw new OpsException("generate wdr exception");
416+ }
417+ }
418+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ParamInfoService.java+12-0
@@ -0,0 +1,12 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import com.nctigba.observability.instance.dto.param.DatabaseParamDTO;
4+import com.nctigba.observability.instance.dto.param.OsParamDTO;
5+import com.nctigba.observability.instance.model.param.ParamQuery;
6+ 
7+import java.util.List;
8+ 
9+public interface ParamInfoService {
10+ List<DatabaseParamDTO> getDatabaseParamInfo(ParamQuery paramQuery);
11+ List<OsParamDTO> getOsParamInfo(ParamQuery paramQuery);
12+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/PrometheusService.java+188-0
@@ -0,0 +1,188 @@
1+package com.nctigba.observability.instance.service;
2+ 
3+import java.io.IOException;
4+import java.io.PrintWriter;
5+import java.io.StringWriter;
6+import java.text.MessageFormat;
7+import java.util.Arrays;
8+import java.util.List;
9+import java.util.Map;
10+ 
11+import org.apache.commons.lang3.StringUtils;
12+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
13+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
14+import org.springframework.stereotype.Service;
15+ 
16+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
17+import com.nctigba.observability.instance.entity.NctigbaEnv;
18+import com.nctigba.observability.instance.entity.NctigbaEnv.type;
19+import com.nctigba.observability.instance.service.AbstractInstaller.Step.status;
20+import com.nctigba.observability.instance.util.HttpUtils;
21+import com.nctigba.observability.instance.util.SshSession;
22+import com.nctigba.observability.instance.util.SshSession.command;
23+ 
24+import cn.hutool.core.thread.ThreadUtil;
25+import cn.hutool.json.JSONUtil;
26+import lombok.Data;
27+import lombok.extern.slf4j.Slf4j;
28+ 
29+@Service
30+@Slf4j
31+public class PrometheusService extends AbstractInstaller {
32+ private static final String PROMETHEUS_USER = "Prometheus";
33+ private static final String PATH = "https://mirrors.tuna.tsinghua.edu.cn/github-release/prometheus/prometheus/LatestRelease/";
34+ private static final String NAME = "prometheus-2.42.0.linux-";
35+ 
36+ public void install(WsSession wsSession, String hostId, String rootPassword) {
37+ // @formatter:off
38+ var steps = Arrays.asList(
39+ new Step("初始化"),
40+ new Step("检查本机prometheus环境存在"),
41+ new Step("连接主机"),
42+ new Step("下载prometheus安装包,解压缩"),
43+ new Step("启动prometheus"),
44+ new Step("验证prometheus启动状态"),
45+ new Step("安装完成"));
46+ // @formatter:on
47+ var curr = 0;
48+ 
49+ curr = nextStep(wsSession, steps, curr);
50+ check(hostId);
51+ 
52+ log.info(encryptionUtils.decrypt(rootPassword));
53+ curr = nextStep(wsSession, steps, curr);
54+ var env = new NctigbaEnv().setHostid(hostId).setPort(9090).setUsername(PROMETHEUS_USER)
55+ .setType(type.PROMETHEUS);
56+ try (var sshsession = connect(env, rootPassword);) {
57+ curr = nextStep(wsSession, steps, curr);
58+ env.setPath(wget(sshsession));
59+ 
60+ curr = nextStep(wsSession, steps, curr);
61+ exec(sshsession, env);
62+ ThreadUtil.sleep(3000L);
63+ 
64+ curr = nextStep(wsSession, steps, curr);
65+ check(env);
66+ 
67+ curr = nextStep(wsSession, steps, curr);
68+ envMapper.insert(env);
69+ sendMsg(wsSession, steps, curr, status.DONE);
70+ } catch (Exception e) {
71+ steps.get(curr).setState(status.ERROR);
72+ wsUtil.sendText(wsSession, JSONUtil.toJsonStr(steps));
73+ var sw = new StringWriter();
74+ try (var pw = new PrintWriter(sw);) {
75+ e.printStackTrace(pw);
76+ }
77+ wsUtil.sendText(wsSession, sw.toString());
78+ }
79+ }
80+ 
81+ private void check(String hostId) {
82+ var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getHostid, hostId)
83+ .eq(NctigbaEnv::getType, type.PROMETHEUS));
84+ if (env != null)
85+ throw new RuntimeException();
86+ }
87+ 
88+ private SshSession connect(NctigbaEnv env, String rootPassword) throws IOException {
89+ OpsHostEntity hostEntity = hostFacade.getById(env.getHostid());
90+ if (hostEntity == null)
91+ throw new RuntimeException("host not found");
92+ env.setHost(hostEntity);
93+ var user = getUser(hostEntity, PROMETHEUS_USER, rootPassword);
94+ return SshSession.connect(hostEntity.getPublicIp(), hostEntity.getPort(), PROMETHEUS_USER,
95+ encryptionUtils.decrypt(user.getPassword()));
96+ }
97+ 
98+ private String wget(SshSession session) throws IOException {
99+ var arch = session.execute(command.ARCH);
100+ String name = NAME + arch(arch);
101+ String tar = name + TAR;
102+ if (!session.test(command.STAT.parse(name))) {
103+ if (!session.test(command.STAT.parse(tar)))
104+ session.execute(command.WGET.parse(PATH + tar));
105+ session.execute(command.TAR.parse(tar));
106+ }
107+ return name;
108+ }
109+ 
110+ private void exec(SshSession session, NctigbaEnv env) throws IOException {
111+ session.execute(MessageFormat.format("echo ''{0}'' > {1}",
112+ "cd " + env.getPath() + "\n./prometheus --web.enable-lifecycle --config.file=prometheus.yml &",
113+ "start.sh"));
114+ session.execute("sh start.sh", false);
115+ }
116+ 
117+ private void check(NctigbaEnv env) {
118+ String str = HttpUtils.sendGet("http://" + env.getHost().getPublicIp() + ":9090/api/v1/status/runtimeinfo",
119+ null);
120+ if (StringUtils.isBlank(str))
121+ throw new RuntimeException("prometheus 启动失败");
122+ }
123+ 
124+ /**
125+ * default prometheus config
126+ * @formatter:off
127+alerting:
128+ alertmanagers:
129+ - static_configs:
130+ - targets:
131+ - alertmanager: 9093
132+global:
133+ evaluation_interval: 15s
134+ scrape_interval: 15s
135+rule_files: null
136+scrape_configs:
137+- job_name: prometheus
138+ static_configs:
139+ - targets:
140+ - localhost:9090
141+ * @formatter:on
142+ */
143+ @Data
144+ public static class prometheusConfig {
145+ global global;
146+ alert alerting;
147+ List<String> rule_files;
148+ List<job> scrape_configs;
149+ 
150+ @Data
151+ public static class global {
152+ String scrape_interval;
153+ String evaluation_interval;
154+ }
155+ 
156+ @Data
157+ public static class alert {
158+ List<alertmanager> alertmanagers;
159+ 
160+ @Data
161+ public static class alertmanager {
162+ List<conf> static_configs;
163+ 
164+ @Data
165+ public static class conf {
166+ List<target> targets;
167+ 
168+ @Data
169+ public static class target {
170+ int alertmanager;
171+ }
172+ }
173+ }
174+ }
175+ 
176+ @Data
177+ public static class job {
178+ String job_name;
179+ List<conf> static_configs;
180+ 
181+ @Data
182+ public static class conf {
183+ List<String> targets;
184+ Map<String, String> labels;
185+ }
186+ }
187+ }
188+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/ParamInfoServiceImpl.java+133-0
@@ -0,0 +1,133 @@
1+package com.nctigba.observability.instance.service.impl;
2+ 
3+import com.nctigba.observability.instance.dto.param.DatabaseParamDTO;
4+import com.nctigba.observability.instance.dto.param.OsParamDTO;
5+import com.nctigba.observability.instance.model.param.DatabaseParamData;
6+import com.nctigba.observability.instance.model.param.OsParamData;
7+import com.nctigba.observability.instance.model.param.ParamQuery;
8+import com.nctigba.observability.instance.pool.SSHPoolManager;
9+import com.nctigba.observability.instance.service.ClusterManager;
10+import com.nctigba.observability.instance.service.ParamInfoService;
11+import com.nctigba.observability.instance.util.SSHOperator;
12+import lombok.RequiredArgsConstructor;
13+import lombok.extern.slf4j.Slf4j;
14+import org.springframework.stereotype.Service;
15+ 
16+import java.util.ArrayList;
17+import java.util.List;
18+ 
19+@Slf4j
20+@RequiredArgsConstructor
21+@Service
22+public class ParamInfoServiceImpl implements ParamInfoService {
23+ 
24+ private final ClusterManager opsFacade;
25+ private final String[] osParamType={"net.ipv4.tcp_max_tw_buckets","net.ipv4.tcp_tw_reuse","net.ipv4.tcp_tw_recycle",
26+ "net.ipv4.tcp_keepalive_time","net.ipv4.tcp_keepalive_probes","net.ipv4.tcp_keepalive_intvl","net.ipv4.tcp_retries1",
27+ "net.ipv4.tcp_syn_retries","net.ipv4.tcp_synack_retries","net.ipv4.tcp_retries2","vm.overcommit_memory","net.ipv4.tcp_rmem",
28+ "net.ipv4.tcp_wmem","net.core.wmem_max","net.core.rmem_max","net.core.wmem_default","net.core.rmem_default",
29+ "net.ipv4.ip_local_port_range","kernel.sem","vm.min_free_kbytes","net.core.somaxconn","net.ipv4.tcp_syncookies",
30+ "net.core.netdev_max_backlog","net.ipv4.tcp_max_syn_backlog","net.ipv4.tcp_fin_timeout","kernel.shmall","kernel.shmmax",
31+ "net.ipv4.tcp_sack","net.ipv4.tcp_timestamps","vm.extfrag_threshold","vm.overcommit_ratio","MTU"};
32+ private final String[] databaseParamType={"max_process_memory","work_mem","pagewriter_sleep","bgwriter_delay","bgwriter_thread_num",
33+ "max_io_capacity","log_min_duration_statement","log_duration","track_stmt_stat_level","track_stmt_retention_time",
34+ "enable_thread_pool","thread_pool_attr","log_statement","log_error_verbosity","log_min_messages","log_min_error_statement"};
35+ 
36+ @Override
37+ public List<DatabaseParamDTO> getDatabaseParamInfo(ParamQuery paramQuery) {
38+ try(var conn = opsFacade.getConnectionByNodeId(paramQuery.getNodeId());) {
39+ String sql="select name,setting from pg_settings";
40+ var stmt = conn.createStatement();
41+ var rs = stmt.executeQuery(sql);
42+ String value = null;
43+ List<DatabaseParamDTO> list=new ArrayList<>();
44+ while (rs.next()) {
45+ DatabaseParamData[] fields= DatabaseParamData.values();
46+ for(int j=0;j<fields.length;j++) {
47+ if (rs.getString(1).equals(fields[j].getParamName())) {
48+ value = rs.getString(2);
49+ log.info(fields[j].toString()+value);
50+ DatabaseParamDTO databaseParamDTO=new DatabaseParamDTO();
51+ databaseParamDTO.setSeqNo(String.valueOf(j+1));
52+ databaseParamDTO.setClassify(fields[j].getClassify());
53+ databaseParamDTO.setParamName(fields[j].getParamName());
54+ databaseParamDTO.setParamDetail(fields[j].getParamDetail());
55+ databaseParamDTO.setActualValue(value);
56+ databaseParamDTO.setSuggestValue(fields[j].getSuggestValue());
57+ databaseParamDTO.setDefaultValue(fields[j].getDefaultValue());
58+ databaseParamDTO.setUnit(fields[j].getUnit());
59+ databaseParamDTO.setSuggestExplain(fields[j].getSuggestExplain());
60+ list.add(databaseParamDTO);
61+ }
62+ }
63+ }
64+ stmt.close();
65+ return list;
66+ }catch (Exception e){
67+ log.info(e.getMessage());
68+ return null;
69+ }
70+ }
71+ 
72+ /*@Override
73+ public List<OsParamDTO> getOsParamInfo(ParamQuery paramQuery) {
74+ List<OsParamDTO> list=new ArrayList<>();
75+ for(int i=0;i<osParamType.length;i++){
76+ SSHOperator ssh = SSHPoolManager.getSSHOperator("10.10.9.238", 22, "root",
77+ "Van@09876");
78+ String paramValue=ssh.executeCommandReturnStr("sysctl -a | grep "+osParamType[i]+" | awk -F= '{print $2}'");
79+ OsParamDTO osParamDTO=new OsParamDTO();
80+ osParamDTO.setSeqNo(String.valueOf(i+1));
81+ osParamDTO.setClassify("操作系统");
82+ osParamDTO.setParamName(osParamType[i]);
83+ osParamDTO.setParamDetail("表示同时保持TIME_WAIT状态的TCP/IP连接最大数量。如果超过所配置的取值,TIME_WAIT将立刻被释放并打印警告信息。");
84+ osParamDTO.setActualValue(paramValue.replace("\n","").replace("\t"," "));
85+ osParamDTO.setSuggestValue("10000");
86+ osParamDTO.setDefaultValue("180000");
87+ osParamDTO.setUnit("数目");
88+ osParamDTO.setSuggestExplain("系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。(事实上做NAT的时候最好可以适当地增加该值)");
89+ list.add(osParamDTO);
90+ }
91+ return list;
92+ }*/
93+ 
94+ @Override
95+ public List<OsParamDTO> getOsParamInfo(ParamQuery paramQuery) {
96+ var node = opsFacade.getOpsNodeById(paramQuery.getNodeId());
97+ List<OsParamDTO> list=new ArrayList<>();
98+ SSHOperator ssh = SSHPoolManager.getSSHOperator(node.getPublicIp(), node.getHostPort(), "root",
99+ paramQuery.getPassword());
100+ //String paramValue=ssh.executeCommandReturnStr("sysctl -a | grep "+osParamType[i]+" | awk -F= '{print $2}'");
101+ String paramValues=ssh.executeCommandReturnStr("sysctl -a");
102+ String[] values=paramValues.split("\n");
103+ String paramData=null;
104+ for(int n=0;n<values.length;n++){
105+ paramData=values[n].substring(values[n].indexOf("=")+1).trim();
106+ OsParamData[] fields= OsParamData.values();
107+ for(int j=0;j<fields.length;j++){
108+ if(values[n].substring(0,values[n].lastIndexOf("=")).trim().equals(fields[j].getParamName()))
109+ {
110+ OsParamDTO osParamDTO=new OsParamDTO();
111+ osParamDTO.setSeqNo(String.valueOf(j+1));
112+ osParamDTO.setClassify(fields[j].getClassify());
113+ osParamDTO.setParamName(fields[j].getParamName());
114+ osParamDTO.setParamDetail(fields[j].getParamDetail());
115+ osParamDTO.setActualValue(paramData);
116+ osParamDTO.setSuggestValue(fields[j].getSuggestValue());
117+ osParamDTO.setDefaultValue(fields[j].getDefaultValue());
118+ osParamDTO.setUnit(fields[j].getUnit());
119+ osParamDTO.setSuggestExplain(fields[j].getSuggestExplain());
120+ list.add(osParamDTO);
121+ }
122+ }
123+ }
124+ //HostUserFacade hostUserFacade=new HostUserFacade();
125+ //List<OpsHostUserEntity> userList=hostUserFacade.listHostUserByHostId(node.getHostId());
126+ //for(int n=0;n<userList.size();n++){OpsHostUserEntity opsHostUserEntity=userList.get(n);}
127+ 
128+ //for(int i=0;i<osParamType.length;i++){}
129+ return list;
130+ }
131+ 
132+ 
133+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/AbstractOpsProvider.java+279-0
@@ -0,0 +1,279 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.io.IOException;
4+import java.text.MessageFormat;
5+import java.util.List;
6+import java.util.Objects;
7+ 
8+import org.opengauss.admin.common.constant.ops.SshCommandConstants;
9+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
10+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
11+import org.opengauss.admin.common.core.domain.model.ops.HostInfoHolder;
12+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
13+import org.opengauss.admin.common.core.domain.model.ops.WsSession;
14+import org.opengauss.admin.common.exception.ops.OpsException;
15+import org.opengauss.admin.common.utils.ops.JschUtil;
16+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
17+import org.springframework.beans.factory.InitializingBean;
18+ 
19+import com.jcraft.jsch.Session;
20+import com.nctigba.observability.instance.service.ClusterOpsProvider;
21+import com.nctigba.observability.instance.service.ClusterOpsProviderManager;
22+ 
23+import cn.hutool.core.util.StrUtil;
24+import lombok.extern.slf4j.Slf4j;
25+ 
26+/**
27+ * @author lhf
28+ * @date 2022/8/12 09:20
29+ **/
30+@Slf4j
31+public abstract class AbstractOpsProvider implements ClusterOpsProvider, InitializingBean {
32+ 
33+ protected void ensureLimits(JschUtil jschUtil, Session rootSession, WsSession retSession) {
34+ String limitsCheck = SshCommandConstants.LIMITS_CHECK;
35+ try {
36+ JschResult jschResult = null;
37+ try {
38+ jschResult = jschUtil.executeCommand(limitsCheck, rootSession, retSession, null);
39+ } catch (InterruptedException e) {
40+ throw new OpsException("thread is interrupted");
41+ }
42+ if (0 != jschResult.getExitCode()) {
43+ log.error("Detect ulimit exception, exit code: {}, error message: {}", jschResult.getExitCode(),
44+ jschResult.getResult());
45+ }
46+ 
47+ if (StrUtil.isNotEmpty(jschResult.getResult())) {
48+ return;
49+ }
50+ } catch (IOException e) {
51+ log.error("Detect ulimit error", e);
52+ }
53+ 
54+ String limits = SshCommandConstants.LIMITS;
55+ try {
56+ JschResult jschResult = null;
57+ try {
58+ jschResult = jschUtil.executeCommand(limits, rootSession, retSession, null);
59+ } catch (InterruptedException e) {
60+ throw new OpsException("thread is interrupted");
61+ }
62+ if (0 != jschResult.getExitCode()) {
63+ log.error("set ulimit exception, exit code: {}, error message: {}", jschResult.getExitCode(),
64+ jschResult.getResult());
65+ throw new OpsException("set ulimit exception");
66+ }
67+ } catch (IOException e) {
68+ log.error("set ulimit exception", e);
69+ throw new OpsException("set ulimit exception");
70+ }
71+ }
72+ 
73+ protected String scpInstallPackageToMasterNode(JschUtil jschUtil, Session rootSession, String sourcePath,
74+ String targetPath, WsSession retSession) {
75+ String installPackageFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1);
76+ String installPackageFullPath = targetPath + "/" + installPackageFileName;
77+ jschUtil.upload(rootSession, retSession, sourcePath, installPackageFullPath);
78+ return installPackageFullPath;
79+ }
80+ 
81+ protected void sem(JschUtil jschUtil, Session rootSession, WsSession retSession) {
82+ String command = SshCommandConstants.SEM;
83+ try {
84+ JschResult jschResult = null;
85+ try {
86+ jschResult = jschUtil.executeCommand(command, rootSession, retSession, null);
87+ } catch (InterruptedException e) {
88+ throw new OpsException("thread is interrupted");
89+ }
90+ if (0 != jschResult.getExitCode()) {
91+ log.error("set kernel.sem exception, exit code: {}, error message: {}", jschResult.getExitCode(),
92+ jschResult.getResult());
93+ throw new OpsException("set kernel.sem exception");
94+ }
95+ } catch (IOException e) {
96+ log.error("set kernel.sem exception", e);
97+ }
98+ }
99+ 
100+ protected void decompress(JschUtil jschUtil, Session rootSession, String targetPath, String installPackageFullPath,
101+ WsSession retSession, String decompressArgs) {
102+ String command = MessageFormat.format(SshCommandConstants.DECOMPRESS, decompressArgs, installPackageFullPath,
103+ targetPath);
104+ try {
105+ JschResult jschResult = jschUtil.executeCommand(command, rootSession, retSession, null);
106+ if (0 != jschResult.getExitCode()) {
107+ log.error("Failed to decompress installation package, exit code: {}, error message: {}",
108+ jschResult.getExitCode(), jschResult.getResult());
109+ throw new OpsException("Unzip the installation package failed");
110+ }
111+ } catch (Exception e) {
112+ log.error("Unzip the installation package failed:", e);
113+ throw new OpsException("Unzip the installation package failed");
114+ }
115+ }
116+ 
117+ protected Session loginWithUser(JschUtil jschUtil, EncryptionUtils encryptionUtils,
118+ List<HostInfoHolder> hostInfoHolders, boolean root, String hostId, String userId) {
119+ HostInfoHolder hostInfoHolder = hostInfoHolders.stream()
120+ .filter(host -> host.getHostEntity().getHostId().equals(hostId)).findFirst()
121+ .orElseThrow(() -> new OpsException("host information not found"));
122+ OpsHostEntity hostEntity = hostInfoHolder.getHostEntity();
123+ 
124+ if (root) {
125+ userId = hostInfoHolder.getHostUserEntities().stream()
126+ .filter(hostUser -> "root".equals(hostUser.getUsername())).findFirst()
127+ .orElseThrow(() -> new OpsException("root user information not found")).getHostUserId();
128+ }
129+ 
130+ OpsHostUserEntity userEntity = null;
131+ for (OpsHostUserEntity hostUserEntity : hostInfoHolder.getHostUserEntities()) {
132+ if (hostUserEntity.getHostUserId().equals(userId)) {
133+ userEntity = hostUserEntity;
134+ break;
135+ }
136+ }
137+ 
138+ if (Objects.isNull(userEntity)) {
139+ throw new OpsException("No installation user information found");
140+ }
141+ 
142+ return sshLogin(jschUtil, encryptionUtils, hostEntity, userEntity);
143+ }
144+ 
145+ protected Session sshLogin(JschUtil jschUtil, EncryptionUtils encryptionUtils, OpsHostEntity hostEntity,
146+ OpsHostUserEntity userEntity) {
147+ return jschUtil
148+ .getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(),
149+ encryptionUtils.decrypt(userEntity.getPassword()))
150+ .orElseThrow(() -> new OpsException(
151+ "Session establishment exception with host[" + hostEntity.getPublicIp() + "]"));
152+ }
153+ 
154+ protected void chmodFullPath(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) {
155+ String chmod = MessageFormat.format(SshCommandConstants.CHMOD, path);
156+ 
157+ try {
158+ try {
159+ jschUtil.executeCommand(chmod, rootSession, wsSession, null);
160+ } catch (InterruptedException e) {
161+ throw new OpsException("thread is interrupted");
162+ }
163+ } catch (IOException e) {
164+ log.error("Failed to grant permission", e);
165+ }
166+ }
167+ 
168+ protected void chmod(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) {
169+ if (StrUtil.isNotEmpty(path) && path.indexOf("/", 1) > 0) {
170+ path = path.substring(0, path.indexOf("/", 1));
171+ }
172+ String chmod = MessageFormat.format(SshCommandConstants.CHMOD, path);
173+ 
174+ try {
175+ try {
176+ jschUtil.executeCommand(chmod, rootSession, wsSession, null);
177+ } catch (InterruptedException e) {
178+ throw new OpsException("thread is interrupted");
179+ }
180+ } catch (IOException e) {
181+ log.error("Failed to grant permission", e);
182+ }
183+ }
184+ 
185+ protected void chmodDataPath(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) {
186+ String chmod = MessageFormat.format(SshCommandConstants.CHMOD_DATA_PATH, path);
187+ 
188+ try {
189+ try {
190+ jschUtil.executeCommand(chmod, rootSession, wsSession, null);
191+ } catch (InterruptedException e) {
192+ throw new OpsException("thread is interrupted");
193+ }
194+ } catch (IOException e) {
195+ log.error("Failed to grant permission", e);
196+ }
197+ }
198+ 
199+ protected void ensurePermission(JschUtil jschUtil, Session rootSession, String installUserName, String targetPath,
200+ WsSession wsSession) {
201+ chmod(jschUtil, rootSession, targetPath, wsSession);
202+ 
203+ String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath);
204+ 
205+ try {
206+ JschResult jschResult = null;
207+ try {
208+ jschResult = jschUtil.executeCommand(chown, rootSession, wsSession, null);
209+ } catch (InterruptedException e) {
210+ throw new OpsException("thread is interrupted");
211+ }
212+ if (0 != jschResult.getExitCode()) {
213+ log.error("Failed to grant permission, exit code: {}, error message: {}", jschResult.getExitCode(),
214+ jschResult.getResult());
215+ throw new OpsException("Failed to grant permission");
216+ }
217+ } catch (IOException e) {
218+ log.error("Failed to grant permission", e);
219+ throw new OpsException("Failed to grant permission");
220+ }
221+ }
222+ 
223+ protected void ensureDataPathPermission(JschUtil jschUtil, Session rootSession, String installUserName,
224+ String targetPath, WsSession wsSession) {
225+ chmodDataPath(jschUtil, rootSession, targetPath, wsSession);
226+ 
227+ String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath);
228+ 
229+ try {
230+ JschResult jschResult = null;
231+ try {
232+ jschResult = jschUtil.executeCommand(chown, rootSession, wsSession, null);
233+ } catch (InterruptedException e) {
234+ throw new OpsException("thread is interrupted");
235+ }
236+ if (0 != jschResult.getExitCode()) {
237+ log.error("Failed to grant permission, exit code: {}, error message: {}", jschResult.getExitCode(),
238+ jschResult.getResult());
239+ throw new OpsException("Failed to grant permission");
240+ }
241+ } catch (IOException e) {
242+ log.error("Failed to grant permission", e);
243+ throw new OpsException("Failed to grant permission");
244+ }
245+ }
246+ 
247+ protected void ensureDirExist(JschUtil jschUtil, Session rootSession, String targetPath, WsSession retSession) {
248+ String command = MessageFormat.format(SshCommandConstants.MK_DIR, targetPath);
249+ try {
250+ JschResult jschResult = null;
251+ try {
252+ jschResult = jschUtil.executeCommand(command, rootSession, retSession, null);
253+ } catch (InterruptedException e) {
254+ throw new OpsException("thread is interrupted");
255+ }
256+ if (0 != jschResult.getExitCode()) {
257+ log.error("Failed to create directory, exit code: {}, error message: {}", jschResult.getExitCode(),
258+ jschResult.getResult());
259+ throw new OpsException("Failed to create installation directory");
260+ }
261+ } catch (IOException e) {
262+ log.error("Failed to create installation directory:", e);
263+ throw new OpsException("Failed to create installation directory");
264+ }
265+ }
266+ 
267+ protected String preparePath(String path) {
268+ if (StrUtil.isEmpty(path) || path.endsWith("/")) {
269+ return path;
270+ }
271+ 
272+ return path + "/";
273+ }
274+ 
275+ @Override
276+ public void afterPropertiesSet() {
277+ ClusterOpsProviderManager.registry(version(), os(), this);
278+ }
279+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/EnterpriseOpsProvider.java+66-0
@@ -0,0 +1,66 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
9+import org.opengauss.admin.common.exception.ops.OpsException;
10+import org.opengauss.admin.common.utils.ops.JschUtil;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.stereotype.Service;
13+ 
14+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
15+import com.jcraft.jsch.Session;
16+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
17+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
18+ 
19+import cn.hutool.core.util.StrUtil;
20+import lombok.extern.slf4j.Slf4j;
21+ 
22+/**
23+ * @author lhf
24+ * @date 2022/8/12 09:23
25+ **/
26+@Slf4j
27+@Service
28+public class EnterpriseOpsProvider extends AbstractOpsProvider {
29+ @Autowired
30+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
31+ private JschUtil jschUtil;
32+ 
33+ @Override
34+ public OpenGaussVersionEnum version() {
35+ return OpenGaussVersionEnum.ENTERPRISE;
36+ }
37+ 
38+ @Override
39+ public OpenGaussSupportOSEnum os() {
40+ return OpenGaussSupportOSEnum.CENTOS_X86_64;
41+ }
42+ 
43+ @Override
44+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
45+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
46+ String command;
47+ if (StrUtil.isEmpty(dataPath)) {
48+ command = "gs_guc reload -I all -c \"enable_wdr_snapshot=on\"";
49+ } else {
50+ command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
51+ }
52+ 
53+ try {
54+ JschResult jschResult = jschUtil.executeCommand(command, session);
55+ if (0 != jschResult.getExitCode()) {
56+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
57+ jschResult.getExitCode(), jschResult.getResult());
58+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
59+ }
60+ 
61+ } catch (Exception e) {
62+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
63+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
64+ }
65+ }
66+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/LiteOpsProvider.java+75-0
@@ -0,0 +1,75 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
10+import org.opengauss.admin.common.exception.ops.OpsException;
11+import org.opengauss.admin.common.utils.ops.JschUtil;
12+import org.opengauss.admin.system.plugin.facade.HostFacade;
13+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
14+import org.springframework.beans.factory.annotation.Autowired;
15+import org.springframework.stereotype.Service;
16+ 
17+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
18+import com.jcraft.jsch.Session;
19+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
20+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
21+ 
22+import cn.hutool.core.util.StrUtil;
23+import lombok.extern.slf4j.Slf4j;
24+ 
25+/**
26+ * @author lhf
27+ * @date 2022/8/12 09:26
28+ **/
29+@Slf4j
30+@Service
31+public class LiteOpsProvider extends AbstractOpsProvider {
32+ @Autowired(required = false)
33+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
34+ private HostUserFacade hostUserFacade;
35+ @Autowired(required = false)
36+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
37+ private HostFacade hostFacade;
38+ @Autowired
39+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
40+ private JschUtil jschUtil;
41+ 
42+ @Override
43+ public OpenGaussVersionEnum version() {
44+ return OpenGaussVersionEnum.LITE;
45+ }
46+ 
47+ @Override
48+ public OpenGaussSupportOSEnum os() {
49+ return OpenGaussSupportOSEnum.CENTOS_X86_64;
50+ }
51+ 
52+ @Override
53+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
54+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
55+ if (StrUtil.isEmpty(dataPath)) {
56+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
57+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
58+ .getDataPath();
59+ }
60+ 
61+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
62+ 
63+ try {
64+ JschResult jschResult = jschUtil.executeCommand(command, session);
65+ if (0 != jschResult.getExitCode()) {
66+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
67+ jschResult.getExitCode(), jschResult.getResult());
68+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
69+ }
70+ } catch (Exception e) {
71+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
72+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
73+ }
74+ }
75+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/MinimaListOpsProvider.java+78-0
@@ -0,0 +1,78 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.DeployTypeEnum;
10+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
11+import org.opengauss.admin.common.exception.ops.OpsException;
12+import org.opengauss.admin.common.utils.ops.JschUtil;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.stereotype.Service;
15+ 
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import com.jcraft.jsch.Session;
18+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
19+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
20+ 
21+import cn.hutool.core.util.StrUtil;
22+import lombok.extern.slf4j.Slf4j;
23+ 
24+/**
25+ * @author lhf
26+ * @date 2022/8/12 09:25
27+ **/
28+@Slf4j
29+@Service
30+public class MinimaListOpsProvider extends AbstractOpsProvider {
31+ @Autowired
32+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
33+ private JschUtil jschUtil;
34+ 
35+ @Override
36+ public OpenGaussVersionEnum version() {
37+ return OpenGaussVersionEnum.MINIMAL_LIST;
38+ }
39+ 
40+ @Override
41+ public OpenGaussSupportOSEnum os() {
42+ return OpenGaussSupportOSEnum.CENTOS_X86_64;
43+ }
44+ 
45+ @Override
46+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
47+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
48+ if (StrUtil.isEmpty(dataPath)) {
49+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
50+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
51+ .getDataPath();
52+ 
53+ if (clusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) {
54+ dataPath = dataPath + "/master";
55+ } else {
56+ dataPath = dataPath + "/single_node";
57+ }
58+ }
59+ 
60+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
61+ 
62+ try {
63+ JschResult jschResult = jschUtil.executeCommand(command, session);
64+ if (0 != jschResult.getExitCode()) {
65+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
66+ jschResult.getExitCode(), jschResult.getResult());
67+ throw new OpsException("Failed to query the enable_wdr_snapshot parameter");
68+ }
69+ } catch (Exception e) {
70+ String msg = "Failed to set the enable_wdr_snapshot parameter";
71+ if (e instanceof OpsException) {
72+ msg = e.getMessage();
73+ }
74+ log.error(msg, e);
75+ throw new OpsException(msg);
76+ }
77+ }
78+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64EnterpriseOpsProvider.java+66-0
@@ -0,0 +1,66 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
9+import org.opengauss.admin.common.exception.ops.OpsException;
10+import org.opengauss.admin.common.utils.ops.JschUtil;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.stereotype.Service;
13+ 
14+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
15+import com.jcraft.jsch.Session;
16+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
17+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
18+ 
19+import cn.hutool.core.util.StrUtil;
20+import lombok.extern.slf4j.Slf4j;
21+ 
22+/**
23+ * @author lhf
24+ * @date 2022/8/12 09:23
25+ **/
26+@Slf4j
27+@Service
28+public class OpenEulerArch64EnterpriseOpsProvider extends AbstractOpsProvider {
29+ @Autowired
30+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
31+ private JschUtil jschUtil;
32+ 
33+ @Override
34+ public OpenGaussVersionEnum version() {
35+ return OpenGaussVersionEnum.ENTERPRISE;
36+ }
37+ 
38+ @Override
39+ public OpenGaussSupportOSEnum os() {
40+ return OpenGaussSupportOSEnum.OPENEULER_ARCH64;
41+ }
42+ 
43+ @Override
44+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
45+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
46+ String command;
47+ if (StrUtil.isEmpty(dataPath)) {
48+ command = "gs_guc reload -I all -c \"enable_wdr_snapshot=on\"";
49+ } else {
50+ command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
51+ }
52+ 
53+ try {
54+ JschResult jschResult = jschUtil.executeCommand(command, session);
55+ if (0 != jschResult.getExitCode()) {
56+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
57+ jschResult.getExitCode(), jschResult.getResult());
58+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
59+ }
60+ 
61+ } catch (Exception e) {
62+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
63+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
64+ }
65+ }
66+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64LiteOpsProvider.java+67-0
@@ -0,0 +1,67 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
10+import org.opengauss.admin.common.exception.ops.OpsException;
11+import org.opengauss.admin.common.utils.ops.JschUtil;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.stereotype.Service;
14+ 
15+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
16+import com.jcraft.jsch.Session;
17+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
18+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
19+ 
20+import cn.hutool.core.util.StrUtil;
21+import lombok.extern.slf4j.Slf4j;
22+ 
23+/**
24+ * @author lhf
25+ * @date 2022/8/12 09:26
26+ **/
27+@Slf4j
28+@Service
29+public class OpenEulerArch64LiteOpsProvider extends AbstractOpsProvider {
30+ @Autowired
31+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
32+ private JschUtil jschUtil;
33+ 
34+ @Override
35+ public OpenGaussVersionEnum version() {
36+ return OpenGaussVersionEnum.LITE;
37+ }
38+ 
39+ @Override
40+ public OpenGaussSupportOSEnum os() {
41+ return OpenGaussSupportOSEnum.OPENEULER_ARCH64;
42+ }
43+ 
44+ @Override
45+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
46+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
47+ if (StrUtil.isEmpty(dataPath)) {
48+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
49+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
50+ .getDataPath();
51+ }
52+ 
53+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
54+ 
55+ try {
56+ JschResult jschResult = jschUtil.executeCommand(command, session);
57+ if (0 != jschResult.getExitCode()) {
58+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
59+ jschResult.getExitCode(), jschResult.getResult());
60+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
61+ }
62+ } catch (Exception e) {
63+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
64+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
65+ }
66+ }
67+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64MinimaListOpsProvider.java+78-0
@@ -0,0 +1,78 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.DeployTypeEnum;
10+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
11+import org.opengauss.admin.common.exception.ops.OpsException;
12+import org.opengauss.admin.common.utils.ops.JschUtil;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.stereotype.Service;
15+ 
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import com.jcraft.jsch.Session;
18+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
19+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
20+ 
21+import cn.hutool.core.util.StrUtil;
22+import lombok.extern.slf4j.Slf4j;
23+ 
24+/**
25+ * @author lhf
26+ * @date 2022/8/12 09:25
27+ **/
28+@Slf4j
29+@Service
30+public class OpenEulerArch64MinimaListOpsProvider extends AbstractOpsProvider {
31+ @Autowired
32+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
33+ private JschUtil jschUtil;
34+ 
35+ @Override
36+ public OpenGaussVersionEnum version() {
37+ return OpenGaussVersionEnum.MINIMAL_LIST;
38+ }
39+ 
40+ @Override
41+ public OpenGaussSupportOSEnum os() {
42+ return OpenGaussSupportOSEnum.OPENEULER_ARCH64;
43+ }
44+ 
45+ @Override
46+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
47+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
48+ if (StrUtil.isEmpty(dataPath)) {
49+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
50+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
51+ .getDataPath();
52+ 
53+ if (clusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) {
54+ dataPath = dataPath + "/master";
55+ } else {
56+ dataPath = dataPath + "/single_node";
57+ }
58+ }
59+ 
60+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
61+ 
62+ try {
63+ JschResult jschResult = jschUtil.executeCommand(command, session);
64+ if (0 != jschResult.getExitCode()) {
65+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
66+ jschResult.getExitCode(), jschResult.getResult());
67+ throw new OpsException("Failed to query the enable_wdr_snapshot parameter");
68+ }
69+ } catch (Exception e) {
70+ String msg = "Failed to set the enable_wdr_snapshot parameter";
71+ if (e instanceof OpsException) {
72+ msg = e.getMessage();
73+ }
74+ log.error(msg, e);
75+ throw new OpsException(msg);
76+ }
77+ }
78+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86EnterpriseOpsProvider.java+66-0
@@ -0,0 +1,66 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
9+import org.opengauss.admin.common.exception.ops.OpsException;
10+import org.opengauss.admin.common.utils.ops.JschUtil;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.stereotype.Service;
13+ 
14+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
15+import com.jcraft.jsch.Session;
16+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
17+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
18+ 
19+import cn.hutool.core.util.StrUtil;
20+import lombok.extern.slf4j.Slf4j;
21+ 
22+/**
23+ * @author lhf
24+ * @date 2022/8/12 09:23
25+ **/
26+@Slf4j
27+@Service
28+public class OpenEulerX86EnterpriseOpsProvider extends AbstractOpsProvider {
29+ @Autowired
30+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
31+ private JschUtil jschUtil;
32+ 
33+ @Override
34+ public OpenGaussVersionEnum version() {
35+ return OpenGaussVersionEnum.ENTERPRISE;
36+ }
37+ 
38+ @Override
39+ public OpenGaussSupportOSEnum os() {
40+ return OpenGaussSupportOSEnum.OPENEULER_X86_64;
41+ }
42+ 
43+ @Override
44+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
45+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
46+ String command;
47+ if (StrUtil.isEmpty(dataPath)) {
48+ command = "gs_guc reload -I all -c \"enable_wdr_snapshot=on\"";
49+ } else {
50+ command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
51+ }
52+ 
53+ try {
54+ JschResult jschResult = jschUtil.executeCommand(command, session);
55+ if (0 != jschResult.getExitCode()) {
56+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
57+ jschResult.getExitCode(), jschResult.getResult());
58+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
59+ }
60+ 
61+ } catch (Exception e) {
62+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
63+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
64+ }
65+ }
66+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86LiteOpsProvider.java+67-0
@@ -0,0 +1,67 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
10+import org.opengauss.admin.common.exception.ops.OpsException;
11+import org.opengauss.admin.common.utils.ops.JschUtil;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.stereotype.Service;
14+ 
15+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
16+import com.jcraft.jsch.Session;
17+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
18+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
19+ 
20+import cn.hutool.core.util.StrUtil;
21+import lombok.extern.slf4j.Slf4j;
22+ 
23+/**
24+ * @author lhf
25+ * @date 2022/8/12 09:26
26+ **/
27+@Slf4j
28+@Service
29+public class OpenEulerX86LiteOpsProvider extends AbstractOpsProvider {
30+ @Autowired
31+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
32+ private JschUtil jschUtil;
33+ 
34+ @Override
35+ public OpenGaussVersionEnum version() {
36+ return OpenGaussVersionEnum.LITE;
37+ }
38+ 
39+ @Override
40+ public OpenGaussSupportOSEnum os() {
41+ return OpenGaussSupportOSEnum.OPENEULER_X86_64;
42+ }
43+ 
44+ @Override
45+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
46+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
47+ if (StrUtil.isEmpty(dataPath)) {
48+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
49+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
50+ .getDataPath();
51+ }
52+ 
53+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
54+ 
55+ try {
56+ JschResult jschResult = jschUtil.executeCommand(command, session);
57+ if (0 != jschResult.getExitCode()) {
58+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
59+ jschResult.getExitCode(), jschResult.getResult());
60+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
61+ }
62+ } catch (Exception e) {
63+ log.error("Failed to set the enable_wdr_snapshot parameter", e);
64+ throw new OpsException("Failed to set the enable_wdr_snapshot parameter");
65+ }
66+ }
67+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86MinimaListOpsProvider.java+78-0
@@ -0,0 +1,78 @@
1+package com.nctigba.observability.instance.service.provider;
2+ 
3+import java.util.List;
4+ 
5+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
6+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
7+import org.opengauss.admin.common.core.domain.model.ops.JschResult;
8+import org.opengauss.admin.common.enums.ops.ClusterRoleEnum;
9+import org.opengauss.admin.common.enums.ops.DeployTypeEnum;
10+import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum;
11+import org.opengauss.admin.common.exception.ops.OpsException;
12+import org.opengauss.admin.common.utils.ops.JschUtil;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.stereotype.Service;
15+ 
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import com.jcraft.jsch.Session;
18+import com.nctigba.observability.instance.entity.OpsWdrEntity.WdrScopeEnum;
19+import com.nctigba.observability.instance.service.ClusterOpsProviderManager.OpenGaussSupportOSEnum;
20+ 
21+import cn.hutool.core.util.StrUtil;
22+import lombok.extern.slf4j.Slf4j;
23+ 
24+/**
25+ * @author lhf
26+ * @date 2022/8/12 09:25
27+ **/
28+@Slf4j
29+@Service
30+public class OpenEulerX86MinimaListOpsProvider extends AbstractOpsProvider {
31+ @Autowired
32+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
33+ private JschUtil jschUtil;
34+ 
35+ @Override
36+ public OpenGaussVersionEnum version() {
37+ return OpenGaussVersionEnum.MINIMAL_LIST;
38+ }
39+ 
40+ @Override
41+ public OpenGaussSupportOSEnum os() {
42+ return OpenGaussSupportOSEnum.OPENEULER_X86_64;
43+ }
44+ 
45+ @Override
46+ public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity,
47+ List<OpsClusterNodeEntity> opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) {
48+ if (StrUtil.isEmpty(dataPath)) {
49+ dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER)
50+ .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found"))
51+ .getDataPath();
52+ 
53+ if (clusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) {
54+ dataPath = dataPath + "/master";
55+ } else {
56+ dataPath = dataPath + "/single_node";
57+ }
58+ }
59+ 
60+ String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\"";
61+ 
62+ try {
63+ JschResult jschResult = jschUtil.executeCommand(command, session);
64+ if (0 != jschResult.getExitCode()) {
65+ log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}",
66+ jschResult.getExitCode(), jschResult.getResult());
67+ throw new OpsException("Failed to query the enable_wdr_snapshot parameter");
68+ }
69+ } catch (Exception e) {
70+ String msg = "Failed to set the enable_wdr_snapshot parameter";
71+ if (e instanceof OpsException) {
72+ msg = e.getMessage();
73+ }
74+ log.error(msg, e);
75+ throw new OpsException(msg);
76+ }
77+ }
78+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/SshSession.java+199-0
@@ -0,0 +1,199 @@
1+package com.nctigba.observability.instance.util;
2+ 
3+import java.io.IOException;
4+import java.io.InputStream;
5+import java.io.OutputStream;
6+import java.nio.charset.StandardCharsets;
7+import java.text.MessageFormat;
8+import java.util.Map;
9+ 
10+import org.opengauss.admin.common.exception.ops.OpsException;
11+ 
12+import com.jcraft.jsch.ChannelExec;
13+import com.jcraft.jsch.ChannelSftp;
14+import com.jcraft.jsch.JSch;
15+import com.jcraft.jsch.JSchException;
16+import com.jcraft.jsch.Session;
17+import com.jcraft.jsch.SftpProgressMonitor;
18+ 
19+import cn.hutool.core.thread.ThreadUtil;
20+import lombok.extern.slf4j.Slf4j;
21+ 
22+@Slf4j
23+public class SshSession implements AutoCloseable {
24+ private static final int SESSION_TIMEOUT = 10000;
25+ private static final int CHANNEL_TIMEOUT = 50000;
26+ 
27+ public enum command {
28+ ARCH("arch"),
29+ CD("cd {0}"),
30+ LS("ls {0}"),
31+ STAT("stat {0}"),
32+ WGET("wget {0}"),
33+ TAR("tar zxf {0}"),
34+ UNZIP("unzip {0}"),
35+ APPEND_FILE(""),
36+ CHECK_USER("cat /etc/passwd | awk -F \":\" \"'{print $1}\"|grep {0} | wc -l"),
37+ CREATE_USER("useradd omm && echo ''{0} ALL=(ALL) ALL'' >> /etc/sudoers"),
38+ CHANGE_PASSWORD("passwd {1}"),;
39+ 
40+ private String cmd;
41+ 
42+ command(String cmd) {
43+ this.cmd = cmd;
44+ }
45+ 
46+ public String parse(Object... args) {
47+ return MessageFormat.format(cmd, args);
48+ }
49+ }
50+ 
51+ public boolean test(String command) throws IOException {
52+ try {
53+ execute(command, null);
54+ return true;
55+ } catch (RuntimeException e) {
56+ return false;
57+ }
58+ }
59+ 
60+ public String execute(command command) throws IOException {
61+ return execute(command.cmd, null, null);
62+ }
63+ 
64+ public String execute(command command, Map<String, String> autoResponse) throws IOException {
65+ return execute(command.cmd, autoResponse, null);
66+ }
67+ 
68+ public String execute(String command) throws IOException {
69+ return execute(command, null, null);
70+ }
71+ 
72+ public String execute(String command, Boolean pty) throws IOException {
73+ return execute(command, null, pty);
74+ }
75+ 
76+ public String execute(String command, Map<String, String> autoResponse, Boolean pty) throws IOException {
77+ log.info("Execute an order:{}", command);
78+ ChannelExec channelExec;
79+ try {
80+ channelExec = (ChannelExec) session.openChannel("exec");
81+ channelExec.setPtyType("dump");
82+ channelExec.setPty(pty == null ? true : pty);
83+ } catch (JSchException e) {
84+ throw new OpsException("Obtaining the exec channel fails");
85+ }
86+ channelExec.setCommand(command);
87+ try {
88+ channelExec.connect(CHANNEL_TIMEOUT);
89+ } catch (JSchException e) {
90+ throw new OpsException("Command execution exception");
91+ }
92+ StringBuilder resultStrBuilder = new StringBuilder();
93+ InputStream in = channelExec.getInputStream();
94+ OutputStream out = channelExec.getOutputStream();
95+ byte[] tmp = new byte[1024];
96+ while (true) {
97+ while (in.available() > 0) {
98+ int i = in.read(tmp, 0, 1024);
99+ if (i < 0) {
100+ break;
101+ }
102+ String msg = new String(tmp, 0, i);
103+ resultStrBuilder.append(msg);
104+ }
105+ if (pty != null && !pty)
106+ return resultStrBuilder.toString().trim();
107+ if (channelExec.isClosed()) {
108+ if (in.available() > 0) {
109+ continue;
110+ }
111+ in.close();
112+ out.close();
113+ int exitStatus = channelExec.getExitStatus();
114+ if (exitStatus != 0)
115+ throw new RuntimeException(resultStrBuilder.toString().trim());
116+ return resultStrBuilder.toString().trim();
117+ }
118+ ThreadUtil.sleep(2000);
119+ if (autoResponse != null) {
120+ autoResponse.forEach((k, v) -> {
121+ if (resultStrBuilder.toString().trim().endsWith(k.trim())) {
122+ try {
123+ out.write((v.trim() + "\r").getBytes(StandardCharsets.UTF_8));
124+ out.flush();
125+ resultStrBuilder.append(v.trim() + "\r");
126+ } catch (IOException e) {
127+ }
128+ }
129+ });
130+ }
131+ }
132+ }
133+ 
134+ public synchronized void upload(String source, String target) {
135+ try {
136+ ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
137+ channel.connect();
138+ channel.put(source, target, new SftpProgressMonitor() {
139+ private long count = 0;
140+ // Final file size
141+ private long max = 0;
142+ // The progress of
143+ private long percent = -1;
144+ 
145+ @Override
146+ public void init(int op, String src, String dest, long max) {
147+ this.max = max;
148+ System.out.println(op);
149+ }
150+ 
151+ @Override
152+ public boolean count(long count) {
153+ this.count += count;
154+ if (percent >= this.count * 100 / max) {
155+ return true;
156+ }
157+ percent = this.count * 100 / max;
158+ System.out.println("Completed " + this.count + "(" + percent + "%) out of " + max + ".");
159+ return false;
160+ }
161+ 
162+ @Override
163+ public void end() {
164+ System.out.println("end");
165+ }
166+ }, ChannelSftp.RESUME);
167+ } catch (Exception e) {
168+ log.error("upload fail", e);
169+ throw new RuntimeException(e);
170+ }
171+ }
172+ 
173+ private Session session;
174+ 
175+ private SshSession(String host, Integer port, String username, String password) throws IOException {
176+ JSch jSch = new JSch();
177+ try {
178+ session = jSch.getSession(username, host, port);
179+ } catch (JSchException e) {
180+ throw new OpsException("Connection establishment fail");
181+ }
182+ session.setPassword(password);
183+ session.setConfig("StrictHostKeyChecking", "no");
184+ try {
185+ session.connect(SESSION_TIMEOUT);
186+ } catch (JSchException e) {
187+ throw new OpsException(host + "Connection establishment fail");
188+ }
189+ }
190+ 
191+ public static SshSession connect(String host, Integer port, String username, String password) throws IOException {
192+ return new SshSession(host, port, username, password);
193+ }
194+ 
195+ @Override
196+ public void close() {
197+ session.disconnect();
198+ }
199+}
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/YamlUtil.java+28-0
@@ -0,0 +1,28 @@
1+package com.nctigba.observability.instance.util;
2+ 
3+import org.yaml.snakeyaml.Yaml;
4+import org.yaml.snakeyaml.introspector.Property;
5+import org.yaml.snakeyaml.nodes.NodeTuple;
6+import org.yaml.snakeyaml.nodes.Tag;
7+import org.yaml.snakeyaml.representer.Representer;
8+ 
9+public class YamlUtil {
10+ public static Yaml get() {
11+ return new Yaml(new Representer() {
12+ @Override
13+ protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
14+ Tag customTag) {
15+ return propertyValue == null ? null
16+ : super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
17+ }
18+ });
19+ }
20+ 
21+ public static <T> T loadAs(String yaml, Class<T> type) {
22+ return get().loadAs(yaml, type);
23+ }
24+ 
25+ public static String dump(Object obj) {
26+ return get().dumpAsMap(obj);
27+ }
28+}
Mplugins/observability-instance/src/main/resources/application-dev.yml+0-4
@@ -1,7 +1,3 @@
1spring:1spring:
2 autoconfigure:2 autoconfigure:
3 exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration3 exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
4- 
5-prometheus:
6- server:
7- url: http://172.16.107.108:9090
Mplugins/observability-instance/src/main/resources/application-prod.yml+0-3
@@ -1,3 +0,0 @@
1-prometheus:
2- server:
3- url: http://119.3.170.242:9090
Mplugins/observability-instance/src/main/resources/application.yml+1-1
@@ -31,7 +31,7 @@ server.compression:
31 mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain31 mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
32mybatis-plus:32mybatis-plus:
33 # 搜索指定包别名33 # 搜索指定包别名
34- typeAliasesPackage: com.nctigba.observability.entity34+ typeAliasesPackage: com.nctigba.observability.instance.entity
35 # 配置mapper的扫描,找到所有的mapper.xml映射文件35 # 配置mapper的扫描,找到所有的mapper.xml映射文件
36 mapperLocations: classpath*:mapper/*.xml36 mapperLocations: classpath*:mapper/*.xml
37 # 加载全局的配置文件37 # 加载全局的配置文件
Aplugins/observability-instance/src/main/resources/og_exporter.yml+2468-0
Aplugins/observability-instance/src/main/resources/schema-all.sql+8-0
@@ -0,0 +1,8 @@
1+CREATE TABLE public.nctigba_env (
2+ id varchar NULL,
3+ hostid varchar NULL,
4+ "type" varchar NULL,
5+ username varchar NULL,
6+ "path" varchar NULL,
7+ port int8 NULL
8+);
Mplugins/observability-instance/web-ui/.prettierrc.json+1-0
@@ -1,3 +1,4 @@
1{1{
2+ "printWidth": 800,
2 "tabWidth": 43 "tabWidth": 4
3}4}
Mplugins/observability-instance/web-ui/package-lock.json+44-8832
Mplugins/observability-instance/web-ui/package.json+1-0
@@ -19,6 +19,7 @@
19 "dayjs": "^1.11.5",19 "dayjs": "^1.11.5",
20 "echarts": "^5.4.0",20 "echarts": "^5.4.0",
21 "element-plus": "^2.2.15",21 "element-plus": "^2.2.15",
22+ "jsencrypt": "^3.0.0-rc.1",
22 "lodash-es": "^4.17.21",23 "lodash-es": "^4.17.21",
23 "md5": "^2.3.0",24 "md5": "^2.3.0",
24 "moment": "^2.29.4",25 "moment": "^2.29.4",
Aplugins/observability-instance/web-ui/src/assets/style/common.scss+52-0
@@ -0,0 +1,52 @@
1+@use "sass:math";
2+@use "./color.scss" as *;
3+@use "sass:map";
4+ 
5+// table
6+.normal-table {
7+ .operate-btns {
8+ display: flex;
9+ justify-content: space-around;
10+ }
11+}
12+.search-form {
13+ display: flex;
14+ justify-content: flex-end;
15+ align-items: center;
16+ margin-bottom: 20px;
17+ .search-time-range {
18+ width: 300px;
19+ }
20+ .filter {
21+ display: flex;
22+ align-items: center;
23+ }
24+ .filter:not(:last-child) {
25+ margin-right: 15px;
26+ }
27+ .seperator {
28+ margin: auto;
29+ }
30+}
31+.search-form-multirow {
32+ .row {
33+ display: flex;
34+ justify-content: flex-end;
35+ align-items: center;
36+ .search-time-range {
37+ width: 300px;
38+ }
39+ .filter {
40+ display: flex;
41+ align-items: center;
42+ }
43+ .filter:not(:last-child) {
44+ margin-right: 15px;
45+ }
46+ .seperator {
47+ margin: auto;
48+ }
49+ margin-bottom: 10px;
50+ }
51+ margin-bottom: 10px;
52+}
Mplugins/observability-instance/web-ui/src/assets/style/dark.scss+5-4
@@ -63,14 +63,14 @@ html.dark {
63 --color-text-2: rgb(79, 91, 107);63 --color-text-2: rgb(79, 91, 107);
64 --color-text-3: rgb(134, 143, 156);64 --color-text-3: rgb(134, 143, 156);
65 --el-dialog-background-color:rgb(42,42,43);65 --el-dialog-background-color:rgb(42,42,43);
66- --primary-6: #e41d1d;66+ --primary-6: rgb(252,239,146);
67 --main-background-color:rgb(35,35,36);67 --main-background-color:rgb(35,35,36);
68 --color-neutral-2: rgb(52, 52, 53);68 --color-neutral-2: rgb(52, 52, 53);
69 --color-fill-2: var(--color-neutral-2);69 --color-fill-2: var(--color-neutral-2);
70 --color-text-2: hsla(0,0%,100%,.7);70 --color-text-2: hsla(0,0%,100%,.7);
71 --gray-9: rgb(223, 223, 223);71 --gray-9: rgb(223, 223, 223);
72 --color-secondary: rgba(223, 223, 223, 0.08);72 --color-secondary: rgba(223, 223, 223, 0.08);
73- --color-primary-light-1: rgba(228, 29, 29,0.2);73+ --color-primary-light-1: rgba(252, 239, 146,0.2);
74 --el-color-primary: var(--primary-6);74 --el-color-primary: var(--primary-6);
75 --el-color-primary-light-3: var(--primary-6);75 --el-color-primary-light-3: var(--primary-6);
76 --el-menu-text-color: #cfcfcf;76 --el-menu-text-color: #cfcfcf;
@@ -89,16 +89,17 @@ html.dark {
89 --el-menu-hover-bg-color: #323e43;89 --el-menu-hover-bg-color: #323e43;
90 90 
91 --el-dialog-background-color:rgb(42,42,43);91 --el-dialog-background-color:rgb(42,42,43);
92- --primary-6: #e41d1d;92+ --primary-6: rgb(252,239,146);
93 --main-background-color:rgb(35,35,36);93 --main-background-color:rgb(35,35,36);
94 --color-neutral-2: rgb(52, 52, 53);94 --color-neutral-2: rgb(52, 52, 53);
95 --color-fill-2: var(--color-neutral-2);95 --color-fill-2: var(--color-neutral-2);
96 --color-text-2: hsla(0,0%,100%,.7);96 --color-text-2: hsla(0,0%,100%,.7);
97 --gray-9: rgb(223, 223, 223);97 --gray-9: rgb(223, 223, 223);
98 --color-secondary: rgba(223, 223, 223, 0.08);98 --color-secondary: rgba(223, 223, 223, 0.08);
99- --color-primary-light-1: rgba(228, 29, 29,0.2);99+ --color-primary-light-1: rgba(252, 239, 146,0.2);
100 --el-color-primary: var(--primary-6);100 --el-color-primary: var(--primary-6);
101 --el-color-primary-light-3: var(--primary-6);101 --el-color-primary-light-3: var(--primary-6);
102 --el-table-cell-center-background: #424242;102 --el-table-cell-center-background: #424242;
103 --el-color-primary-light-3: var(--primary-6);103 --el-color-primary-light-3: var(--primary-6);
104+ --color-bg-2: #232324;
104}105}
Mplugins/observability-instance/web-ui/src/assets/style/reset.scss+1-4
@@ -153,9 +153,6 @@ body {
153.el-picker-panel__icon-btn .el-icon{153.el-picker-panel__icon-btn .el-icon{
154 color:var(--el-text-color-og);154 color:var(--el-text-color-og);
155}155}
156-.el-picker__popper {
157- left: calc(100% - 655px) !important;
158-}
159.in-active-path .el-cascader-node__label {156.in-active-path .el-cascader-node__label {
160 color: var(--el-color-tabbar-active) !important;157 color: var(--el-color-tabbar-active) !important;
161}158}
@@ -224,7 +221,7 @@ body {
224 221 
225// button for message box222// button for message box
226.el-message-box__btns .el-button.el-button--primary {223.el-message-box__btns .el-button.el-button--primary {
227- color: #fff !important;224+ color: var(--color-bg-2) !important;
228 background-color: var(--primary-6) !important;225 background-color: var(--primary-6) !important;
229}226}
230.el-message-box__btns .el-button.el-button--primary.search-button {227.el-message-box__btns .el-button.el-button--primary.search-button {
Aplugins/observability-instance/web-ui/src/assets/style/style1.scss+60-0
@@ -0,0 +1,60 @@
1+@use 'sass:math';
2+@use "./color.scss" as *;
3+@use 'sass:map';
4+ 
5+.el-link.el-link--primary{
6+ color: var(--primary-6) !important;
7+}
8+.el-button {
9+ color: var(--color-text-2) !important;
10+ background-color: var(--color-secondary) !important;
11+ border: none !important;
12+}
13+.el-button.el-button--primary {
14+ color: var(--color-bg-2) !important;
15+ background-color: var(--primary-6) !important;
16+}
17+.el-button.el-button--primary.search-button {
18+ color: var(--primary-6) !important;
19+ background-color: rgb(255, 255, 255, 0) !important;
20+ border: 1px solid var(--primary-6) !important;
21+}
22+:deep(.el-pagination) {
23+ display: flex;
24+ justify-content: flex-end;
25+}
26+.deleteBtn {
27+ color: #d4d4d4;
28+}
29+.deleteBtn-icon {
30+ position: relative;
31+ top: 2px;
32+ right: 5px;
33+}
34+:deep(.el-range-editor--small.el-input__wrapper) {
35+ width: 240px;
36+}
37+ 
38+.dialog {
39+ &:deep(.el-dialog .el-dialog__header) {
40+ text-align: center;
41+ }
42+ &:deep(.el-form-item--small .el-form-item__label) {
43+ width: 110px;
44+ }
45+ &:deep(.el-dialog .el-dialog__footer) {
46+ border: none !important;
47+ }
48+ .form-textarea {
49+ width: 100%;
50+ &:deep(.el-textarea__inner) {
51+ height: 100px;
52+ }
53+ }
54+ .dialog-content {
55+ padding-bottom: 180px;
56+ }
57+ .option-wrap {
58+ margin-right: 20px;
59+ }
60+}
Mplugins/observability-instance/web-ui/src/assets/style/theme.scss+1-0
@@ -81,4 +81,5 @@ html {
81 --el-color-primary: var(--primary-6);81 --el-color-primary: var(--primary-6);
82 --el-color-primary-light-3: var(--primary-6);82 --el-color-primary-light-3: var(--primary-6);
83 --el-table-cell-center-background: #ffffff;83 --el-table-cell-center-background: #ffffff;
84+ --color-bg-2: #fff;
84}85}
Mplugins/observability-instance/web-ui/src/components.d.ts+11-0
@@ -7,12 +7,16 @@ export {}
7 7 
8declare module '@vue/runtime-core' {8declare module '@vue/runtime-core' {
9 export interface GlobalComponents {9 export interface GlobalComponents {
10+ ClusterCascader: typeof import('./components/ClusterCascader.vue')['default']
11+ ElAside: typeof import('element-plus/es')['ElAside']
12+ ElAvatar: typeof import('element-plus/es')['ElAvatar']
10 ElButton: typeof import('element-plus/es')['ElButton']13 ElButton: typeof import('element-plus/es')['ElButton']
11 ElCascader: typeof import('element-plus/es')['ElCascader']14 ElCascader: typeof import('element-plus/es')['ElCascader']
12 ElCheckbox: typeof import('element-plus/es')['ElCheckbox']15 ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
13 ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']16 ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
14 ElCol: typeof import('element-plus/es')['ElCol']17 ElCol: typeof import('element-plus/es')['ElCol']
15 ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']18 ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
19+ ElContainer: typeof import('element-plus/es')['ElContainer']
16 ElDatePicker: typeof import('element-plus/es')['ElDatePicker']20 ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
17 ElDialog: typeof import('element-plus/es')['ElDialog']21 ElDialog: typeof import('element-plus/es')['ElDialog']
18 ElDropdown: typeof import('element-plus/es')['ElDropdown']22 ElDropdown: typeof import('element-plus/es')['ElDropdown']
@@ -23,19 +27,25 @@ declare module '@vue/runtime-core' {
23 ElIcon: typeof import('element-plus/es')['ElIcon']27 ElIcon: typeof import('element-plus/es')['ElIcon']
24 ElInput: typeof import('element-plus/es')['ElInput']28 ElInput: typeof import('element-plus/es')['ElInput']
25 ElLink: typeof import('element-plus/es')['ElLink']29 ElLink: typeof import('element-plus/es')['ElLink']
30+ ElMain: typeof import('element-plus/es')['ElMain']
26 ElMenu: typeof import('element-plus/es')['ElMenu']31 ElMenu: typeof import('element-plus/es')['ElMenu']
27 ElMenuItem: typeof import('element-plus/es')['ElMenuItem']32 ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
28 ElOption: typeof import('element-plus/es')['ElOption']33 ElOption: typeof import('element-plus/es')['ElOption']
29 ElPagination: typeof import('element-plus/es')['ElPagination']34 ElPagination: typeof import('element-plus/es')['ElPagination']
35+ ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
30 ElPopover: typeof import('element-plus/es')['ElPopover']36 ElPopover: typeof import('element-plus/es')['ElPopover']
31 ElRow: typeof import('element-plus/es')['ElRow']37 ElRow: typeof import('element-plus/es')['ElRow']
32 ElSelect: typeof import('element-plus/es')['ElSelect']38 ElSelect: typeof import('element-plus/es')['ElSelect']
39+ ElStep: typeof import('element-plus/es')['ElStep']
40+ ElSteps: typeof import('element-plus/es')['ElSteps']
33 ElSubMenu: typeof import('element-plus/es')['ElSubMenu']41 ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
34 ElTable: typeof import('element-plus/es')['ElTable']42 ElTable: typeof import('element-plus/es')['ElTable']
35 ElTableColumn: typeof import('element-plus/es')['ElTableColumn']43 ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
36 ElTabPane: typeof import('element-plus/es')['ElTabPane']44 ElTabPane: typeof import('element-plus/es')['ElTabPane']
37 ElTabs: typeof import('element-plus/es')['ElTabs']45 ElTabs: typeof import('element-plus/es')['ElTabs']
38 ElTooltip: typeof import('element-plus/es')['ElTooltip']46 ElTooltip: typeof import('element-plus/es')['ElTooltip']
47+ ElTree: typeof import('element-plus/es')['ElTree']
48+ Machines: typeof import('./components/Machines.vue')['default']
39 MonacoEditor: typeof import('./components/MonacoEditor.vue')['default']49 MonacoEditor: typeof import('./components/MonacoEditor.vue')['default']
40 MyBar: typeof import('./components/MyBar.vue')['default']50 MyBar: typeof import('./components/MyBar.vue')['default']
41 MyBarLine: typeof import('./components/MyBarLine.vue')['default']51 MyBarLine: typeof import('./components/MyBarLine.vue')['default']
@@ -50,6 +60,7 @@ declare module '@vue/runtime-core' {
50 MyPie: typeof import('./components/MyPie.vue')['default']60 MyPie: typeof import('./components/MyPie.vue')['default']
51 MyProgress: typeof import('./components/MyProgress.vue')['default']61 MyProgress: typeof import('./components/MyProgress.vue')['default']
52 MyTable: typeof import('./components/MyTable.vue')['default']62 MyTable: typeof import('./components/MyTable.vue')['default']
63+ Proxies: typeof import('./components/Proxies.vue')['default']
53 RouterLink: typeof import('vue-router')['RouterLink']64 RouterLink: typeof import('vue-router')['RouterLink']
54 RouterView: typeof import('vue-router')['RouterView']65 RouterView: typeof import('vue-router')['RouterView']
55 SvgIcon: typeof import('./components/SvgIcon.vue')['default']66 SvgIcon: typeof import('./components/SvgIcon.vue')['default']
Aplugins/observability-instance/web-ui/src/components/ClusterCascader.vue+91-0
@@ -0,0 +1,91 @@
1+<template>
2+ <div class="filter">
3+ <span>{{ props.title }}&nbsp;</span>
4+ <el-cascader v-model="cluster" :options="clusterList" @change="getClusterValue" style="max-width: 200px" :style="{width:width?width+'px':'auto'}" :clearable="!notClearable" />
5+ </div>
6+</template>
7+ 
8+<script lang="ts" setup>
9+import ogRequest from "../request";
10+import { useRequest } from "vue-request";
11+ 
12+type Rer =
13+ | [
14+ {
15+ [propName: string]: string | number;
16+ }
17+ ]
18+ | undefined;
19+const props = withDefaults(
20+ defineProps<{
21+ title?: string;
22+ width?: string;
23+ instanceValueKey?: string;
24+ notClearable?: boolean;
25+ clusterOnly?: boolean;
26+ autoSelectFirst?: boolean; // now only support one level
27+ }>(),
28+ {
29+ title: "",
30+ width: "",
31+ instanceValueKey: "nodeId",
32+ notClearable: false,
33+ clusterOnly: false,
34+ autoSelectFirst: false,
35+ }
36+);
37+const emit = defineEmits(["getCluster", "loaded"]);
38+ 
39+const cluster = ref<Array<any>>([]);
40+const clusterList = ref<Array<any>>([]);
41+ 
42+const treeTransform = (arr: any) => {
43+ let obj: any = [];
44+ if (arr instanceof Array) {
45+ arr.forEach((item) => {
46+ obj.push({
47+ label: item.clusterId ? item.clusterId : item.azName + "_" + item.publicIp,
48+ value: item.clusterId ? item.clusterId : item[props.instanceValueKey],
49+ children: props.clusterOnly ? null : treeTransform(item.clusterNodes),
50+ });
51+ });
52+ // now only support one level
53+ if (props.autoSelectFirst && obj.length > 0) {
54+ if (props.clusterOnly) cluster.value = [obj[0].value];
55+ else if (obj[0].children.length > 0) cluster.value = [obj[0].value, obj[0].children[0].value];
56+ emit("getCluster", cluster.value);
57+ }
58+ }
59+ return obj;
60+};
61+const getClusterValue = (val: string[]) => {
62+ console.log("getClusterValue");
63+ if (val == null) emit("getCluster", []);
64+ else emit("getCluster", val);
65+};
66+ 
67+onMounted(() => {
68+ clusterData();
69+});
70+const { data: rer, run: clusterData } = useRequest(
71+ () => {
72+ return ogRequest.get("/observability/v1/topsql/cluster", "");
73+ },
74+ { manual: true }
75+);
76+ 
77+watch(rer, (rer: Rer) => {
78+ if (rer && Object.keys(rer).length) {
79+ clusterList.value = treeTransform(rer);
80+ emit("loaded");
81+ }
82+});
83+</script>
84+ 
85+<style lang="scss" scoped>
86+.filter {
87+ display: flex;
88+ flex-wrap: nowrap;
89+ align-items: center;
90+}
91+</style>
Aplugins/observability-instance/web-ui/src/components/Machines.vue+54-0
@@ -0,0 +1,54 @@
1+<template>
2+ <div>
3+ <el-select v-model="machineValue" @change="selectMachine" :clearable="!notClearable" :style="{ width: width ? width + 'px' : 'auto' }">
4+ <el-option v-for="item in machineList" :key="item.hostId" :label="item.privateIp + '(' + item.publicIp + ')'" :value="item.hostId" />
5+ </el-select>
6+ </div>
7+</template>
8+ 
9+<script lang="ts" setup>
10+import restRequest from "../request/restful";
11+import { useRequest } from "vue-request";
12+ 
13+const props = withDefaults(
14+ defineProps<{
15+ width?: string;
16+ notClearable?: boolean;
17+ autoSelectFirst?: boolean;
18+ }>(),
19+ {
20+ width: "",
21+ notClearable: false,
22+ autoSelectFirst: false,
23+ }
24+);
25+const emit = defineEmits(["change", "loaded"]);
26+ 
27+const machineValue = ref<any>();
28+const machineList = ref<Array<any>>([]);
29+ 
30+const selectMachine = (val: string[]) => {
31+ emit("change", machineValue);
32+};
33+ 
34+onMounted(() => {
35+ clusterData();
36+});
37+const { data: rer, run: clusterData } = useRequest(
38+ () => {
39+ return restRequest.get("/observability/v1/environment/hosts", "");
40+ },
41+ { manual: true }
42+);
43+ 
44+watch(rer, (rer) => {
45+ if (rer.length) {
46+ machineList.value = rer;
47+ emit("loaded");
48+ if (props.autoSelectFirst && machineList.value.length > 0) {
49+ machineValue.value = machineList.value[0].hostId;
50+ emit("change", machineValue.value);
51+ }
52+ }
53+});
54+</script>
Aplugins/observability-instance/web-ui/src/components/Proxies.vue+54-0
@@ -0,0 +1,54 @@
1+<template>
2+ <div>
3+ <el-select v-model="machineValue" @change="selectMachine" :clearable="!notClearable" :style="{ width: width ? width + 'px' : 'auto' }">
4+ <el-option v-for="item in machineList" :key="item.hostId" :label="item.privateIp + '(' + item.publicIp + ')'" :value="item.hostId" />
5+ </el-select>
6+ </div>
7+</template>
8+ 
9+<script lang="ts" setup>
10+import ogRequest from "../request";
11+import { useRequest } from "vue-request";
12+ 
13+const props = withDefaults(
14+ defineProps<{
15+ width?: string;
16+ notClearable?: boolean;
17+ autoSelectFirst?: boolean;
18+ }>(),
19+ {
20+ width: "",
21+ notClearable: false,
22+ autoSelectFirst: false,
23+ }
24+);
25+const emit = defineEmits(["change", "loaded"]);
26+ 
27+const machineValue = ref<any>();
28+const machineList = ref<Array<any>>([]);
29+ 
30+const selectMachine = (val: string[]) => {
31+ emit("change", machineValue);
32+};
33+ 
34+onMounted(() => {
35+ clusterData();
36+});
37+const { data: rer, run: clusterData } = useRequest(
38+ () => {
39+ return ogRequest.get("/observability/v1/environment/prometheus", "");
40+ },
41+ { manual: true }
42+);
43+ 
44+watch(rer, (rer) => {
45+ if (rer && rer.data && Object.keys(rer.data).length) {
46+ machineList.value = rer.data.rows;
47+ emit("loaded");
48+ if (props.autoSelectFirst && machineList.value.length > 0) {
49+ machineValue.value = machineList.value[0].hostId;
50+ emit("change", machineValue.value);
51+ }
52+ }
53+});
54+</script>
Mplugins/observability-instance/web-ui/src/i18n/en.ts+41-1
@@ -16,7 +16,16 @@ export default {
16 cancel: 'cancel',16 cancel: 'cancel',
17 confirm: 'confirm',17 confirm: 'confirm',
18 edit: 'edit',18 edit: 'edit',
19- operate: 'operate'19+ operate: 'operate',
20+ reset: 'Reset',
21+ back: 'Back'
22+ },
23+ install: {
24+ install: 'Install',
25+ installAgent: 'Install Collector',
26+ installProxy: 'Install Proxy',
27+ installedAgent: 'Installed Collector',
28+ installedProxy: 'Installed Proxy'
20 },29 },
21 dashboard: {30 dashboard: {
22 name: 'Dashboard',31 name: 'Dashboard',
@@ -35,6 +44,37 @@ export default {
35 ],44 ],
36 instance: 'Instance Monitoring',45 instance: 'Instance Monitoring',
37 load: 'Performance Load',46 load: 'Performance Load',
47+ systemConfig: {
48+ tabName: 'System Configuration',
49+ osTabName: 'System Parameter',
50+ dbTabName: 'DB Parameter',
51+ },
52+ wdrReports: {
53+ tabName: 'WDR Reports',
54+ clusterName: 'Cluster Name',
55+ hostId: 'Host IP',
56+ reportRange: 'Report Scope',
57+ reportType: 'Report Type',
58+ buildTime: 'Production time',
59+ snapshotManage: 'Snapshot Manage',
60+ buildWDR: 'Generate WDR',
61+ list: {
62+ buildTime: 'Report Generation Time',
63+ reportName: 'Report Name'
64+ },
65+ snapshotManageDialog: {
66+ dialogName: 'Snapshot Manage',
67+ createSnapshot: 'Create Snapshot',
68+ snapshotID: 'Snapshot ID',
69+ captureTime: 'Capture Time'
70+ },
71+ buildWDRDialog: {
72+ startSnapshot: 'Start Snapshot',
73+ endSnapshot: 'End Snapshot',
74+ build: 'Generate',
75+ buildSuccess: 'Generate suceed'
76+ }
77+ },
38 session: 'Session Management',78 session: 'Session Management',
39 slow: 'Slow SQL',79 slow: 'Slow SQL',
40 space: 'Spatial Analysis',80 space: 'Spatial Analysis',
Mplugins/observability-instance/web-ui/src/i18n/zhCn.ts+69-0
@@ -17,6 +17,42 @@ export default {
17 confirm: '确定',17 confirm: '确定',
18 edit: '编辑',18 edit: '编辑',
19 operate: '操作',19 operate: '操作',
20+ view: '查看',
21+ download: '下载',
22+ reset: '重置',
23+ back: '返回'
24+ },
25+ install: {
26+ install: '一键部署',
27+ installAgent: '安装采集器',
28+ installProxy: '安装代理',
29+ installedAgent: '已安装采集器',
30+ installedProxy: '已安装代理',
31+ machine: '物理机',
32+ rootPWD: 'Root用户密码',
33+ proxyPort: '代理端口',
34+ collectInstance: '采集实例',
35+ collectProxy: '采集代理',
36+ proxyRules: [
37+ '请选择物理机',
38+ '请输入Root用户密码',
39+ '请输入代理端口号'
40+ ],
41+ collectorRules: [
42+ '请选择实例',
43+ '请输入Root用户密码'
44+ ],
45+ },
46+ configParam: {
47+ tabTitle: '系统与数据库配置',
48+ systemConfig: '系统配置',
49+ databaseConfig: '数据库配置',
50+ paramDesc: '参数说明',
51+ paramTuning: '参数调优',
52+ suggestValue: '推荐值:',
53+ suggestReason: '推荐原因:',
54+ rootPWDTitle: '请输入Root用户密码',
55+ rootPWD: 'Root用户密码',
20 },56 },
21 dashboard: {57 dashboard: {
22 name: '实例概览',58 name: '实例概览',
@@ -35,6 +71,39 @@ export default {
35 ],71 ],
36 instance: '实例监控',72 instance: '实例监控',
37 load: '系统负载',73 load: '系统负载',
74+ systemConfig: {
75+ tabName: '系统配置',
76+ osTabName: '系统参数',
77+ dbTabName: '数据库参数',
78+ },
79+ wdrReports: {
80+ tabName: 'WDR报告',
81+ clusterName: '集群名称',
82+ hostId: '主机IP',
83+ reportRange: '报告范围',
84+ reportRangeSelect: ['集群', '节点'],
85+ reportType: '报告类型',
86+ reportTypeSelect: ['明细', '汇总', '全部'],
87+ buildTime: '生产时间',
88+ snapshotManage: '快照管理',
89+ buildWDR: '生成WDR',
90+ list: {
91+ buildTime: '报告生成时间',
92+ reportName: '报告名称'
93+ },
94+ snapshotManageDialog: {
95+ dialogName: '快照管理',
96+ createSnapshot: '创建快照',
97+ snapshotID: '快照ID',
98+ captureTime: '捕获时间'
99+ },
100+ buildWDRDialog: {
101+ startSnapshot: '开始快照',
102+ endSnapshot: '结束快照',
103+ build: '生成',
104+ buildSuccess: 'WDR生成成功'
105+ }
106+ },
38 session: '会话管理',107 session: '会话管理',
39 slow: '慢SQL',108 slow: '慢SQL',
40 top: 'TOPSQL',109 top: 'TOPSQL',
Mplugins/observability-instance/web-ui/src/main.ts+1-0
@@ -6,6 +6,7 @@ import 'element-plus/theme-chalk/el-message.css'
6import 'element-plus/theme-chalk/dark/css-vars.css';6import 'element-plus/theme-chalk/dark/css-vars.css';
7import '@/assets/style/dark.scss'7import '@/assets/style/dark.scss'
8import '@/assets/style/reset.scss';8import '@/assets/style/reset.scss';
9+import '@/assets/style/common.scss';
9import App from "./App.vue";10import App from "./App.vue";
10import { createPinia } from "pinia";11import { createPinia } from "pinia";
11import piniaPluginPersistedstate from "pinia-plugin-persistedstate";12import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
Mplugins/observability-instance/web-ui/src/pages/dashboard/Index.vue+221-204
@@ -1,84 +1,90 @@
1<script setup lang="ts">1<script setup lang="ts">
2-import { Refresh } from '@element-plus/icons-vue';2+import { Fold, Expand } from "@element-plus/icons-vue";
3-import { storeToRefs } from 'pinia';3+import { Refresh } from "@element-plus/icons-vue";
4-import { useI18n } from 'vue-i18n';4+import { storeToRefs } from "pinia";
5-import { useMonitorStore } from '../../store/monitor';5+import { useI18n } from "vue-i18n";
6-import { useWindowStore } from '../../store/window';6+import { useMonitorStore } from "../../store/monitor";
7-import PerformanceLoad from './performance_load/Index.vue';7+import { useWindowStore } from "../../store/window";
8-import TopSql from './top_sql/Index.vue';8+import PerformanceLoad from "./performance_load/Index.vue";
9-import { i18n } from '../../i18n';9+import TopSql from "./top_sql/Index.vue";
10+import Wdr from "./wdr/Index.vue";
11+import { i18n } from "../../i18n";
10import ogRequest from "../../request";12import ogRequest from "../../request";
11import { useRequest } from "vue-request";13import { useRequest } from "vue-request";
14+import Install from "./install/Index.vue";
15+import SystemConfiguration from "./system_configuration/Index.vue";
12 16 
13const { t } = useI18n();17const { t } = useI18n();
14 18 
15-type Res = [{19+type Res =
16- [propName:string]:string | number20+ | [
17-}] | undefined;21+ {
22+ [propName: string]: string | number;
23+ }
24+ ]
25+ | undefined;
18 26 
19-const datePickerRef = ref<HTMLDivElement>()27+const datePickerRef = ref<HTMLDivElement>();
20const clusterNodeId = ref();28const clusterNodeId = ref();
21const clusterList = ref<Array<any>[]>([]);29const clusterList = ref<Array<any>[]>([]);
22const connectStatus = ref<boolean | undefined>(undefined);30const connectStatus = ref<boolean | undefined>(undefined);
23const curServerInfoText = ref("");31const curServerInfoText = ref("");
24const nodeVersion = ref<string>("");32const nodeVersion = ref<string>("");
25 33 
26-const { serverInfoText } = storeToRefs(useWindowStore())34+const { serverInfoText } = storeToRefs(useWindowStore());
27-const { tab, filters, autoRefresh, rangeTime, instanceId } = storeToRefs(useMonitorStore())35+const { tab, filters, autoRefresh, rangeTime, instanceId } = storeToRefs(useMonitorStore());
28// tab render only once36// tab render only once
29-const tabLoaded = reactive([tab.value === 0, tab.value === 1])37+const tabLoaded = reactive([tab.value === 0, tab.value === 1, tab.value === 2, tab.value === 3]);
30-watch(tab, v => {38+watch(tab, (v) => {
31 if (!tabLoaded[v]) {39 if (!tabLoaded[v]) {
32- tabLoaded[v] = true40+ tabLoaded[v] = true;
33 }41 }
34-})42+});
35- 43+const isCollapse = ref(false);
36-// const tabHeaderW = computed(() => i18n.global.locale.value === 'en' ? 'calc(100% - 723px)' : 'calc(100% - 678px)')44+const toggleCollapse = () => {
45+ isCollapse.value = !isCollapse.value;
46+};
37 47 
38const autoRefreshFn = () => {48const autoRefreshFn = () => {
39- autoRefresh.value = !autoRefresh.value49+ autoRefresh.value = !autoRefresh.value;
40-}50+};
41 51 
42-const { data: opsClusterData } = useRequest(() =>52+const { data: opsClusterData } = useRequest(() => ogRequest.get("/observability/v1/topsql/cluster"), { manual: false });
43- ogRequest.get("/observability/v1/topsql/cluster"), { manual: false }
44-)
45 53 
46-const { data: connectStatusData, run: runConnectStatus, loading: connectStatusLoadding } = useRequest((nodeId: String) =>54+const { data: connectStatusData, run: runConnectStatus, loading: connectStatusLoadding } = useRequest((nodeId: String) => ogRequest.get(`/observability/v1/topsql/connect/${nodeId}`), { manual: true });
47- ogRequest.get(`/observability/v1/topsql/connect/${nodeId}`), { manual: true }
48-)
49 55 
50-const treeTransform = (arr:any) => {56+const treeTransform = (arr: any) => {
51- let obj:any = [];57+ let obj: any = [];
52 if (arr instanceof Array) {58 if (arr instanceof Array) {
53- arr.forEach(item => {59+ arr.forEach((item) => {
54 // init current cluster node60 // init current cluster node
55 if (item.nodeId && item.nodeId === instanceId.value) {61 if (item.nodeId && item.nodeId === instanceId.value) {
56 clusterNodeId.value = instanceId.value;62 clusterNodeId.value = instanceId.value;
57 }63 }
58 obj.push({64 obj.push({
59- label: item.clusterId ? item.clusterId : item.azName + '_' + item.publicIp + '(' + item.nodeId + ')',65+ label: item.clusterId ? item.clusterId : item.azName + "_" + item.publicIp + "(" + item.nodeId + ")",
60 value: item.clusterId ? item.clusterId : item.nodeId,66 value: item.clusterId ? item.clusterId : item.nodeId,
61- children: treeTransform(item.clusterNodes)67+ children: treeTransform(item.clusterNodes),
62- })68+ });
63- })69+ });
64 }70 }
65 return obj;71 return obj;
66-}72+};
67 73 
68const showConnectStatus = (status: boolean | undefined) => {74const showConnectStatus = (status: boolean | undefined) => {
69 if (status === undefined) {75 if (status === undefined) {
70- return '';76+ return "";
71 }77 }
72- return status ? t('dashboard.connectStatus.success') : t('dashboard.connectStatus.error');78+ return status ? t("dashboard.connectStatus.success") : t("dashboard.connectStatus.error");
73-}79+};
74 80 
75const onDatePackerVisible = (v: boolean) => {81const onDatePackerVisible = (v: boolean) => {
76 if (!v) {82 if (!v) {
77 const docu = document.getElementsByClassName("el-range-input");83 const docu = document.getElementsByClassName("el-range-input");
78 // @ts-ignore84 // @ts-ignore
79- docu[0]?.blur()85+ docu[0]?.blur();
80 }86 }
81-}87+};
82 88 
83const getVersionByNodeId = (curNodeId: string) => {89const getVersionByNodeId = (curNodeId: string) => {
84 if (!Array.isArray(opsClusterData.value)) {90 if (!Array.isArray(opsClusterData.value)) {
@@ -97,18 +103,17 @@ const getVersionByNodeId = (curNodeId: string) => {
97 }103 }
98 }104 }
99 return "";105 return "";
100-}106+};
101 107 
102-// 集群与主机IP108+watch(opsClusterData, (res: Res) => {
103-watch(opsClusterData, (res:Res) => {
104 if (res && Object.keys(res).length) {109 if (res && Object.keys(res).length) {
105 clusterList.value = treeTransform(res);110 clusterList.value = treeTransform(res);
106 }111 }
107-})112+});
108 113 
109-watch(clusterNodeId, res => {114+watch(clusterNodeId, (res) => {
110 let curInstanceId = instanceId.value;115 let curInstanceId = instanceId.value;
111- if (typeof res === 'string') {116+ if (typeof res === "string") {
112 curInstanceId = res;117 curInstanceId = res;
113 } else if (Array.isArray(res) && res.length > 0) {118 } else if (Array.isArray(res) && res.length > 0) {
114 curInstanceId = res[res.length - 1];119 curInstanceId = res[res.length - 1];
@@ -122,191 +127,203 @@ watch(clusterNodeId, res => {
122 nodeVersion.value = getVersionByNodeId(curInstanceId);127 nodeVersion.value = getVersionByNodeId(curInstanceId);
123});128});
124 129 
125-watch(connectStatusData, res => {130+watch(connectStatusData, (res) => {
126 connectStatus.value = res;131 connectStatus.value = res;
127-})132+});
128 133 
129-watch(rangeTime, r => {134+watch(rangeTime, (r) => {
130 if (r !== -1) {135 if (r !== -1) {
131- filters.value[tab.value].time = null136+ filters.value[tab.value].time = null;
132 }137 }
133-})138+});
134 139 
135-watch(serverInfoText, val => {140+watch(serverInfoText, (val) => {
136- if (typeof val === 'string' && val !== '') {141+ if (typeof val === "string" && val !== "") {
137 curServerInfoText.value = val;142 curServerInfoText.value = val;
138 } else {143 } else {
139- curServerInfoText.value = '';144+ curServerInfoText.value = "";
140 }145 }
141-})146+});
142</script>147</script>
143 148 
144<template>149<template>
145 <div class="tab-wrapper" :key="clusterNodeId">150 <div class="tab-wrapper" :key="clusterNodeId">
146- <el-tabs v-model="tab">151+ <el-container>
147- <div class="tab-wrapper-container">152+ <el-aside :width="isCollapse ? '0px' : '300px'">
148- <div class="cluster-container">153+ <div style="height: 23px"></div>
149- <div class="cluster-container-title">{{ $t('datasource.cluterTitle') }}</div>154+ <Install />
150- <el-cascader v-model="clusterNodeId" :options="clusterList" />155+ </el-aside>
151- <div v-if="false" class="divider" />156+ <el-main style="position: relative">
152- <div class="cluster-info-loading" v-if="false && connectStatusLoadding" v-loading="connectStatusLoadding"></div>157+ <div>
153- <div class="cluster-info" v-if="false && connectStatus !== undefined && !connectStatusLoadding">158+ <div style="position: absolute; left: 10px; top: 31px; z-index: 9999" @click="toggleCollapse">
154- <span class="cluster-info-light" :style="{backgroundColor: connectStatus ? 'green' : 'red'}" />159+ <el-icon v-if="!isCollapse" size="20px"><Fold /></el-icon>
155- <span>{{ showConnectStatus(connectStatus) }}</span>160+ <el-icon v-if="isCollapse" size="20px"><Expand /></el-icon>
156 </div>161 </div>
157- <div v-if="false && curServerInfoText !== ''" class="divider" />
158- <div v-if="false && curServerInfoText !== ''">{{ serverInfoText }}</div>
159 </div>162 </div>
160- <div class="tab-wrapper-filter">163+ <el-tabs v-model="tab">
161- <span>{{ $t('app.autoRefresh') }}:</span>164+ <div class="tab-wrapper-container" v-show="tab === 0 || tab === 1">
162- <el-select v-model="filters[tab].refreshTime" style="width: 60px;margin: 0 4px;">165+ <div class="cluster-container">
163- <el-option :value="15" label="15s" />166+ <div class="cluster-container-title">{{ $t("datasource.cluterTitle") }}</div>
164- <el-option :value="30" label="30s" />167+ <el-cascader v-model="clusterNodeId" :options="clusterList" />
165- <el-option :value="60" label="60s" />168+ <div v-if="false" class="divider" />
166- </el-select>169+ <div class="cluster-info-loading" v-if="false && connectStatusLoadding" v-loading="connectStatusLoadding"></div>
167- <el-button type="primary" :icon="Refresh" style="padding: 8px;" @click="autoRefreshFn" />170+ <div class="cluster-info" v-if="false && connectStatus !== undefined && !connectStatusLoadding">
168- <div class="divider"></div>171+ <span class="cluster-info-light" :style="{ backgroundColor: connectStatus ? 'green' : 'red' }" />
169- <span>{{ $t('dashboard.range') }}:</span>172+ <span>{{ showConnectStatus(connectStatus) }}</span>
170- <el-select v-model="filters[tab].rangeTime" :style="{width: i18n.global.locale.value === 'en' ? '115px' : '85px'}">173+ </div>
171- <el-option :value="1" :label="$t('dashboard.last1H')" />174+ <div v-if="false && curServerInfoText !== ''" class="divider" />
172- <el-option :value="12" :label="$t('dashboard.last12H')" />175+ <div v-if="false && curServerInfoText !== ''">{{ serverInfoText }}</div>
173- <el-option :value="24" :label="$t('dashboard.last1D')" />176+ </div>
174- <el-option :value="48" :label="$t('dashboard.last2D')" />177+ <div class="tab-wrapper-filter">
175- <el-option :value="168" :label="$t('dashboard.last7D')" />178+ <span>{{ $t("app.autoRefresh") }}:</span>
176- <el-option :value="-1" :label="$t('app.custom')" />179+ <el-select v-model="filters[tab].refreshTime" style="width: 60px; margin: 0 4px">
177- </el-select>180+ <el-option :value="15" label="15s" />
178- <el-date-picker181+ <el-option :value="30" label="30s" />
179- ref="datePickerRef"182+ <el-option :value="60" label="60s" />
180- :disabled="filters[tab].rangeTime !== -1"183+ </el-select>
181- type="datetimerange"184+ <el-button type="primary" :icon="Refresh" style="padding: 8px" @click="autoRefreshFn" />
182- v-model="filters[tab].time"185+ <div class="divider"></div>
183- :start-placeholder="$t('app.startDate')"186+ <span>{{ $t("dashboard.range") }}:</span>
184- :end-placeholder="$t('app.endDate')"187+ <el-select v-model="filters[tab].rangeTime" :style="{ width: i18n.global.locale.value === 'en' ? '115px' : '85px' }">
185- :range-separator="$t('app.to')"188+ <el-option :value="1" :label="$t('dashboard.last1H')" />
186- @visible-change="onDatePackerVisible"189+ <el-option :value="12" :label="$t('dashboard.last12H')" />
187- />190+ <el-option :value="24" :label="$t('dashboard.last1D')" />
188- </div>191+ <el-option :value="48" :label="$t('dashboard.last2D')" />
189- </div>192+ <el-option :value="168" :label="$t('dashboard.last7D')" />
190- 193+ <el-option :value="-1" :label="$t('app.custom')" />
191- <el-tab-pane :label="$t('dashboard.load')" :name="0">194+ </el-select>
192- <performance-load v-if="tabLoaded[0] || tab === 0" :nodeVersion="nodeVersion" />195+ <el-date-picker ref="datePickerRef" :disabled="filters[tab].rangeTime !== -1" type="datetimerange" v-model="filters[tab].time" :start-placeholder="$t('app.startDate')" :end-placeholder="$t('app.endDate')" :range-separator="$t('app.to')" @visible-change="onDatePackerVisible" />
193- </el-tab-pane>196+ </div>
194- <el-tab-pane :label="$t('dashboard.top')" :name="1">197+ </div>
195- <top-sql v-if="tabLoaded[1] || tab === 1" :instanceId="instanceId" />198+ 
196- </el-tab-pane>199+ <el-tab-pane :label="$t('dashboard.load')" :name="0">
197- </el-tabs>200+ <performance-load v-if="tabLoaded[0] || tab === 0" :nodeVersion="nodeVersion" />
201+ </el-tab-pane>
202+ <el-tab-pane :label="$t('dashboard.top')" :name="1">
203+ <top-sql v-if="tabLoaded[1] || tab === 1" :instanceId="instanceId" />
204+ </el-tab-pane>
205+ <el-tab-pane :label="$t('dashboard.wdrReports.tabName')" :name="2">
206+ <wdr v-if="tabLoaded[2] || tab === 2" :instanceId="instanceId" />
207+ </el-tab-pane>
208+ <el-tab-pane :label="$t('dashboard.systemConfig.tabName')" :name="3">
209+ <system-configuration v-if="tabLoaded[3] || tab === 3" :instanceId="instanceId" />
210+ </el-tab-pane>
211+ </el-tabs>
212+ </el-main>
213+ </el-container>
198 </div>214 </div>
199</template>215</template>
200 216 
201<style scoped lang="scss">217<style scoped lang="scss">
218+.cluster-container {
219+ height: 40px;
220+ // background-color: var(--el-bg-color-sub);
221+ padding: 0 16px;
222+ display: flex;
223+ align-items: center;
202 224 
203- .cluster-container {225+ &-title {
204- height: 40px;226+ font-size: 14px;
205- // background-color: var(--el-bg-color-sub);227+ margin-right: 10px;
206- padding: 0 16px;
207- display: flex;
208- align-items: center;
209- 
210- &-title {
211- font-size: 14px;
212- margin-right: 10px;
213- }
214- 
215- :deep(.el-cascader) {
216- width: 210px;
217- }
218- 
219- :deep(.el-input__wrapper) {
220- border-radius: 5px;
221- font-size: 12px;
222- font-weight: 700;
223- }
224 }228 }
225 229 
226- .cluster-info {230+ :deep(.el-cascader) {
227- height: inherit;231+ width: 210px;
228- display: flex;232+ }
229- align-items: center;233+ 
234+ :deep(.el-input__wrapper) {
235+ border-radius: 5px;
230 font-size: 12px;236 font-size: 12px;
237+ font-weight: 700;
238+ }
239+}
231 240 
232- &-light {241+.cluster-info {
233- display: inline-block;242+ height: inherit;
234- width: 6px;243+ display: flex;
235- height: 6px;244+ align-items: center;
236- border-radius: 50%;245+ font-size: 12px;
237- background-color: green;246+ 
238- margin-right: 8px;247+ &-light {
248+ display: inline-block;
249+ width: 6px;
250+ height: 6px;
251+ border-radius: 50%;
252+ background-color: green;
253+ margin-right: 8px;
254+ }
255+ 
256+ &-loading {
257+ width: 50px;
258+ }
259+}
260+ 
261+.tab-wrapper {
262+ position: relative;
263+ 
264+ &-container {
265+ display: flex;
266+ align-items: center;
267+ margin-bottom: 10px;
268+ justify-content: end;
269+ overflow: hidden;
270+ font-size: 12px;
271+ }
272+ 
273+ &-filter {
274+ font-size: 12px;
275+ width: 600px;
276+ z-index: 10;
277+ padding-right: 16px;
278+ display: flex;
279+ align-items: center;
280+ padding: 0 10px;
281+ height: 40px;
282+ > div:not(:last-of-type),
283+ > span,
284+ > button {
285+ margin-right: 4px;
239 }286 }
240 287 
241- &-loading {288+ :deep(.el-button .el-icon svg) {
242- width: 50px;289+ color: var(--el-color-icon-refresh-color);
290+ }
291+ 
292+ :deep(.el-button--small) {
293+ background-color: var(--el-color-button-small-bg) !important;
294+ border: 1px solid var(--el-bg-color-og-hover) !important;
295+ color: var(--el-bg-color-og-hover) !important;
296+ padding: 5px !important;
297+ }
298+ 
299+ :deep(.el-select .el-input.is-focus .el-input__wrapper) {
300+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
301+ }
302+ :deep(.el-select-dropdown__item.selected) {
303+ color: var(--el-color-tabbar-active) !important;
304+ }
305+ :deep(.el-range-editor.is-active) {
306+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
243 }307 }
244 }308 }
245 309 
246- .tab-wrapper {310+ :deep(.el-range-input) {
247- position: relative;311+ background-color: var(--el-bg-color);
248- 
249- &-container {
250- display: flex;
251- align-items: center;
252- margin-bottom: 10px;
253- justify-content: end;
254- overflow: hidden;
255- font-size: 12px;
256- }
257- 
258- &-filter {
259- font-size: 12px;
260- width: 600px;
261- z-index: 10;
262- padding-right: 16px;
263- display: flex;
264- align-items: center;
265- padding: 0 10px;
266- height: 40px;
267- > div:not(:last-of-type), > span, > button {
268- margin-right: 4px;
269- }
270- 
271- :deep(.el-button .el-icon svg) {
272- color: var(--el-color-icon-refresh-color);
273- }
274- 
275- :deep(.el-button--small) {
276- background-color: var(--el-color-button-small-bg) !important;
277- border: 1px solid var(--el-bg-color-og-hover) !important;
278- color: var(--el-bg-color-og-hover) !important;
279- padding: 5px !important;
280- }
281- 
282- :deep(.el-select .el-input.is-focus .el-input__wrapper) {
283- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
284- }
285- :deep(.el-select-dropdown__item.selected) {
286- color: var(--el-color-tabbar-active) !important;
287- }
288- :deep(.el-range-editor.is-active) {
289- box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
290- }
291- }
292- 
293- :deep(.el-range-input) {
294- background-color: var(--el-bg-color);
295- }
296- 
297- :deep(.el-date-editor--datetimerange) {
298- width: 100px;
299- background-color: var(--el-bg-color);
300- }
301 }312 }
302- .divider {313+ 
303- height: 24px;314+ :deep(.el-date-editor--datetimerange) {
304- width: 1px;315+ width: 100px;
305- margin: 0 8px!important;316+ background-color: var(--el-bg-color);
306- background-color: var(--el-color-divider-border-color);
307- }
308- :deep(.el-tabs__header) {
309- padding: 0 16px;
310- // width: v-bind(tabHeaderW);
311 }317 }
318+}
319+.divider {
320+ height: 24px;
321+ width: 1px;
322+ margin: 0 8px !important;
323+ background-color: var(--el-color-divider-border-color);
324+}
325+:deep(.el-tabs__header) {
326+ padding: 0 16px;
327+ // width: v-bind(tabHeaderW);
328+}
312</style>329</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/install/Index.vue+146-0
@@ -0,0 +1,146 @@
1+<template>
2+ <div class="top-sql">
3+ <div class="tab-wrapper-container">
4+ <div class="search-form-multirow">
5+ <div class="row" style="justify-content: flex-start">
6+ <div class="filter">
7+ <el-button type="primary" @click="showInstallCollector">{{ $t("install.installAgent") }}</el-button>
8+ <el-button type="primary" @click="showInstallProxy">{{ $t("install.installProxy") }}</el-button>
9+ </div>
10+ </div>
11+ </div>
12+ </div>
13+ 
14+ <div class="page-container">
15+ <div class="table-wrapper">
16+ <el-tabs v-model="activeName" @tab-click="handleClick">
17+ <el-tab-pane :label="t('install.installedAgent')" name="collector" v-loading="loadingCollector">
18+ <el-tree :data="collectorList" :props="collectorProps" />
19+ </el-tab-pane>
20+ <el-tab-pane :label="t('install.installedProxy')" name="proxy" v-loading="loadingProxies">
21+ <el-tree :data="proxyList" :props="defaultProps" />
22+ </el-tab-pane>
23+ </el-tabs>
24+ </div>
25+ </div>
26+ <InstallAgent v-if="installCollectorShown" :show="installCollectorShown" @changeModal="changeModalInstallCollector" />
27+ <InstallProxy v-if="installProxyShown" :show="installProxyShown" @changeModal="changeModalInstallProxy" @installed="proxyInstalled()" />
28+ 
29+ <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
30+ </div>
31+</template>
32+ 
33+<script setup lang="ts">
34+import { useRequest } from "vue-request";
35+import ogRequest from "../../../request";
36+import restRequest from "../../../request/restful";
37+import { useI18n } from "vue-i18n";
38+import InstallAgent from "./installAgent.vue";
39+import InstallProxy from "./installProxy.vue";
40+ 
41+const { t } = useI18n();
42+ 
43+const activeName = ref("collector");
44+const errorInfo = ref<string | Error>();
45+ 
46+onMounted(() => {
47+ refreshCollectors();
48+});
49+ 
50+// install collector
51+const installCollectorShown = ref(false);
52+const showInstallCollector = () => {
53+ installCollectorShown.value = true;
54+};
55+const changeModalInstallCollector = (val: boolean) => {
56+ installCollectorShown.value = val;
57+};
58+ 
59+// install proxy
60+const installProxyShown = ref(false);
61+const showInstallProxy = () => {
62+ installProxyShown.value = true;
63+};
64+const changeModalInstallProxy = (val: boolean) => {
65+ installProxyShown.value = val;
66+};
67+const proxyInstalled = (code: number) => {
68+ activeName.value = "proxy";
69+ refreshProxies();
70+};
71+ 
72+// Collector list
73+const collectorList = ref<Array<any>>([]);
74+const collectorProps = {
75+ children: "clusterNodes",
76+ label: "label",
77+};
78+const {
79+ data: resCollectors,
80+ run: refreshCollectors,
81+ loading: loadingCollector,
82+} = useRequest(
83+ () => {
84+ return ogRequest
85+ .get("/observability/v1/topsql/cluster", {})
86+ .then(function (res) {
87+ return res;
88+ })
89+ .catch(function (res) {});
90+ },
91+ { manual: true }
92+);
93+watch(resCollectors, (res: any) => {
94+ if (res.length) {
95+ collectorList.value = res;
96+ for (let index = 0; index < collectorList.value.length; index++) {
97+ const element = collectorList.value[index];
98+ element.label = element.clusterId;
99+ for (let index2 = 0; index2 < element.clusterNodes.length; index2++) {
100+ const node = element.clusterNodes[index2];
101+ node.label = node.privateIp + "(" + node.publicIp + ")";
102+ }
103+ }
104+ } else collectorList.value = [];
105+});
106+ 
107+// Proxy list
108+const defaultProps = {
109+ children: "children",
110+ label: "label",
111+};
112+const proxyList = ref<Array<any>>([]);
113+const {
114+ data: res,
115+ run: refreshProxies,
116+ loading: loadingProxies,
117+} = useRequest(
118+ () => {
119+ return restRequest
120+ .get("/observability/v1/environment/prometheus", {})
121+ .then(function (res) {
122+ return res;
123+ })
124+ .catch(function (res) {});
125+ },
126+ { manual: true }
127+);
128+watch(res, (res: any) => {
129+ if (res.length) {
130+ proxyList.value = res;
131+ for (let index = 0; index < proxyList.value.length; index++) {
132+ const element = proxyList.value[index];
133+ element.label = element.hostid;
134+ }
135+ } else proxyList.value = [];
136+});
137+ 
138+const handleClick = (tab: any, event: Event) => {
139+ if (tab.paneName === "collector") refreshCollectors();
140+ else if (tab.paneName === "proxy") refreshProxies();
141+};
142+</script>
143+ 
144+<style scoped lang="scss">
145+@import "../../../assets/style/style1.scss";
146+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/install/installAgent.vue+137-0
@@ -0,0 +1,137 @@
1+<template>
2+ <div class="dialog">
3+ <el-dialog width="400px" :title="t('install.installAgent')" v-model="visible" :close-on-click-modal="false" draggable @close="closeDialog">
4+ <div class="dialog-content" v-show="installData.length != 0">
5+ <div>
6+ <el-steps direction="vertical" :active="doingIndex">
7+ <el-step v-for="item in installData" :key="item.name" :title="item.name" />
8+ </el-steps>
9+ </div>
10+ </div>
11+ <div class="dialog-content" v-loading="started" v-show="installData.length === 0">
12+ <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
13+ <el-form-item :label="t('install.collectInstance')" prop="nodeId">
14+ <ClusterCascader width="200" instanceValueKey="nodeId" @getCluster="handleClusterValue" autoSelectFirst notClearable />
15+ </el-form-item>
16+ <el-form-item :label="t('install.rootPWD')" prop="rootPassword">
17+ <el-input v-model="formData.rootPassword" show-password style="width: 200px; margin: 0 4px" />
18+ </el-form-item>
19+ </el-form>
20+ </div>
21+ 
22+ <template #footer>
23+ <el-button v-if="installData.length === 0" :loading="started" style="padding: 5px 20px" type="primary" @click="install">{{ $t("install.install") }}</el-button>
24+ <el-button v-if="installData.length != 0" style="padding: 5px 20px" @click="back">{{ $t("app.back") }}</el-button>
25+ <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t("app.cancel") }}</el-button>
26+ </template>
27+ </el-dialog>
28+ </div>
29+</template>
30+ 
31+<script lang="ts" setup>
32+import { cloneDeep } from "lodash-es";
33+import { FormRules } from "element-plus";
34+import { useI18n } from "vue-i18n";
35+import WebSocketClass from "../../../utils/websocket";
36+import { encryptPassword } from "../../../utils/jsencrypt";
37+import moment from "moment";
38+const { t } = useI18n();
39+ 
40+const visible = ref(false);
41+const props = withDefaults(
42+ defineProps<{
43+ show: boolean;
44+ }>(),
45+ {}
46+);
47+watch(
48+ () => props.show,
49+ (newValue) => {
50+ visible.value = newValue;
51+ },
52+ { immediate: true }
53+);
54+ 
55+// form data
56+const initFormData = {
57+ nodeId: "",
58+ rootPassword: "",
59+ port: "9090",
60+};
61+const formData = reactive(cloneDeep(initFormData));
62+const connectionFormRules = reactive<FormRules>({
63+ nodeId: [{ required: true, message: t("install.collectorRules[0]"), trigger: "blur" }],
64+ rootPassword: [{ required: true, message: t("install.collectorRules[1]"), trigger: "blur" }],
65+});
66+// cluster component
67+const handleClusterValue = (val: any) => {
68+ formData.nodeId = val.length > 1 ? val[1] : "";
69+};
70+ 
71+const started = ref(false);
72+const installSucceed = ref(false);
73+const ws = reactive({
74+ name: "",
75+ webUser: "",
76+ connectionName: "",
77+ sessionId: "",
78+ instance: null,
79+});
80+const install = async () => {
81+ started.value = true;
82+ ws.name = moment(new Date()).format("YYYYMMDDHHmmss") as string; // websocket connection name
83+ ws.sessionId = moment(new Date()).format("YYYYMMDDHHmmss") as string; // websocket connection id
84+ ws.instance = new WebSocketClass(ws.name, ws.sessionId, onWebSocketMessage);
85+ sendData();
86+};
87+const sendData = async () => {
88+ const encryptPwd = await encryptPassword(formData.rootPassword);
89+ const sendData = {
90+ key: "exporter",
91+ nodeId: formData.nodeId,
92+ rootPassword: encryptPwd,
93+ };
94+ ws.instance.send(sendData);
95+};
96+const onWebSocketMessage = (data: Array<any>) => {
97+ if (Array.isArray(installData.value)) installData.value = JSON.parse(data);
98+};
99+ 
100+// action
101+const back = () => {
102+ started.value = false;
103+ ws.instance.close();
104+ installData.value = [];
105+};
106+ 
107+// list Data
108+const installData = ref<Array<any>>([]);
109+const doingIndex = computed(() => {
110+ for (let index = 0; index < installData.value.length; index++) {
111+ const element = installData.value[index];
112+ if (element.state === "DOING" || element.state === "ERROR") return index;
113+ }
114+ if (!installSucceed.value) installSucceed.value = true;
115+ return installData.value.length;
116+});
117+ 
118+// dialog
119+const emit = defineEmits(["changeModal", "installed"]);
120+const handleCancelModel = () => {
121+ visible.value = false;
122+ if (installSucceed.value) emit("installed");
123+ emit("changeModal", visible.value);
124+};
125+const closeDialog = () => {
126+ visible.value = false;
127+ if (installSucceed.value) emit("installed");
128+ emit("changeModal", visible.value);
129+};
130+ 
131+onBeforeUnmount(() => {
132+ if (ws.instance) ws.instance.close();
133+});
134+</script>
135+<style lang="scss" scoped>
136+@import "../../../assets/style/style1.scss";
137+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/install/installProxy.vue+140-0
@@ -0,0 +1,140 @@
1+<template>
2+ <div class="dialog">
3+ <el-dialog width="400px" :title="t('install.installProxy')" v-model="visible" :close-on-click-modal="false" draggable @close="closeDialog">
4+ <div class="dialog-content" v-show="installData.length != 0">
5+ <div>
6+ <el-steps direction="vertical" :active="doingIndex">
7+ <el-step v-for="item in installData" :key="item.name" :title="item.name" />
8+ </el-steps>
9+ </div>
10+ </div>
11+ <div class="dialog-content" v-loading="started" v-show="installData.length === 0">
12+ <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
13+ <el-form-item :label="t('install.machine')" prop="nodeId">
14+ <Machines width="200" @change="changeMachine" autoSelectFirst notClearable style="width: 200px; margin: 0 4px" />
15+ </el-form-item>
16+ <el-form-item :label="t('install.rootPWD')" prop="rootPassword">
17+ <el-input v-model="formData.rootPassword" show-password style="width: 200px; margin: 0 4px" />
18+ </el-form-item>
19+ <el-form-item :label="t('install.proxyPort')" prop="port">
20+ <el-input v-model="formData.port" style="width: 200px; margin: 0 4px" />
21+ </el-form-item>
22+ </el-form>
23+ </div>
24+ 
25+ <template #footer>
26+ <el-button v-if="installData.length === 0" :loading="started" style="padding: 5px 20px" type="primary" @click="install">{{ $t("install.install") }}</el-button>
27+ <el-button v-if="installData.length != 0" style="padding: 5px 20px" @click="back">{{ $t("app.back") }}</el-button>
28+ <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t("app.cancel") }}</el-button>
29+ </template>
30+ </el-dialog>
31+ </div>
32+</template>
33+ 
34+<script lang="ts" setup>
35+import { cloneDeep } from "lodash-es";
36+import { FormRules } from "element-plus";
37+import { useI18n } from "vue-i18n";
38+import WebSocketClass from "../../../utils/websocket";
39+import { encryptPassword } from "../../../utils/jsencrypt";
40+import moment from "moment";
41+const { t } = useI18n();
42+ 
43+const visible = ref(false);
44+const props = withDefaults(
45+ defineProps<{
46+ show: boolean;
47+ }>(),
48+ {}
49+);
50+watch(
51+ () => props.show,
52+ (newValue) => {
53+ visible.value = newValue;
54+ },
55+ { immediate: true }
56+);
57+ 
58+// form data
59+const initFormData = {
60+ nodeId: "",
61+ rootPassword: "",
62+ port: "9090",
63+};
64+const formData = reactive(cloneDeep(initFormData));
65+const changeMachine = (val: any) => {
66+ formData.nodeId = val;
67+};
68+const connectionFormRules = reactive<FormRules>({
69+ nodeId: [{ required: true, message: t("install.proxyRules[0]"), trigger: "blur" }],
70+ rootPassword: [{ required: true, message: t("install.proxyRules[1]"), trigger: "blur" }],
71+ port: [{ required: true, message: t("install.proxyRules[2]"), trigger: "blur" }],
72+});
73+ 
74+const started = ref(false);
75+const installSucceed = ref(false);
76+const ws = reactive({
77+ name: "",
78+ webUser: "",
79+ connectionName: "",
80+ sessionId: "",
81+ instance: null,
82+});
83+const install = async () => {
84+ started.value = true;
85+ ws.name = moment(new Date()).format("YYYYMMDDHHmmss") as string; // websocket connection name
86+ ws.sessionId = moment(new Date()).format("YYYYMMDDHHmmss") as string; // websocket connection id
87+ ws.instance = new WebSocketClass(ws.name, ws.sessionId, onWebSocketMessage);
88+ sendData();
89+};
90+const sendData = async () => {
91+ const encryptPwd = await encryptPassword(formData.rootPassword);
92+ const sendData = {
93+ key: "prometheus",
94+ hostId: formData.nodeId,
95+ rootPassword: encryptPwd,
96+ };
97+ ws.instance.send(sendData);
98+};
99+const onWebSocketMessage = (data: Array<any>) => {
100+ if (Array.isArray(installData.value)) installData.value = JSON.parse(data);
101+};
102+ 
103+// action
104+const back = () => {
105+ started.value = false;
106+ ws.instance.close();
107+ installData.value = [];
108+};
109+ 
110+// list Data
111+const installData = ref<Array<any>>([]);
112+const doingIndex = computed(() => {
113+ for (let index = 0; index < installData.value.length; index++) {
114+ const element = installData.value[index];
115+ if (element.state === "DOING" || element.state === "ERROR") return index;
116+ }
117+ if (!installSucceed.value) installSucceed.value = true;
118+ return installData.value.length;
119+});
120+ 
121+// dialog
122+const emit = defineEmits(["changeModal", "installed"]);
123+const handleCancelModel = () => {
124+ visible.value = false;
125+ if (installSucceed.value) emit("installed");
126+ emit("changeModal", visible.value);
127+};
128+const closeDialog = () => {
129+ visible.value = false;
130+ if (installSucceed.value) emit("installed");
131+ emit("changeModal", visible.value);
132+};
133+ 
134+onBeforeUnmount(() => {
135+ if (ws.instance) ws.instance.close();
136+});
137+</script>
138+<style lang="scss" scoped>
139+@import "../../../assets/style/style1.scss";
140+</style>
Mplugins/observability-instance/web-ui/src/pages/dashboard/performance_load/Index.vue+2-0
@@ -74,6 +74,7 @@ const loadRateData = (data: any[]) => {
74 }74 }
75 ioRateData.value = data.map(d => {75 ioRateData.value = data.map(d => {
76 const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)76 const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)
77+ console.log("count", count);
77 return {78 return {
78 name: `${t(`metric.${d.name}`)}${t('dashboard.rate')}`,79 name: `${t(`metric.${d.name}`)}${t('dashboard.rate')}`,
79 cur: `${d.data[Math.max(0, d.data.length - 1)]}KB/s`,80 cur: `${d.data[Math.max(0, d.data.length - 1)]}KB/s`,
@@ -91,6 +92,7 @@ const loadData = (data: any[]) => {
91 }92 }
92 ioData.value = data.map(d => {93 ioData.value = data.map(d => {
93 const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)94 const count = d.data.reduce((a: number, s: string) => s != null ? a + Number.parseFloat(s) : a, 0)
95+ console.log("count", count);
94 return {96 return {
95 name: `${t(`metric.${d.name}`)}${t('dashboard.capacity')}`,97 name: `${t(`metric.${d.name}`)}${t('dashboard.capacity')}`,
96 cur: `${d.data[Math.max(0, d.data.length - 1)]}MB/s`,98 cur: `${d.data[Math.max(0, d.data.length - 1)]}MB/s`,
Aplugins/observability-instance/web-ui/src/pages/dashboard/system_configuration/Index.vue+263-0
@@ -0,0 +1,263 @@
1+<script setup lang="ts">
2+import { useRequest } from "vue-request";
3+import restRequest from "../../../request/restful";
4+import { useI18n } from "vue-i18n";
5+import { View, Guide } from "@element-plus/icons-vue";
6+import Password from "./password.vue";
7+ 
8+const { t } = useI18n();
9+ 
10+const errorInfo = ref<string | Error>();
11+ 
12+const props = withDefaults(
13+ defineProps<{
14+ instanceId?: string;
15+ }>(),
16+ {
17+ instanceId: "",
18+ }
19+);
20+ 
21+onMounted(() => {});
22+const nodeId = ref<string>("");
23+const data = reactive<{
24+ dbParamData: Array<Record<string, string>>;
25+ osParamData: Array<Record<string, string>>;
26+}>({
27+ dbParamData: [],
28+ osParamData: [],
29+});
30+ 
31+// password dialog
32+const snapshotManageShown = ref(false);
33+const showSnapshotManage = () => {
34+ snapshotManageShown.value = true;
35+};
36+const changeModalSnapshotManage = (val: boolean) => {
37+ snapshotManageShown.value = val;
38+};
39+ 
40+// cluster component
41+const handleClusterValue = (val: any) => {
42+ nodeId.value = val.length > 1 ? val[1] : "";
43+};
44+ 
45+const handleQuery = () => {
46+ showSnapshotManage();
47+};
48+const refreshData = (password: string) => {
49+ requestDBData(password);
50+ requestOSData(password);
51+};
52+const {
53+ data: res,
54+ run: requestDBData,
55+ loading: loadingDBData,
56+} = useRequest(
57+ (password) => {
58+ return restRequest
59+ .get("/observability/v1/param/databaseParamInfo", {
60+ nodeId: nodeId.value,
61+ })
62+ .then(function (res) {
63+ return res;
64+ })
65+ .catch(function (res) {
66+ data.dbParamData = [];
67+ });
68+ },
69+ { manual: true }
70+);
71+watch(res, (res) => {
72+ data.dbParamData = res;
73+});
74+ 
75+const {
76+ data: resOS,
77+ run: requestOSData,
78+ loading: loadingOSData,
79+} = useRequest(
80+ (password) => {
81+ return restRequest
82+ .get("/observability/v1/param/osParamInfo", {
83+ paramName: "",
84+ nodeId: nodeId.value,
85+ dbName: null,
86+ password,
87+ isRefresh: null,
88+ paramType: "",
89+ })
90+ .then(function (res) {
91+ return res;
92+ })
93+ .catch(function (res) {
94+ data.osParamData = [];
95+ });
96+ },
97+ { manual: true }
98+);
99+watch(resOS, (resOS) => {
100+ data.osParamData = resOS;
101+});
102+const color = computed(() => {
103+ console.log('localStorage.getItem("theme")',localStorage.getItem("theme"))
104+ if (localStorage.getItem("theme") === "dark") return "#fcef92";
105+ else return "#E41D1D";
106+});
107+</script>
108+ 
109+<template>
110+ <div class="" style="padding: 0px 15px">
111+ <div class="search-form head">
112+ <div class="filter title" style="margin-right: auto">{{ $t("configParam.tabTitle") }}</div>
113+ 
114+ <div class="filter">
115+ <ClusterCascader notClearable autoSelectFirst :title="$t('datasource.cluterTitle')" @getCluster="handleClusterValue" />
116+ </div>
117+ <div class="query filter">
118+ <el-button type="primary" @click="handleQuery">{{ $t("app.query") }}</el-button>
119+ </div>
120+ </div>
121+ <div class="list">
122+ <div class="list-item">
123+ <div class="item-title">{{ $t("configParam.systemConfig") }}</div>
124+ <div v-loading="loadingOSData">
125+ <div class="item-list" v-for="item in data.osParamData" :key="item.seqNo">
126+ <div class="item-list-left">
127+ <div class="item-name">{{ item.paramName }}</div>
128+ <el-popover placement="top-start" :title="$t('configParam.paramDesc')" :width="200" trigger="hover" :content="item.paramDetail">
129+ <template #reference>
130+ <el-icon class="detail-btn" :color="'#7d7d7d'" size="18px">
131+ <View />
132+ </el-icon>
133+ </template>
134+ </el-popover>
135+ </div>
136+ <div class="item-list-right">
137+ <div class="item-value">{{ item.actualValue === undefined || item.actualValue === null ? "--" : item.actualValue }}</div>
138+ <div class="suggest-btn-container">
139+ <el-popover placement="top-start" :title="$t('configParam.paramTuning')" :width="300" trigger="hover" v-if="item.suggestValue != undefined && item.suggestValue != null && item.suggestValue != '' && item.suggestValue != item.actualValue">
140+ <template #reference>
141+ <el-icon class="suggest-btn" :color="color" size="18px" v-if="item.actualValue != item.suggestValue">
142+ <Guide />
143+ </el-icon>
144+ </template>
145+ <template #default>
146+ <div class="demo-rich-conent" style="display: flex; gap: 16px; flex-direction: column">
147+ <div>
148+ <p class="demo-rich-content__name" style="margin: 0; font-weight: 500">{{ $t("configParam.suggestValue") }}{{ item.suggestValue }}</p>
149+ <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ $t("configParam.suggestReason") }}</p>
150+ <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ item.suggestExplain }}</p>
151+ </div>
152+ </div>
153+ </template>
154+ </el-popover>
155+ </div>
156+ </div>
157+ </div>
158+ </div>
159+ </div>
160+ <div class="list-item">
161+ <div class="item-title">{{ $t("configParam.databaseConfig") }}</div>
162+ <div v-loading="loadingDBData">
163+ <div class="item-list" v-for="item in data.dbParamData" :key="item.seqNo">
164+ <div class="item-list-left">
165+ <div class="item-name">{{ item.paramName }}</div>
166+ <el-popover placement="top-start" :title="$t('configParam.paramDesc')" :width="200" trigger="hover" :content="item.paramDetail">
167+ <template #reference>
168+ <el-icon class="detail-btn" :color="'#7d7d7d'" size="18px">
169+ <View />
170+ </el-icon>
171+ </template>
172+ </el-popover>
173+ </div>
174+ <div class="item-list-right">
175+ <div class="item-value">{{ item.actualValue === undefined || item.actualValue === null ? "--" : item.actualValue }}</div>
176+ <div class="suggest-btn-container">
177+ <el-popover placement="top-start" :title="$t('configParam.paramTuning')" :width="300" trigger="hover" v-if="item.suggestValue != undefined && item.suggestValue != null && item.suggestValue != '' && item.suggestValue != item.actualValue">
178+ <template #reference>
179+ <el-icon class="suggest-btn" :color="color" size="18px" v-if="item.actualValue != item.suggestValue">
180+ <Guide />
181+ </el-icon>
182+ </template>
183+ <template #default>
184+ <div class="demo-rich-conent" style="display: flex; gap: 16px; flex-direction: column">
185+ <div>
186+ <p class="demo-rich-content__name" style="margin: 0; font-weight: 500">{{ $t("configParam.suggestValue") }}{{ item.suggestValue }}</p>
187+ <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ $t("configParam.suggestReason") }}</p>
188+ <p class="demo-rich-content__mention" style="margin: 0; font-size: 14px" v-if="item.suggestExplain">{{ item.suggestExplain }}</p>
189+ </div>
190+ </div>
191+ </template>
192+ </el-popover>
193+ </div>
194+ </div>
195+ </div>
196+ </div>
197+ </div>
198+ </div>
199+ <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
200+ <Password :show="snapshotManageShown" @changeModal="changeModalSnapshotManage" @confirm="refreshData" />
201+ </div>
202+</template>
203+ 
204+<style scoped lang="scss">
205+@import "../../../assets/style/style1.scss";
206+.head {
207+ display: flex;
208+ align-items: center;
209+ margin-bottom: 15px;
210+ .title {
211+ font-size: 16px;
212+ font-weight: bold;
213+ }
214+}
215+.list {
216+ display: flex;
217+ flex-direction: row;
218+ .list-item {
219+ width: 50%;
220+ .item-title {
221+ font-size: 14px;
222+ font-weight: bold;
223+ margin-bottom: 5px;
224+ }
225+ .item-list {
226+ display: flex;
227+ margin: 5px 0px;
228+ .item-list-left {
229+ width: 55%;
230+ display: flex;
231+ align-items: center;
232+ flex-shrink: 0;
233+ .detail-btn {
234+ margin-left: 10px;
235+ }
236+ }
237+ .item-list-right {
238+ width: 40%;
239+ display: flex;
240+ align-items: center;
241+ overflow: hidden;
242+ padding-right: 20px;
243+ padding-left: 20px;
244+ vertical-align: middle;
245+ .item-value {
246+ display: inline-block;
247+ white-space: nowrap;
248+ overflow: hidden;
249+ text-overflow: ellipsis;
250+ text-align: left;
251+ }
252+ .suggest-btn-container {
253+ width: 20px;
254+ display: flex;
255+ }
256+ .suggest-btn {
257+ margin-left: 5px;
258+ }
259+ }
260+ }
261+ }
262+}
263+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/system_configuration/password.vue+64-0
@@ -0,0 +1,64 @@
1+<template>
2+ <div class="dialog">
3+ <el-dialog width="400px" :title="$t('configParam.rootPWDTitle')" v-model="visible" :close-on-click-modal="false" draggable @close="taskClose">
4+ <div class="dialog-content" v-loading="generating">
5+ <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
6+ <el-form-item :label="t('configParam.rootPWD')" prop="rootPassword">
7+ <el-input v-model="formData.rootPassword" show-password style="width: 200px; margin: 0 4px" />
8+ </el-form-item>
9+ </el-form>
10+ </div>
11+ 
12+ <template #footer>
13+ <el-button style="padding: 5px 20px" type="primary" @click="handleconfirmModel">{{ $t("app.confirm") }}</el-button>
14+ <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t("app.cancel") }}</el-button>
15+ </template>
16+ </el-dialog>
17+ </div>
18+</template>
19+ 
20+<script lang="ts" setup>
21+import { cloneDeep } from "lodash-es";
22+import { useRequest } from "vue-request";
23+import { FormRules, FormInstance, ElMessage } from "element-plus";
24+import { useI18n } from "vue-i18n";
25+import restRequest from "../../../request/restful";
26+const { t } = useI18n();
27+ 
28+const visible = ref(false);
29+const props = withDefaults(
30+ defineProps<{
31+ show: boolean;
32+ }>(),
33+ {}
34+);
35+watch(
36+ () => props.show,
37+ (newValue) => {
38+ visible.value = newValue;
39+ },
40+ { immediate: true }
41+);
42+ 
43+// form data
44+const initFormData = {
45+ rootPassword: "",
46+};
47+const formData = reactive(cloneDeep(initFormData));
48+ 
49+// build
50+const emit = defineEmits(["changeModal", "confirm"]);
51+async function handleconfirmModel() {
52+ emit("confirm", formData.rootPassword);
53+ visible.value = false;
54+ emit("changeModal", visible.value);
55+}
56+const connectionFormRef = ref<FormInstance>();
57+const connectionFormRules = reactive<FormRules>({
58+ rootPassword: [{ required: true, message: t("configParam.rootPWDTitle"), trigger: "blur" }],
59+});
60+ 
61+</script>
62+<style lang="scss" scoped>
63+@import "../../../assets/style/style1.scss";
64+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/wdr/Index.vue+262-0
@@ -0,0 +1,262 @@
1+<script setup lang="ts">
2+import { useRequest } from "vue-request";
3+import moment from "moment";
4+import restRequest from "../../../request/restful";
5+import BuildWdr from "./build_wdr.vue";
6+import SnapshotManage from "./snapshot_manage.vue";
7+import { cloneDeep } from "lodash-es";
8+ 
9+const errorInfo = ref<string | Error>();
10+ 
11+const props = withDefaults(
12+ defineProps<{
13+ instanceId?: string;
14+ }>(),
15+ {
16+ instanceId: "",
17+ }
18+);
19+ 
20+const initFormData = {
21+ reportRange: "CLUSTER",
22+ reportType: "DETAIL",
23+ dateValue: [moment(new Date()).format("YYYY-MM-DD") + " 00:00:00", moment(new Date()).format("YYYY-MM-DD") + " 23:59:59"],
24+};
25+const formData = reactive(cloneDeep(initFormData));
26+const tableData = ref<Array<any>>([]);
27+ 
28+const page = reactive({
29+ currentPage: 1,
30+ pageSize: 10,
31+ total: 10,
32+});
33+const snapshotManageShown = ref(false);
34+const buildWDRShown = ref(false);
35+const showSnapshotManage = () => {
36+ snapshotManageShown.value = true;
37+};
38+const changeModalSnapshotManage = (val: boolean) => {
39+ snapshotManageShown.value = val;
40+};
41+const showBuildWDR = () => {
42+ buildWDRShown.value = true;
43+};
44+const changeModalBuildWDR = (val: boolean) => {
45+ buildWDRShown.value = val;
46+};
47+const bandleCoveyBuildWDR = (code: number) => {
48+ requestData();
49+};
50+const handleQuery = () => {
51+ requestData();
52+};
53+const handleReset = () => {
54+ formData.reportRange = initFormData.reportRange;
55+ formData.reportType = initFormData.reportType;
56+ formData.dateValue = initFormData.dateValue;
57+ requestData();
58+};
59+ 
60+const cluster = ref<Array<any>>([]);
61+const handleClusterValue = (val: any) => {
62+ cluster.value = val;
63+};
64+const {
65+ data: res,
66+ run: requestData,
67+ loading,
68+} = useRequest(
69+ () => {
70+ const clusterId = cluster.value.length ? cluster.value[0] : "";
71+ return restRequest
72+ .get("/wdr/list", {
73+ clusterId,
74+ wdrScope: formData.reportRange,
75+ wdrType: formData.reportType,
76+ start: formData.dateValue && formData.dateValue.length > 0 ? formData.dateValue[0] : null,
77+ end: formData.dateValue && formData.dateValue.length > 1 ? formData.dateValue[1] : null,
78+ pageSize: page.pageSize,
79+ pageNum: page.currentPage,
80+ })
81+ .then(function (res) {
82+ return res;
83+ })
84+ .catch(function (res) {
85+ tableData.value = [];
86+ Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
87+ });
88+ },
89+ { manual: true }
90+);
91+type Res =
92+ | {
93+ records: string[];
94+ pageNum: number;
95+ total: number;
96+ }
97+ | undefined;
98+watch(res, (res: Res) => {
99+ if (res && res.records && res.records.length) {
100+ const { total } = res
101+ tableData.value = res.records;
102+ Object.assign(page, { pageSize: page.pageSize, total })
103+ } else {
104+ tableData.value = [];
105+ }
106+});
107+const handleSizeChange = (val: number) => {
108+ page.currentPage = 1;
109+ page.pageSize = val;
110+ changePageCurrent(page.currentPage);
111+};
112+const handleCurrentChange = (val: number) => {
113+ page.currentPage = val;
114+ changePageCurrent(page.currentPage);
115+};
116+const changePageCurrent = (data: number) => {
117+ Object.assign(page, data);
118+ requestData();
119+};
120+ 
121+// view WDR
122+type Row = {
123+ wdrId: string;
124+ reportName: string;
125+};
126+const { run: handleView, loading: viewing } = useRequest(
127+ (row: Row) => {
128+ return restRequest
129+ .get("/wdr/downloadWdr", {
130+ wdrId: row?.wdrId,
131+ })
132+ .then(function (res) {
133+ console.log('res',res)
134+ const newWindow = window.open(row.reportName, "_blank");
135+ newWindow?.document.write(res.data);
136+ })
137+ .catch(function (res) {});
138+ },
139+ { manual: true }
140+);
141+ 
142+// download WDR
143+const { run: handleDownload, loading: downloading } = useRequest(
144+ (row: Row) => {
145+ return restRequest
146+ .get("/wdr/downloadWdr", {
147+ wdrId: row?.wdrId,
148+ })
149+ .then(function (res) {
150+ if (res.data) {
151+ const blob = new Blob([res.data], {
152+ type: "text/plain",
153+ });
154+ const a = document.createElement("a");
155+ const URL = window.URL || window.webkitURL;
156+ const herf = URL.createObjectURL(blob);
157+ a.href = herf;
158+ a.download = row.reportName;
159+ document.body.appendChild(a);
160+ a.click();
161+ document.body.removeChild(a);
162+ window.URL.revokeObjectURL(herf);
163+ }
164+ })
165+ .catch(function (res) {
166+ tableData.value = [];
167+ Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
168+ });
169+ },
170+ { manual: true }
171+);
172+ 
173+// delete row
174+const { run: hanleDelete, loading: deleting } = useRequest(
175+ (row: Row) => {
176+ return restRequest
177+ .delete(`/wdr/del/${row.wdrId}`)
178+ .then(function (res) {
179+ requestData();
180+ })
181+ .catch(function (res) {});
182+ },
183+ { manual: true }
184+);
185+</script>
186+ 
187+<template>
188+ <div class="top-sql">
189+ <div class="tab-wrapper-container">
190+ <div class="search-form-multirow">
191+ <div class="row">
192+ <div class="filter">
193+ <ClusterCascader @loaded="requestData" notClearable autoSelectFirst :title="$t('dashboard.wdrReports.clusterName')" @getCluster="handleClusterValue" />
194+ </div>
195+ <div class="filter">
196+ <span>{{ $t("dashboard.wdrReports.reportRange") }}&nbsp;</span>
197+ <el-select v-model="formData.reportRange" style="width: 160px; margin: 0 4px">
198+ <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
199+ <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
200+ </el-select>
201+ </div>
202+ <div class="filter">
203+ <span>{{ $t("dashboard.wdrReports.reportType") }}&nbsp;</span>
204+ <el-select v-model="formData.reportType" style="width: 160px; margin: 0 4px">
205+ <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
206+ <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
207+ <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
208+ </el-select>
209+ </div>
210+ 
211+ <div class="filter">
212+ <span>{{ $t("dashboard.wdrReports.buildTime") }}&nbsp;</span>
213+ <MyDatePicker v-model="formData.dateValue" type="datetimerange" style="width: 300px" />
214+ </div>
215+ </div>
216+ 
217+ <div class="row">
218+ <div class="filter">
219+ <el-button @click="handleQuery">{{ $t("app.query") }}</el-button>
220+ <el-button @click="handleReset">{{ $t("app.reset") }}</el-button>
221+ <el-button type="primary" @click="showSnapshotManage">{{ $t("dashboard.wdrReports.snapshotManage") }}</el-button>
222+ <el-button type="primary" @click="showBuildWDR">{{ $t("dashboard.wdrReports.buildWDR") }}</el-button>
223+ </div>
224+ </div>
225+ </div>
226+ </div>
227+ 
228+ <div class="page-container">
229+ <div class="table-wrapper" v-loading="loading || viewing || downloading || deleting">
230+ <el-table class="normal-table" :data="tableData" :header-cell-style="{ 'text-align': 'center' }" style="width: 100%" :default-sort="{ prop: 'date', order: 'descending' }">
231+ <el-table-column prop="scope" :label="$t('dashboard.wdrReports.reportRange')" width="80" align="center" />
232+ <el-table-column prop="reportAt" :label="$t('dashboard.wdrReports.list.buildTime')" width="180" align="center" />
233+ <el-table-column prop="reportType" :label="$t('dashboard.wdrReports.reportType')" width="80" align="center" />
234+ <el-table-column prop="reportName" :label="$t('dashboard.wdrReports.list.reportName')" width="420" align="center" />
235+ <el-table-column :label="$t('app.operate')" align="center" fixed="right" width="130">
236+ <template #default="scope">
237+ <div class="operate-btns">
238+ <el-link size="small" type="primary" @click="handleView(scope.row)">{{ $t("app.view") }}</el-link>
239+ <el-link size="small" type="primary" @click="handleDownload(scope.row)">{{ $t("app.download") }}</el-link>
240+ <el-popconfirm title="Are you sure to delete this?" @confirm="hanleDelete(scope.row)">
241+ <template #reference>
242+ <el-link size="small" type="primary">{{ $t("app.delete") }}</el-link>
243+ </template>
244+ </el-popconfirm>
245+ </div>
246+ </template>
247+ </el-table-column>
248+ </el-table>
249+ </div>
250+ </div>
251+ <el-pagination :currentPage="page.currentPage" :pageSize="page.pageSize" :total="page.total" :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small @size-change="handleSizeChange" @current-change="handleCurrentChange" />
252+ 
253+ <SnapshotManage :show="snapshotManageShown" @changeModal="changeModalSnapshotManage" />
254+ <BuildWdr :show="buildWDRShown" @changeModal="changeModalBuildWDR" @conveyFlag="bandleCoveyBuildWDR" />
255+ 
256+ <my-message v-if="errorInfo" type="error" :tip="errorInfo" defaultTip="" />
257+ </div>
258+</template>
259+ 
260+<style scoped lang="scss">
261+@import "../../../assets/style/style1.scss";
262+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/wdr/build_wdr.vue+167-0
@@ -0,0 +1,167 @@
1+<template>
2+ <div class="dialog">
3+ <el-dialog width="400px" :title="$t('dashboard.wdrReports.buildWDR')" v-model="visible" :close-on-click-modal="false" draggable @close="taskClose">
4+ <div class="dialog-content" v-loading="generating">
5+ <el-form :model="formData" :rules="connectionFormRules" ref="connectionFormRef">
6+ <el-form-item :label="$t('datasource.cluterTitle')" prop="hostId">
7+ <ClusterCascader width="200" instanceValueKey="hostId" @loaded="requestData" @getCluster="handleClusterValue" autoSelectFirst notClearable />
8+ </el-form-item>
9+ <el-form-item :label="$t('dashboard.wdrReports.reportRange')" prop="reportRange">
10+ <el-select v-model="formData.scope" style="width: 200px; margin: 0 4px">
11+ <el-option value="CLUSTER" :label="$t('dashboard.wdrReports.reportRangeSelect[0]')" />
12+ <el-option value="NODE" :label="$t('dashboard.wdrReports.reportRangeSelect[1]')" />
13+ </el-select>
14+ </el-form-item>
15+ <el-form-item :label="$t('dashboard.wdrReports.reportType')" prop="reportType">
16+ <el-select v-model="formData.type" style="width: 200px; margin: 0 4px">
17+ <el-option value="DETAIL" :label="$t('dashboard.wdrReports.reportTypeSelect[0]')" />
18+ <el-option value="SUMMARY" :label="$t('dashboard.wdrReports.reportTypeSelect[1]')" />
19+ <el-option value="ALL" :label="$t('dashboard.wdrReports.reportTypeSelect[2]')" />
20+ </el-select>
21+ </el-form-item>
22+ <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.startSnapshot')" prop="startId">
23+ <el-select v-model="formData.startId" style="width: 200px; margin: 0 4px">
24+ <el-option v-for="item in tableData" :key="item.snapshotId" :label="item.snapshotId" :value="item.snapshotId" />
25+ </el-select>
26+ </el-form-item>
27+ <el-form-item :label="$t('dashboard.wdrReports.buildWDRDialog.endSnapshot')" prop="endId">
28+ <el-select v-model="formData.endId" style="width: 200px; margin: 0 4px">
29+ <el-option v-for="item in tableData" :key="item.snapshotId" :label="item.snapshotId" :value="item.snapshotId" />
30+ </el-select>
31+ </el-form-item>
32+ </el-form>
33+ </div>
34+ 
35+ <template #footer>
36+ <el-button style="padding: 5px 20px" :loading="generating" type="primary" @click="handleconfirmModel">{{ $t("dashboard.wdrReports.buildWDRDialog.build") }}</el-button>
37+ <el-button style="padding: 5px 20px" @click="handleCancelModel">{{ $t("app.cancel") }}</el-button>
38+ </template>
39+ </el-dialog>
40+ </div>
41+</template>
42+ 
43+<script lang="ts" setup>
44+import { cloneDeep } from "lodash-es";
45+import { useRequest } from "vue-request";
46+import { FormRules, FormInstance, ElMessage } from "element-plus";
47+import { useI18n } from "vue-i18n";
48+import restRequest from "../../../request/restful";
49+const { t } = useI18n();
50+ 
51+const visible = ref(false);
52+const props = withDefaults(
53+ defineProps<{
54+ show: boolean;
55+ }>(),
56+ {}
57+);
58+watch(
59+ () => props.show,
60+ (newValue) => {
61+ visible.value = newValue;
62+ },
63+ { immediate: true }
64+);
65+ 
66+// form data
67+const initFormData = {
68+ clusterId: "",
69+ endId: "",
70+ hostId: "",
71+ scope: "CLUSTER",
72+ startId: "",
73+ type: "DETAIL",
74+};
75+const formData = reactive(cloneDeep(initFormData));
76+ 
77+// cluster component
78+const handleClusterValue = (val: any) => {
79+ formData.clusterId = val.length ? val[0] : "";
80+ formData.hostId = val.length > 1 ? val[1] : "";
81+};
82+ 
83+// snapshotList
84+const tableData = ref<Array<any>>([]);
85+const { data: res, run: requestData } = useRequest(
86+ () => {
87+ return restRequest
88+ .get("/wdr/listSnapshot", {
89+ clusterId: formData.clusterId,
90+ hostId: formData.hostId,
91+ orderby: "snapshot_id desc",
92+ pageSize: 20,
93+ pageNum: 1,
94+ })
95+ .then(function (res) {
96+ return res;
97+ })
98+ .catch(function (res) {
99+ tableData.value = [];
100+ });
101+ },
102+ { manual: true }
103+);
104+watch(res, (res) => {
105+ if (res && res.records && res.records.length) {
106+ tableData.value = res.records;
107+ if (tableData.value.length > 0) {
108+ formData.startId = tableData.value[0].snapshotId;
109+ formData.endId = tableData.value[tableData.value.length - 1].snapshotId;
110+ }
111+ } else {
112+ tableData.value = [];
113+ }
114+});
115+ 
116+// build
117+const connectionFormRef = ref<FormInstance>();
118+async function handleconfirmModel() {
119+ try {
120+ let result = await connectionFormRef.value?.validate();
121+ if (result) {
122+ buildWDR();
123+ }
124+ } catch (error) {}
125+}
126+const connectionFormRules = reactive<FormRules>({
127+ hostId: [{ required: true, message: t("datasource.trackFormRules[0]"), trigger: "blur" }],
128+ startId: [{ required: true, message: t("datasource.trackFormRules[3]"), trigger: "blur" }],
129+ endId: [{ required: true, message: t("datasource.trackFormRules[3]"), trigger: "blur" }],
130+});
131+const {
132+ data: rez,
133+ run: buildWDR,
134+ loading: generating,
135+} = useRequest(
136+ () => {
137+ return restRequest.post("/wdr/generate", formData).then(function (res) {
138+ return res;
139+ });
140+ },
141+ { manual: true }
142+);
143+watch(rez, (rez) => {
144+ const msg = t("dashboard.wdrReports.buildWDRDialog.buildSuccess");
145+ ElMessage({
146+ showClose: true,
147+ message: msg,
148+ type: "success",
149+ });
150+ emit("conveyFlag");
151+ visible.value = false;
152+ emit("changeModal", visible.value);
153+});
154+ 
155+const emit = defineEmits(["changeModal", "conveyFlag"]);
156+const taskClose = () => {
157+ visible.value = false;
158+ emit("changeModal", visible.value);
159+};
160+const handleCancelModel = () => {
161+ visible.value = false;
162+ emit("changeModal", visible.value);
163+};
164+</script>
165+<style lang="scss" scoped>
166+@import "../../../assets/style/style1.scss";
167+</style>
Aplugins/observability-instance/web-ui/src/pages/dashboard/wdr/snapshot_manage.vue+140-0
@@ -0,0 +1,140 @@
1+<template>
2+ <div class="task-dialog">
3+ <el-dialog width="800px" :title="$t('dashboard.wdrReports.snapshotManageDialog.dialogName')" v-model="visible" :close-on-click-modal="false" draggable @close="taskClose">
4+ <div class="dialog-content" v-loading="creatingSnapshot || reading">
5+ <div class="search-form">
6+ <div class="filter" style="margin-right: auto">
7+ <el-button type="primary" @click="handelBuild">{{ $t("dashboard.wdrReports.snapshotManageDialog.createSnapshot") }}</el-button>
8+ </div>
9+ <div class="filter">
10+ <div class="cluster-container-title">{{ $t("datasource.cluterTitle") }}&nbsp;</div>
11+ <ClusterCascader instanceValueKey="hostId" @loaded="requestData" @getCluster="handleClusterValue" autoSelectFirst :options="clusterList" notClearable />
12+ </div>
13+ <div class="filter">
14+ <el-button type="primary" @click="handleQuery">{{ $t("app.query") }}</el-button>
15+ </div>
16+ </div>
17+ 
18+ <div class="table-wrapper">
19+ <el-table :data="tableData" :header-cell-style="{ 'text-align': 'left' }" style="width: 100%" :default-sort="{ prop: 'date', order: 'descending' }">
20+ <el-table-column prop="snapshotId" width="300" :label="$t('dashboard.wdrReports.snapshotManageDialog.snapshotID')" align="left" />
21+ <el-table-column prop="endTs" :label="$t('dashboard.wdrReports.snapshotManageDialog.captureTime')" align="left" />
22+ </el-table>
23+ <el-pagination :currentPage="page.currentPage" :pageSize="page.pageSize" :total="page.total" :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small @size-change="handleSizeChange" @current-change="handleCurrentChange" />
24+ </div>
25+ </div>
26+ </el-dialog>
27+ </div>
28+</template>
29+ 
30+<script lang="ts" setup>
31+import { useRequest } from "vue-request";
32+import restRequest from "../../../request/restful";
33+ 
34+const visible = ref(false);
35+const props = withDefaults(
36+ defineProps<{
37+ show: boolean;
38+ }>(),
39+ {}
40+);
41+const page = reactive({
42+ pageSize: 10,
43+ currentPage: 1,
44+ total: 30,
45+});
46+const handleSizeChange = (val: number) => {
47+ page.currentPage = 1;
48+ page.pageSize = val;
49+ changePageCurrent(page.currentPage);
50+};
51+const handleCurrentChange = (val: number) => {
52+ page.currentPage = val;
53+ changePageCurrent(page.currentPage);
54+};
55+const changePageCurrent = (data: number) => {
56+ Object.assign(page, data);
57+ requestData();
58+};
59+watch(
60+ () => props.show,
61+ (newValue) => {
62+ visible.value = newValue;
63+ },
64+ { immediate: true }
65+);
66+ 
67+const cluster = ref<Array<any>>([]);
68+const handleClusterValue = (val: any) => {
69+ cluster.value = val;
70+};
71+ 
72+const handleQuery = () => {
73+ requestData();
74+};
75+ 
76+const handelBuild = () => {
77+ createSnapshot();
78+};
79+const { run: createSnapshot, loading: creatingSnapshot } = useRequest(
80+ () => {
81+ return restRequest
82+ .get("/wdr/createSnapshot", {
83+ clusterId: cluster.value.length ? cluster.value[0] : "",
84+ hostId: cluster.value.length > 1 ? cluster.value[1] : "",
85+ })
86+ .then(function (res) {
87+ return res;
88+ })
89+ .catch(function (res) {});
90+ },
91+ { manual: true }
92+);
93+ 
94+// list Data
95+const tableData = ref<Array<any>>([]);
96+const {
97+ data: res,
98+ run: requestData,
99+ loading: reading,
100+} = useRequest(
101+ () => {
102+ return restRequest
103+ .get("/wdr/listSnapshot", {
104+ clusterId: cluster.value.length ? cluster.value[0] : "",
105+ hostId: cluster.value.length > 1 ? cluster.value[1] : "",
106+ orderby: "snapshot_id desc",
107+ pageSize: page.pageSize,
108+ pageNum: page.currentPage,
109+ })
110+ .then(function (res) {
111+ return res;
112+ })
113+ .catch(function (res) {
114+ tableData.value = [];
115+ Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
116+ });
117+ },
118+ { manual: true }
119+);
120+type Res =
121+ | {
122+ records: string[];
123+ pageNum: number;
124+ total: number;
125+ }
126+ | undefined;
127+watch(res, (res: Res) => {
128+ if (res && res.records && res.records.length) {
129+ const { total } = res
130+ tableData.value = res.records;
131+ Object.assign(page, { pageSize: page.pageSize, total })
132+ } else {
133+ tableData.value = [];
134+ }
135+});
136+</script>
137+ 
138+<style lang="scss" scoped>
139+@import "../../../assets/style/style1.scss";
140+</style>
Mplugins/observability-instance/web-ui/src/pages/sql_detail/sql_diagnose/Index.vue+79-79
@@ -1,7 +1,7 @@
1<template>1<template>
2 <div class="search-form">2 <div class="search-form">
3 <div class="filter">3 <div class="filter">
4- <el-button type="primary" @click="handleModal">{{ $t('sql.sqlDiagnoseCreateTask') }}</el-button>4+ <el-button type="primary" @click="handleModal">{{ $t("sql.sqlDiagnoseCreateTask") }}</el-button>
5 </div>5 </div>
6 </div>6 </div>
7 <div class="page-container">7 <div class="page-container">
@@ -20,7 +20,7 @@
20 <span v-if="scope.row.sql && scope.row.sql.length > 35">20 <span v-if="scope.row.sql && scope.row.sql.length > 35">
21 <el-popover width="300" trigger="hover" :content="scope.row.sql" popper-class="sql-popover-tip">21 <el-popover width="300" trigger="hover" :content="scope.row.sql" popper-class="sql-popover-tip">
22 <template #reference>22 <template #reference>
23- <span>{{ scope.row.sql.substr(0, 35) + '...' }}</span>23+ <span>{{ scope.row.sql.substr(0, 35) + "..." }}</span>
24 </template>24 </template>
25 </el-popover>25 </el-popover>
26 </span>26 </span>
@@ -30,25 +30,25 @@
30 <el-table-column prop="state" :label="$t('datasource.trackTable[2]')" width="100" align="center" />30 <el-table-column prop="state" :label="$t('datasource.trackTable[2]')" width="100" align="center" />
31 <el-table-column :label="$t('datasource.trackTable[3]')" width="140" align="center">31 <el-table-column :label="$t('datasource.trackTable[3]')" width="140" align="center">
32 <template #default="scope">32 <template #default="scope">
33- {{ scope.row.starttime ? dayjs.utc(scope.row.starttime).local().format('YYYY-MM-DD HH:mm:ss') : '' }}33+ {{ scope.row.starttime ? dayjs.utc(scope.row.starttime).local().format("YYYY-MM-DD HH:mm:ss") : "" }}
34 </template>34 </template>
35 </el-table-column>35 </el-table-column>
36 <el-table-column :label="$t('datasource.trackTable[4]')" width="140" align="center">36 <el-table-column :label="$t('datasource.trackTable[4]')" width="140" align="center">
37 <template #default="scope">37 <template #default="scope">
38- <div>{{ scope.row.endtime ? dayjs.utc(scope.row.endtime).local().format('YYYY-MM-DD HH:mm:ss') : '' }}</div>38+ <div>{{ scope.row.endtime ? dayjs.utc(scope.row.endtime).local().format("YYYY-MM-DD HH:mm:ss") : "" }}</div>
39 </template>39 </template>
40 </el-table-column>40 </el-table-column>
41 <el-table-column prop="cost" :label="$t('datasource.trackTable[5]')" width="100" />41 <el-table-column prop="cost" :label="$t('datasource.trackTable[5]')" width="100" />
42 <el-table-column :label="$t('datasource.trackTable[6]')" width="140" align="center">42 <el-table-column :label="$t('datasource.trackTable[6]')" width="140" align="center">
43 <template #default="scope">43 <template #default="scope">
44- <div>{{ scope.row.createtime ? dayjs.utc(scope.row.createtime).local().format('YYYY-MM-DD HH:mm:ss') : '' }}</div>44+ <div>{{ scope.row.createtime ? dayjs.utc(scope.row.createtime).local().format("YYYY-MM-DD HH:mm:ss") : "" }}</div>
45 </template>45 </template>
46 </el-table-column>46 </el-table-column>
47 <el-table-column prop="clusterId" :label="$t('datasource.trackTable[7]')" width="100" align="center" />47 <el-table-column prop="clusterId" :label="$t('datasource.trackTable[7]')" width="100" align="center" />
48 <el-table-column prop="nodeId" :label="$t('datasource.trackTable[8]')" width="100" align="center" />48 <el-table-column prop="nodeId" :label="$t('datasource.trackTable[8]')" width="100" align="center" />
49 <el-table-column :label="$t('datasource.trackTable[9]')" align="center" fixed="right" width="80">49 <el-table-column :label="$t('datasource.trackTable[9]')" align="center" fixed="right" width="80">
50 <template #default="scope">50 <template #default="scope">
51- <el-link size="small" type="primary" @click="handleDelete(scope.row)">{{ $t('app.delete') }}</el-link>51+ <el-link size="small" type="primary" @click="handleDelete(scope.row)">{{ $t("app.delete") }}</el-link>
52 </template>52 </template>
53 </el-table-column>53 </el-table-column>
54 </el-table>54 </el-table>
@@ -59,149 +59,149 @@
59</template>59</template>
60 60 
61<script setup lang="ts">61<script setup lang="ts">
62-import dayjs from 'dayjs'62+import dayjs from "dayjs";
63-import utc from 'dayjs/plugin/utc';63+import utc from "dayjs/plugin/utc";
64-import timezone from 'dayjs/plugin/timezone';64+import timezone from "dayjs/plugin/timezone";
65-import { Delete } from '@element-plus/icons-vue'65+import { Delete } from "@element-plus/icons-vue";
66-import 'element-plus/es/components/message-box/style/index'66+import "element-plus/es/components/message-box/style/index";
67-import { useRequest } from 'vue-request'67+import { useRequest } from "vue-request";
68-import diagnosisRequest from '../../../request/diagnosis'68+import diagnosisRequest from "../../../request/diagnosis";
69-import { i18n } from '../../../i18n'69+import { i18n } from "../../../i18n";
70-import { ElMessageBox } from 'element-plus'70+import { ElMessageBox } from "element-plus";
71-import TrackAdd from './trackAdd.vue'71+import TrackAdd from "./trackAdd.vue";
72-import { useI18n } from 'vue-i18n'72+import { useI18n } from "vue-i18n";
73-const { t } = useI18n()73+const { t } = useI18n();
74 74 
75dayjs.extend(utc);75dayjs.extend(utc);
76dayjs.extend(timezone);76dayjs.extend(timezone);
77 77 
78const props = withDefaults(78const props = withDefaults(
79 defineProps<{79 defineProps<{
80- dbid: any80+ dbid: any;
81- sqlId: string | string[]81+ sqlId: string | string[];
82- sqlText: string82+ sqlText: string;
83- dbName: string83+ dbName: string;
84 }>(),84 }>(),
85 {85 {
86- dbid: '',86+ dbid: "",
87- sqlId: '',87+ sqlId: "",
88- sqlText: '',88+ sqlText: "",
89- dbName: '',89+ dbName: "",
90 }90 }
91-)91+);
92type Res =92type Res =
93 | {93 | {
94- tableData: string[]94+ tableData: string[];
95- total: number95+ total: number;
96- current: number96+ current: number;
97- records: string[]97+ records: string[];
98 }98 }
99- | undefined99+ | undefined;
100 100 
101-const addModel = ref(false)101+const addModel = ref(false);
102-const sqlText = ref('')102+const sqlText = ref("");
103-const tableData = ref<Array<any>>([])103+const tableData = ref<Array<any>>([]);
104const page = reactive({104const page = reactive({
105 currentPage: 1,105 currentPage: 1,
106 pageSize: 10,106 pageSize: 10,
107 total: 10,107 total: 10,
108-})108+});
109 109 
110const queryData = computed(() => {110const queryData = computed(() => {
111- const { pageSize: pagesize, currentPage: current } = page111+ const { pageSize: pagesize, currentPage: current } = page;
112 const queryObj = {112 const queryObj = {
113 dbName: props.dbName,113 dbName: props.dbName,
114 pageNum: current,114 pageNum: current,
115 pageSize: pagesize,115 pageSize: pagesize,
116 sqlId: props.sqlId,116 sqlId: props.sqlId,
117- }117+ };
118- return queryObj118+ return queryObj;
119-})119+});
120onMounted(() => {120onMounted(() => {
121- requestData()121+ requestData();
122-})122+});
123const gotoTaskDetail = (id: string) => {123const gotoTaskDetail = (id: string) => {
124 window.$wujie?.props.methods.jump({124 window.$wujie?.props.methods.jump({
125 name: `Static-pluginObservability-sql-diagnosisVemTrack_detail`,125 name: `Static-pluginObservability-sql-diagnosisVemTrack_detail`,
126 query: {126 query: {
127 id,127 id,
128 },128 },
129- })129+ });
130-}130+};
131const handleModal = () => {131const handleModal = () => {
132- addModel.value = true132+ addModel.value = true;
133-}133+};
134 134 
135const changeModalCurrent = (val: boolean) => {135const changeModalCurrent = (val: boolean) => {
136- addModel.value = val136+ addModel.value = val;
137-}137+};
138const bandleCovey = (code: number) => {138const bandleCovey = (code: number) => {
139- page.currentPage = 1139+ page.currentPage = 1;
140- requestData()140+ requestData();
141-}141+};
142const handleSizeChange = (val: number) => {142const handleSizeChange = (val: number) => {
143- page.currentPage = 1143+ page.currentPage = 1;
144- page.pageSize = val144+ page.pageSize = val;
145- requestData()145+ requestData();
146-}146+};
147const handleCurrentChange = (val: number) => {147const handleCurrentChange = (val: number) => {
148- page.currentPage = val148+ page.currentPage = val;
149- requestData()149+ requestData();
150-}150+};
151const handleDelete = (val: any) => {151const handleDelete = (val: any) => {
152- ElMessageBox.confirm(t('datasource.confirmToDeleteTask'))152+ ElMessageBox.confirm(t("datasource.confirmToDeleteTask"))
153 .then(() => {153 .then(() => {
154- hanleDelete(val.id)154+ hanleDelete(val.id);
155 })155 })
156 .catch(() => {156 .catch(() => {
157- console.log('cancel')157+ console.log("cancel");
158 // catch error158 // catch error
159- })159+ });
160-}160+};
161watch(161watch(
162 () => props.sqlText,162 () => props.sqlText,
163 (newValue) => {163 (newValue) => {
164- sqlText.value = newValue164+ sqlText.value = newValue;
165 },165 },
166 { immediate: true }166 { immediate: true }
167-)167+);
168 168 
169const { data: res, run: requestData } = useRequest(169const { data: res, run: requestData } = useRequest(
170 () => {170 () => {
171- return diagnosisRequest.get('/sqlDiagnosis/api/v1/diagnosisTasks', queryData.value)171+ return diagnosisRequest.get("/sqlDiagnosis/api/v1/diagnosisTasks", queryData.value);
172 },172 },
173 { manual: true }173 { manual: true }
174-)174+);
175 175 
176// Delete one task176// Delete one task
177const hanleDelete = (id: string) => {177const hanleDelete = (id: string) => {
178 useRequest(178 useRequest(
179 () => {179 () => {
180- return diagnosisRequest.delete(`/sqlDiagnosis/api/v1/diagnosisTasks/${id}`)180+ return diagnosisRequest.delete(`/sqlDiagnosis/api/v1/diagnosisTasks/${id}`);
181 },181 },
182 {182 {
183 onSuccess: (data) => {183 onSuccess: (data) => {
184 if (JSON.stringify(data)) {184 if (JSON.stringify(data)) {
185- requestData()185+ requestData();
186 }186 }
187 },187 },
188 }188 }
189- )189+ );
190-}190+};
191watch(res, (res: Res) => {191watch(res, (res: Res) => {
192 if (res && Object.keys(res).length) {192 if (res && Object.keys(res).length) {
193- const { total, current } = res193+ const { total } = res;
194- tableData.value = res.records194+ tableData.value = res.records;
195- Object.assign(page, { pageSize: page.pageSize, total, currentPage: current })195+ Object.assign(page, { pageSize: page.pageSize, total });
196 } else {196 } else {
197- tableData.value = []197+ tableData.value = [];
198- Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 })198+ Object.assign(page, { pageSize: page.pageSize, total: 0, currentPage: 1 });
199 }199 }
200-})200+});
201</script>201</script>
202 202 
203<style scoped lang="scss">203<style scoped lang="scss">
204-.el-link.el-link--primary{204+.el-link.el-link--primary {
205 color: var(--primary-6) !important;205 color: var(--primary-6) !important;
206}206}
207.el-button {207.el-button {
@@ -210,7 +210,7 @@ watch(res, (res: Res) => {
210 border: none !important;210 border: none !important;
211}211}
212.el-button.el-button--primary {212.el-button.el-button--primary {
213- color: #fff !important;213+ color: var(--color-bg-2) !important;
214 background-color: var(--primary-6) !important;214 background-color: var(--primary-6) !important;
215}215}
216.el-button.el-button--primary.search-button {216.el-button.el-button--primary.search-button {
Mplugins/observability-instance/web-ui/src/pages/sql_detail/sql_diagnose/trackAdd.vue+0-3
@@ -204,9 +204,6 @@ watch(rez, (rez: Rez) => {
204 &:deep(.el-dialog .el-dialog__header) {204 &:deep(.el-dialog .el-dialog__header) {
205 text-align: center;205 text-align: center;
206 }206 }
207- &:deep(.el-dialog .el-dialog__title) {
208- color: #fff;
209- }
210 &:deep(.el-form-item--small .el-form-item__label) {207 &:deep(.el-form-item--small .el-form-item__label) {
211 width: 110px;208 width: 110px;
212 }209 }
Aplugins/observability-instance/web-ui/src/request/platform.ts+175-0
@@ -0,0 +1,175 @@
1+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
2+ 
3+const platformBaseURL = "";
4+ 
5+interface ApiResponse<T = any> {
6+ code: string | number;
7+ data: T;
8+ error: any;
9+ msg: string;
10+}
11+ 
12+const handleData = <T>(res: AxiosResponse<ApiResponse<T>, any>) => {
13+ if (res.data && (res.data.code === "200" || res.data.code === 200)) {
14+ return Promise.resolve(res.data.data);
15+ } else {
16+ return Promise.reject(res?.data.msg || "Request Error").catch((err) => {
17+ console.log(err);
18+ });
19+ }
20+};
21+ 
22+const handleRESTfulData = <T>(res: AxiosResponse<T, any>) => {
23+ return Promise.resolve(res.data);
24+};
25+ 
26+export class Request {
27+ constructor(config?: AxiosRequestConfig) {
28+ if (config) {
29+ for (const key in config) {
30+ if (key in axios.defaults) {
31+ // @ts-ignore
32+ axios.defaults[key] = config[key];
33+ }
34+ }
35+ }
36+ }
37+ 
38+ async getNative<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
39+ const token = localStorage.getItem("opengauss-token");
40+ if (token) {
41+ if (!config) {
42+ config = {};
43+ }
44+ if (!config.headers) {
45+ config.headers = {};
46+ }
47+ config.headers.Authorization = `Bearer ${token}`;
48+ }
49+ try {
50+ return await axios.get<ApiResponse<T>>(`${platformBaseURL}${url}`, { params, ...config });
51+ } catch (error) {
52+ const err = error as AxiosError<any>;
53+ if (err.response && err.response.data && err.response.data.msg) {
54+ return Promise.reject(err.response.data.msg);
55+ }
56+ return Promise.reject(error);
57+ }
58+ }
59+ 
60+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
61+ const token = localStorage.getItem("opengauss-token");
62+ if (token) {
63+ if (!config) {
64+ config = {};
65+ }
66+ if (!config.headers) {
67+ config.headers = {};
68+ }
69+ config.headers.Authorization = `Bearer ${token}`;
70+ }
71+ try {
72+ return handleRESTfulData<T>(await axios.get<T>(`${platformBaseURL}${url}`, { params, ...config }));
73+ } catch (error) {
74+ const err = error as AxiosError<any>;
75+ if (err.response && err.response.data && err.response.data.msg) {
76+ return Promise.reject(err.response.data.msg);
77+ }
78+ return Promise.reject(error);
79+ }
80+ }
81+ 
82+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
83+ const token = localStorage.getItem("opengauss-token");
84+ if (token) {
85+ if (!config) {
86+ config = {};
87+ }
88+ if (!config.headers) {
89+ config.headers = {};
90+ }
91+ config.headers.Authorization = `Bearer ${token}`;
92+ }
93+ try {
94+ return handleRESTfulData<T>(await axios.delete<T>(`${platformBaseURL}${url}`, { params, ...config }));
95+ } catch (error) {
96+ const err = error as AxiosError<any>;
97+ if (err.response && err.response.data && err.response.data.msg) {
98+ return Promise.reject(err.response.data.msg);
99+ }
100+ return Promise.reject(error);
101+ }
102+ }
103+ 
104+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
105+ const token = localStorage.getItem("opengauss-token");
106+ if (token) {
107+ if (!config) {
108+ config = {};
109+ }
110+ if (!config.headers) {
111+ config.headers = {};
112+ }
113+ config.headers.Authorization = `Bearer ${token}`;
114+ }
115+ try {
116+ return handleRESTfulData<T>(await axios.post<T>(`${platformBaseURL}${url}`, data, config));
117+ } catch (error) {
118+ const err = error as AxiosError<any>;
119+ if (err.response && err.response.data && err.response.data.msg) {
120+ return Promise.reject(err.response.data.msg);
121+ }
122+ return Promise.reject(error);
123+ }
124+ }
125+ 
126+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
127+ const token = localStorage.getItem("opengauss-token");
128+ if (token) {
129+ if (!config) {
130+ config = {};
131+ }
132+ if (!config.headers) {
133+ config.headers = {};
134+ }
135+ config.headers.Authorization = `Bearer ${token}`;
136+ }
137+ try {
138+ return handleData<T>(await axios.put<ApiResponse<T>>(`${platformBaseURL}${url}`, data, config));
139+ } catch (error) {
140+ const err = error as AxiosError<any>;
141+ if (err.response && err.response.data && err.response.data.msg) {
142+ return Promise.reject(err.response.data.msg);
143+ }
144+ return Promise.reject(error);
145+ }
146+ }
147+ 
148+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
149+ const token = localStorage.getItem("opengauss-token");
150+ if (token) {
151+ if (!config) {
152+ config = {};
153+ }
154+ if (!config.headers) {
155+ config.headers = {};
156+ }
157+ config.headers.Authorization = `Bearer ${token}`;
158+ }
159+ try {
160+ return handleData<T>(await axios.patch<ApiResponse<T>>(`${platformBaseURL}${url}`, data, config));
161+ } catch (error) {
162+ const err = error as AxiosError<any>;
163+ if (err.response && err.response.data && err.response.data.msg) {
164+ return Promise.reject(err.response.data.msg);
165+ }
166+ return Promise.reject(error);
167+ }
168+ }
169+}
170+ 
171+const platformRequest = new Request({
172+ baseURL: import.meta.env.VITE_BASE_URL,
173+});
174+ 
175+export default platformRequest;
Aplugins/observability-instance/web-ui/src/request/restful.ts+166-0
@@ -0,0 +1,166 @@
1+import axios, { AxiosError, AxiosRequestConfig } from "axios";
2+import { ElMessage } from "element-plus";
3+ 
4+const observabilityBaseURL = process.env.mode === "production" ? "/plugins/observability-instance" : "";
5+ 
6+interface ApiResponse<T = any> {
7+ code: string | number;
8+ data: T;
9+ error: any;
10+ msg: string;
11+}
12+ 
13+const handleRESTfulData = (res: any) => {
14+ if (isSuccessResponse(res)) {
15+ return Promise.resolve(res.data);
16+ } else {
17+ ElMessage.error(res?.data.msg || "Request Error");
18+ return Promise.reject(res?.data?.msg || "Request Error").catch((err) => {
19+ console.log(err);
20+ });
21+ }
22+};
23+ 
24+const isSuccessResponse = (res: any) => {
25+ if (res.status === 200) {
26+ if (res.data === undefined) {
27+ return true;
28+ } else if (Object.keys(res.data).length === 2 && res.data.code && res.data.msg && res.data.code !== 200) {
29+ return false;
30+ } else return true;
31+ } else return false;
32+};
33+ 
34+export class Request {
35+ constructor(config?: AxiosRequestConfig) {
36+ if (config) {
37+ for (const key in config) {
38+ if (key in axios.defaults) {
39+ // @ts-ignore
40+ axios.defaults[key] = config[key];
41+ }
42+ }
43+ }
44+ }
45+ 
46+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
47+ const token = localStorage.getItem("opengauss-token");
48+ if (token) {
49+ if (!config) {
50+ config = {};
51+ }
52+ if (!config.headers) {
53+ config.headers = {};
54+ }
55+ config.headers.Authorization = `Bearer ${token}`;
56+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
57+ }
58+ try {
59+ return handleRESTfulData<T>(await axios.get<T>(`${observabilityBaseURL}${url}`, { params, ...config }));
60+ } catch (error) {
61+ const err = error as AxiosError<any>;
62+ if (err.response && err.response.data && err.response.data.msg) {
63+ return Promise.reject(err.response.data.msg);
64+ }
65+ return Promise.reject(error);
66+ }
67+ }
68+ 
69+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
70+ const token = localStorage.getItem("opengauss-token");
71+ if (token) {
72+ if (!config) {
73+ config = {};
74+ }
75+ if (!config.headers) {
76+ config.headers = {};
77+ }
78+ config.headers.Authorization = `Bearer ${token}`;
79+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
80+ }
81+ try {
82+ return handleRESTfulData<T>(await axios.delete<T>(`${observabilityBaseURL}${url}`, { params, ...config }));
83+ } catch (error) {
84+ const err = error as AxiosError<any>;
85+ if (err.response && err.response.data && err.response.data.msg) {
86+ return Promise.reject(err.response.data.msg);
87+ }
88+ return Promise.reject(error);
89+ }
90+ }
91+ 
92+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
93+ const token = localStorage.getItem("opengauss-token");
94+ if (token) {
95+ if (!config) {
96+ config = {};
97+ }
98+ if (!config.headers) {
99+ config.headers = {};
100+ }
101+ config.headers.Authorization = `Bearer ${token}`;
102+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
103+ }
104+ try {
105+ return handleRESTfulData<T>(await axios.post<T>(`${observabilityBaseURL}${url}`, data, config));
106+ } catch (error) {
107+ const err = error as AxiosError<any>;
108+ if (err.response && err.response.data && err.response.data.msg) {
109+ return Promise.reject(err.response.data.msg);
110+ }
111+ return Promise.reject(error);
112+ }
113+ }
114+ 
115+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
116+ const token = localStorage.getItem("opengauss-token");
117+ if (token) {
118+ if (!config) {
119+ config = {};
120+ }
121+ if (!config.headers) {
122+ config.headers = {};
123+ }
124+ config.headers.Authorization = `Bearer ${token}`;
125+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
126+ }
127+ try {
128+ return handleRESTfulData<T>(await axios.put<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config));
129+ } catch (error) {
130+ const err = error as AxiosError<any>;
131+ if (err.response && err.response.data && err.response.data.msg) {
132+ return Promise.reject(err.response.data.msg);
133+ }
134+ return Promise.reject(error);
135+ }
136+ }
137+ 
138+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
139+ const token = localStorage.getItem("opengauss-token");
140+ if (token) {
141+ if (!config) {
142+ config = {};
143+ }
144+ if (!config.headers) {
145+ config.headers = {};
146+ }
147+ config.headers.Authorization = `Bearer ${token}`;
148+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
149+ }
150+ try {
151+ return handleRESTfulData<T>(await axios.patch<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config));
152+ } catch (error) {
153+ const err = error as AxiosError<any>;
154+ if (err.response && err.response.data && err.response.data.msg) {
155+ return Promise.reject(err.response.data.msg);
156+ }
157+ return Promise.reject(error);
158+ }
159+ }
160+}
161+ 
162+const restRequest = new Request({
163+ baseURL: import.meta.env.VITE_BASE_URL,
164+});
165+ 
166+export default restRequest;
Mplugins/observability-instance/web-ui/src/store/monitor.ts+10-0
@@ -53,6 +53,16 @@ export const useMonitorStore = defineStore("monitor", {
53 rangeTime: 1,53 rangeTime: 1,
54 time: null54 time: null
55 },55 },
56+ {
57+ refreshTime: 30,
58+ rangeTime: 1,
59+ time: null
60+ },
61+ {
62+ refreshTime: 30,
63+ rangeTime: 1,
64+ time: null
65+ },
56 ],66 ],
57 autoRefresh: false,67 autoRefresh: false,
58 instanceTimeRange: null,68 instanceTimeRange: null,
Aplugins/observability-instance/web-ui/src/utils/jsencrypt.ts+29-0
@@ -0,0 +1,29 @@
1+import JsEncrypt from "jsencrypt";
2+import platformRequest from "../request/platform";
3+ 
4+interface KeyValue {
5+ [key: string]: any;
6+}
7+interface Res {
8+ data: KeyValue;
9+}
10+// host password encryption
11+export async function encryptPassword(pwd: string) {
12+ let publicKey = "";
13+ const getPublicKey: KeyValue = await getEntryKey();
14+ if (Number(getPublicKey.code) === 200 && getPublicKey.key) {
15+ const newKey = getPublicKey.key;
16+ publicKey = newKey;
17+ }
18+ const encryptor = new JsEncrypt();
19+ encryptor.setPublicKey(publicKey);
20+ return encryptor.encrypt(pwd);
21+}
22+const getEntryKey: KeyValue = (data) => {
23+ return platformRequest
24+ .getNative("/encryption/getKey", {})
25+ .then(function (res: Res) {
26+ return res.data;
27+ })
28+ .catch(function (res) {});
29+};
Aplugins/observability-instance/web-ui/src/utils/websocket.ts+102-0
@@ -0,0 +1,102 @@
1+export default class WebSocketClass {
2+ // ws = null;
3+ ws: WebSocket;
4+ name = null;
5+ sessionId: null;
6+ instance = null;
7+ callback = null;
8+ connected = false;
9+ setIntervalWesocketPush = null;
10+ static instance: any;
11+ 
12+ static getInstance(name, sessionId) {
13+ if (!this.instance) {
14+ this.instance = new WebSocketClass(name, sessionId);
15+ }
16+ return this.instance;
17+ }
18+ 
19+ constructor(name: string, sessionId, callback?) {
20+ this.name = name;
21+ this.sessionId = sessionId;
22+ this.instance = null;
23+ this.connect(name, sessionId, callback);
24+ }
25+ 
26+ connect(name: string, sessionId, callback?) {
27+ if (!window.WebSocket) {
28+ return console.log("Your browser does not support WebSocket");
29+ }
30+ const baseURL = import.meta.env.DEV ? `${import.meta.env.VITE_WS_BASE_URL}` : `${location.protocol == "http:" ? "ws:" : "wss:"}//${location.host}`;
31+ // const url = `${baseURL}/ws/observability-instance-test/${sessionId}`;
32+ const url = `ws://10.10.9.221:9494/ws/observability-instance-test/${sessionId}`;
33+ this.ws = new WebSocket(url);
34+ 
35+ this.ws.onopen = () => {
36+ this.connected = true;
37+ // this.sendPing();
38+ };
39+ if (callback) this.callback = callback;
40+ 
41+ this.ws.onclose = () => {
42+ this.connected = false;
43+ clearInterval(this.setIntervalWesocketPush);
44+ };
45+ 
46+ this.ws.onmessage = (msg: any) => {
47+ if (this.callback) {
48+ this.callback.call(this, msg.data);
49+ }
50+ };
51+ 
52+ this.ws.onerror = () => {
53+ if (this.ws.readyState !== 3) {
54+ this.connect(name, sessionId);
55+ }
56+ };
57+ }
58+ 
59+ registerCallBack(callBack) {
60+ this.callback = callBack;
61+ }
62+ 
63+ unRegisterCallBack() {
64+ this.callback = null;
65+ }
66+ 
67+ send(data) {
68+ if (!!this.ws && this.ws.readyState === 3) {
69+ this.ws.close();
70+ this.connect(this.name, this.sessionId);
71+ } else if (this.ws.readyState === 1) {
72+ this.ws.send(JSON.stringify(data));
73+ } else if (this.ws.readyState === 0) {
74+ this.connecting(data);
75+ }
76+ }
77+ 
78+ sendPing(time = 1000 * 20, ping = "ping") {
79+ clearInterval(this.setIntervalWesocketPush);
80+ this.ws.send(ping);
81+ this.setIntervalWesocketPush = setInterval(() => {
82+ this.ws.send(ping);
83+ }, time);
84+ }
85+ 
86+ // When sending data but the connection is not established, it will be processed and wait for retransmission
87+ connecting(message: any) {
88+ setTimeout(() => {
89+ if (this.ws.readyState === 0) {
90+ this.connecting(message);
91+ } else if (this.ws.readyState === 3) {
92+ return;
93+ } else {
94+ this.ws.send(JSON.stringify(message));
95+ }
96+ }, 1000);
97+ }
98+ 
99+ close() {
100+ this.ws.close();
101+ }
102+}
Mplugins/observability-instance/web-ui/src/vite-env.d.ts+1-0
@@ -5,3 +5,4 @@ declare module "*.vue" {
5 const component: DefineComponent<{}, {}, any>;5 const component: DefineComponent<{}, {}, any>;
6 export default component;6 export default component;
7}7}
8+declare module 'jsencrypt'
Mplugins/observability-instance/web-ui/vite.config.ts+41-36
@@ -1,63 +1,68 @@
1-import { defineConfig, loadEnv } from 'vite'1+import { defineConfig, loadEnv } from "vite";
2-import vue from '@vitejs/plugin-vue'2+import vue from "@vitejs/plugin-vue";
3-import AutoImport from 'unplugin-auto-import/vite'3+import AutoImport from "unplugin-auto-import/vite";
4-import Components from 'unplugin-vue-components/vite'4+import Components from "unplugin-vue-components/vite";
5-import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'5+import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
6-import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'6+import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
7-import { resolve } from 'path';7+import { resolve } from "path";
8 8 
9// https://vitejs.dev/config/9// https://vitejs.dev/config/
10-export default defineConfig(({ command, mode }) => { 10+export default defineConfig(({ command, mode }) => {
11- loadEnv(mode, process.cwd()); 11+ loadEnv(mode, process.cwd());
12 return {12 return {
13- base: mode === 'production' ? "/static-plugin/observability-instance/" : '/',13+ base: mode === "production" ? "/static-plugin/observability-instance/" : "/",
14 plugins: [14 plugins: [
15 vue(),15 vue(),
16 AutoImport({16 AutoImport({
17 resolvers: [ElementPlusResolver()],17 resolvers: [ElementPlusResolver()],
18- imports: ['vue', 'vue-router'],18+ imports: ["vue", "vue-router"],
19- dts: 'src/auto-imports.d.ts',19+ dts: "src/auto-imports.d.ts",
20 eslintrc: {20 eslintrc: {
21 enabled: false,21 enabled: false,
22- filepath: './.eslintrc-auto-import.json',22+ filepath: "./.eslintrc-auto-import.json",
23- globalsPropValue: true23+ globalsPropValue: true,
24- }24+ },
25 }),25 }),
26 Components({26 Components({
27- resolvers: [ElementPlusResolver({27+ resolvers: [
28- importStyle: 'sass'28+ ElementPlusResolver({
29- })],29+ importStyle: "sass",
30- dts: 'src/components.d.ts',30+ }),
31- dirs: ['src/components', 'src/layout']31+ ],
32+ dts: "src/components.d.ts",
33+ dirs: ["src/components", "src/layout"],
32 }),34 }),
33 createSvgIconsPlugin({35 createSvgIconsPlugin({
34- iconDirs: [resolve(process.cwd(), 'src/assets/svg')],36+ iconDirs: [resolve(process.cwd(), "src/assets/svg")],
35- symbolId: 'icon-[dir]-[name]',37+ symbolId: "icon-[dir]-[name]",
36- inject: 'body-first'38+ inject: "body-first",
37- })39+ }),
38 ],40 ],
39 define: {41 define: {
40- 'process.env': {42+ "process.env": {
41- mode43+ mode,
42- }44+ },
43 },45 },
44 resolve: {46 resolve: {
45 alias: {47 alias: {
46- '@': resolve(__dirname, './src/'),48+ "@": resolve(__dirname, "./src/"),
47 },49 },
48 },50 },
49 css: {51 css: {
50 preprocessorOptions: {52 preprocessorOptions: {
51 scss: {53 scss: {
52- additionalData: `@use "@/assets/style/theme.scss" as *;@use "@/assets/style/color.scss" as *;`54+ additionalData: `@use "@/assets/style/theme.scss" as *;@use "@/assets/style/color.scss" as *;`,
53- }55+ },
54- }56+ },
55 },57 },
56 server: {58 server: {
57 proxy: {59 proxy: {
58 '^/observability': 'http://localhost:8080/',60 '^/observability': 'http://localhost:8080/',
59- '^/sqlDiagnosis': 'http://localhost:8080/'61+ '^/sqlDiagnosis': 'http://localhost:8080/',
60- }62+ '^/wdr': 'http://localhost:8080/',
61- }63+ '^/encryption': 'http://localhost:8080/',
62- }64+ '^/host': 'http://localhost:8080/'
63-})65+ },
66+ },
67+ };
68+});