已合并
实例监控-330版本代码合入 #66
Louisyzh创建于 2023年2月21日
实例监控-330版本代码合入 #66
已合并
共 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 | + * LZW | ||
| 15 | + * 2023/1/5 | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +public class DataSourceConfig { | ||
| 19 | + | ||
| 20 | + | ||
| 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 | + | ||
| 23 | + | ||
| 24 | +public class EnvironmentController { | ||
| 25 | + | ||
| 26 | + private NctigbaEnvMapper envMapper; | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + private HostFacade hostFacade; | ||
| 30 | + | ||
| 31 | + private ClusterManager clusterManager; | ||
| 32 | + | ||
| 33 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +public class Installer implements SocketExtract { | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + private WsConnectorManager wsConnectorManager; | ||
| 33 | + | ||
| 34 | + private PrometheusService prometheusService; | ||
| 35 | + | ||
| 36 | + private ExporterService exporterService; | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + private WsFacade wsFacade; | ||
| 40 | + | ||
| 41 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 221 | public AppResult process( String id) { | 222 | public AppResult process( 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 | + * luomeng-gba.cn | ||
| 23 | + * 2023/01/30 15:00 | ||
| 24 | + */ | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +public class ParamInfoController { | ||
| 29 | + | ||
| 30 | + private final ParamInfoService paramInfoService; | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + protected EncryptionUtils encryptionUtils; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + public List<DatabaseParamDTO> databaseParamInfo(ParamQuery paramQuery) { | ||
| 39 | + return paramInfoService.getDatabaseParamInfo(paramQuery); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + | ||
| 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 | + * lhf | ||
| 35 | + * 2022/10/13 15:14 | ||
| 36 | + **/ | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +public class WdrController { | ||
| 40 | + | ||
| 41 | + public void initBinder(WebDataBinder binder) { | ||
| 42 | + // Date format | ||
| 43 | + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { | ||
| 44 | + | ||
| 45 | + public void setAsText(String text) { | ||
| 46 | + setValue(DateUtils.parseDate(text)); | ||
| 47 | + } | ||
| 48 | + }); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + private OpsWdrService wdrService; | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + public Page<?> listSnapshot( String clusterId, String hostId) { | ||
| 56 | + return wdrService.listSnapshot(startPage(), clusterId, hostId); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + public AjaxResult createSnapshot( String clusterId, String hostId) { | ||
| 61 | + wdrService.createSnapshot(clusterId, hostId); | ||
| 62 | + return AjaxResult.success(); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + public Page<OpsWdrEntity> list( String clusterId, | ||
| 67 | + WdrScopeEnum wdrScope, | ||
| 68 | + WdrTypeEnum wdrType, | ||
| 69 | + String hostId, | ||
| 70 | + Date start, | ||
| 71 | + Date end) { | ||
| 72 | + return wdrService.listWdr(startPage(), clusterId, wdrScope, wdrType, hostId, start, end); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + | ||
| 76 | + public AjaxResult del( String id) { | ||
| 77 | + wdrService.del(id); | ||
| 78 | + return AjaxResult.success(); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + public AjaxResult generate( WdrGeneratorBody wdrGeneratorBody) { | ||
| 83 | + wdrService.generate(wdrGeneratorBody); | ||
| 84 | + return AjaxResult.success(); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + | ||
| 88 | + public void downloadWdr( String wdrId, HttpServletResponse response) { | ||
| 89 | + wdrService.downloadWdr(wdrId, response); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + | ||
| 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 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +public class NctigbaEnv { | ||
| 17 | + | ||
| 18 | + String id; | ||
| 19 | + String hostid; | ||
| 20 | + type type; | ||
| 21 | + String username; | ||
| 22 | + String path; | ||
| 23 | + Integer port; | ||
| 24 | + | ||
| 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 | + * lhf | ||
| 17 | + * 2022/10/13 15:03 | ||
| 18 | + **/ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +public class OpsWdrEntity { | ||
| 22 | + | ||
| 23 | + private String wdrId; | ||
| 24 | + private WdrScopeEnum scope; | ||
| 25 | + | ||
| 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 | + | ||
| 41 | + private String searchValue; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * creator | ||
| 45 | + */ | ||
| 46 | + | ||
| 47 | + private String createBy; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * create time | ||
| 51 | + */ | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + private Date createTime; | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * updater | ||
| 58 | + */ | ||
| 59 | + | ||
| 60 | + private String updateBy; | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * update time | ||
| 64 | + */ | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + private Date updateTime; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * remark | ||
| 71 | + */ | ||
| 72 | + private String remark; | ||
| 73 | + | ||
| 74 | + | ||
| 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 | ||
| 16 | import org.apache.commons.lang3.ObjectUtils; | 16 | import org.apache.commons.lang3.ObjectUtils; |
| 17 | import org.apache.commons.lang3.StringUtils; | 17 | import 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; | ||
| 19 | import org.springframework.stereotype.Component; | 20 | import org.springframework.stereotype.Component; |
| 20 | import org.springframework.util.CollectionUtils; | 21 | import org.springframework.util.CollectionUtils; |
| 21 | import org.springframework.web.util.UriComponentsBuilder; | 22 | import org.springframework.web.util.UriComponentsBuilder; |
| @@ -24,10 +25,15 @@ import com.alibaba.fastjson.JSON; | |||
| 24 | import com.alibaba.fastjson.JSONArray; | 25 | import com.alibaba.fastjson.JSONArray; |
| 25 | import com.alibaba.fastjson.JSONObject; | 26 | import com.alibaba.fastjson.JSONObject; |
| 26 | import com.alibaba.fastjson.TypeReference; | 27 | import com.alibaba.fastjson.TypeReference; |
| 28 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||
| 29 | +import com.gitee.starblues.bootstrap.annotation.AutowiredType; | ||
| 27 | import com.nctigba.common.web.exception.CustomException; | 30 | import com.nctigba.common.web.exception.CustomException; |
| 28 | import com.nctigba.common.web.exception.CustomExceptionEnum; | 31 | import com.nctigba.common.web.exception.CustomExceptionEnum; |
| 29 | import com.nctigba.observability.instance.constants.MonitoringConstants; | 32 | import com.nctigba.observability.instance.constants.MonitoringConstants; |
| 30 | import com.nctigba.observability.instance.constants.MonitoringType; | 33 | import 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; | ||
| 31 | import com.nctigba.observability.instance.model.monitoring.MonitoringMetric; | 37 | import com.nctigba.observability.instance.model.monitoring.MonitoringMetric; |
| 32 | import com.nctigba.observability.instance.model.monitoring.MonitoringParam; | 38 | import com.nctigba.observability.instance.model.monitoring.MonitoringParam; |
| 33 | import com.nctigba.observability.instance.util.HttpUtils; | 39 | import com.nctigba.observability.instance.util.HttpUtils; |
| @@ -35,321 +41,343 @@ import com.nctigba.observability.instance.util.HttpUtils; | |||
| 35 | import cn.hutool.core.map.MapUtil; | 41 | import cn.hutool.core.map.MapUtil; |
| 36 | import lombok.extern.slf4j.Slf4j; | 42 | import lombok.extern.slf4j.Slf4j; |
| 37 | 43 | ||
| 38 | - | ||
| 39 | 44 | ||
| 40 | 45 | ||
| 41 | public class NormalMonitoringHandler implements MonitoringHandler { | 46 | public class NormalMonitoringHandler implements MonitoringHandler { |
| 42 | - @Value("${prometheus.server.url}") | 47 | + @Autowired |
| 43 | - private String prometheusUrl; | 48 | + protected NctigbaEnvMapper envMapper; |
| 44 | - | 49 | + @Autowired |
| 45 | - @Override | 50 | + @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 time | 54 | + var env = envMapper.selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, type.PROMETHEUS)); |
| 52 | - * | 55 | + if (env == null) |
| 53 | - * @param query Indicator parameters | 56 | + throw new RuntimeException("Prometheus not found"); |
| 54 | - * @param start start time | 57 | + var host = hostFacade.getById(env.getHostid()); |
| 55 | - * @param end End time | 58 | + return "http://" + host.getPublicIp() + ":" + env.getPort(); |
| 56 | - * @param step step | 59 | + } |
| 57 | - * List<MonitoringMetric> | ||
| 58 | - */ | ||
| 59 | - | ||
| 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 time | 62 | + public String getMonitorType() { |
| 88 | - * | 63 | + return MonitoringType.DEFAULT.getMonitoringType(); |
| 89 | - * @param query Indicator parameters | 64 | + } |
| 90 | - * time Specify the timestamp. The default is the current system time of prometheus | ||
| 91 | - * List<MonitoringMetric> | ||
| 92 | - */ | ||
| 93 | - | ||
| 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 | - @Override | 66 | + /** |
| 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 order | 70 | + * @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 length | 82 | + 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 turn | 96 | + } |
| 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 Data | 109 | + * 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 data | 122 | + 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 field | 130 | + 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 | - @Override | 141 | + @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 name | 150 | + } |
| 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 value | 157 | + 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 format | 163 | + }); |
| 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 data | 197 | + 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 | - @Override | 285 | + // 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 | - // Digital | 292 | + } |
| 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 | * : 2022/12/4 15:05 | 18 | * : 2022/12/4 15:05 |
| 19 | */ | 19 | */ |
| 20 | public class PluginListener implements ApplicationListener<ApplicationEvent> { | 20 | public class PluginListener implements ApplicationListener<ApplicationEvent> { |
| 21 | + public static final String pluginId = "observability-instance"; | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 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 | + | ||
| 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 | + | ||
| 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 | 15 | ||
| 16 | public interface ServerInfoMapper extends BaseMapper<ServerInfoEntity> { | 16 | public 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 | + | ||
| 12 | +public class WdrGeneratorBody { | ||
| 13 | + | ||
| 14 | + private String clusterId; | ||
| 15 | + | ||
| 16 | + private WdrScopeEnum scope; | ||
| 17 | + private String hostId; | ||
| 18 | + | ||
| 19 | + private WdrTypeEnum type; | ||
| 20 | + | ||
| 21 | + private String startId; | ||
| 22 | + | ||
| 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 | + | ||
| 12 | + | ||
| 13 | +public class WdrSnapshotVO { | ||
| 14 | + | ||
| 15 | + private Integer snapshotId; | ||
| 16 | + | ||
| 17 | + private Date startTs; | ||
| 18 | + | ||
| 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 | + | ||
| 7 | + | ||
| 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 | + | ||
| 7 | + | ||
| 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 | + | ||
| 7 | +public class ParamQuery { | ||
| 8 | + | ||
| 9 | + private String paramName; | ||
| 10 | + | ||
| 11 | + private String nodeId; | ||
| 12 | + | ||
| 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 | + | ||
| 30 | + | ||
| 31 | + protected HostFacade hostFacade; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + protected EncryptionUtils encryptionUtils; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + protected HostUserFacade hostUserFacade; | ||
| 38 | + | ||
| 39 | + protected NctigbaEnvMapper envMapper; | ||
| 40 | + | ||
| 41 | + | ||
| 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 { DONE} and next step to { DOING}, send to | ||
| 70 | + * { 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 { DONE}, send to { 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 | + | ||
| 88 | + | ||
| 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 | + * lhf | ||
| 17 | + * 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 | + | ||
| 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 | + | ||
| 33 | + | ||
| 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 | + | ||
| 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 | + | ||
| 43 | + private ClusterManager clusterManager; | ||
| 44 | + | ||
| 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 | + * lhf | ||
| 50 | + * 2022/10/13 15:14 | ||
| 51 | + **/ | ||
| 52 | + | ||
| 53 | + | ||
| 54 | +public class OpsWdrService extends ServiceImpl<OpsWdrMapper, OpsWdrEntity> { | ||
| 55 | + | ||
| 56 | + | ||
| 57 | + private IOpsClusterService opsClusterService; | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + private IOpsClusterNodeService opsClusterNodeService; | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + private HostFacade hostFacade; | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + private HostUserFacade hostUserFacade; | ||
| 67 | + | ||
| 68 | + | ||
| 69 | + private JschUtil jschUtil; | ||
| 70 | + | ||
| 71 | + private ClusterOpsProviderManager clusterOpsProviderManager; | ||
| 72 | + | ||
| 73 | + private ClusterManager clusterManager; | ||
| 74 | + | ||
| 75 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 30 | + | ||
| 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 | + * :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 | + * :on | ||
| 142 | + */ | ||
| 143 | + | ||
| 144 | + public static class prometheusConfig { | ||
| 145 | + global global; | ||
| 146 | + alert alerting; | ||
| 147 | + List<String> rule_files; | ||
| 148 | + List<job> scrape_configs; | ||
| 149 | + | ||
| 150 | + | ||
| 151 | + public static class global { | ||
| 152 | + String scrape_interval; | ||
| 153 | + String evaluation_interval; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + public static class alert { | ||
| 158 | + List<alertmanager> alertmanagers; | ||
| 159 | + | ||
| 160 | + | ||
| 161 | + public static class alertmanager { | ||
| 162 | + List<conf> static_configs; | ||
| 163 | + | ||
| 164 | + | ||
| 165 | + public static class conf { | ||
| 166 | + List<target> targets; | ||
| 167 | + | ||
| 168 | + | ||
| 169 | + public static class target { | ||
| 170 | + int alertmanager; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + | ||
| 177 | + public static class job { | ||
| 178 | + String job_name; | ||
| 179 | + List<conf> static_configs; | ||
| 180 | + | ||
| 181 | + | ||
| 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 | + | ||
| 20 | + | ||
| 21 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + * lhf | ||
| 28 | + * 2022/8/12 09:20 | ||
| 29 | + **/ | ||
| 30 | + | ||
| 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 | + | ||
| 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 | + * lhf | ||
| 24 | + * 2022/8/12 09:23 | ||
| 25 | + **/ | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +public class EnterpriseOpsProvider extends AbstractOpsProvider { | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + private JschUtil jschUtil; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + public OpenGaussVersionEnum version() { | ||
| 35 | + return OpenGaussVersionEnum.ENTERPRISE; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + public OpenGaussSupportOSEnum os() { | ||
| 40 | + return OpenGaussSupportOSEnum.CENTOS_X86_64; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + | ||
| 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 | + * lhf | ||
| 27 | + * 2022/8/12 09:26 | ||
| 28 | + **/ | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +public class LiteOpsProvider extends AbstractOpsProvider { | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + private HostUserFacade hostUserFacade; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + private HostFacade hostFacade; | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + private JschUtil jschUtil; | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + public OpenGaussVersionEnum version() { | ||
| 44 | + return OpenGaussVersionEnum.LITE; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + public OpenGaussSupportOSEnum os() { | ||
| 49 | + return OpenGaussSupportOSEnum.CENTOS_X86_64; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + | ||
| 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 | + * lhf | ||
| 26 | + * 2022/8/12 09:25 | ||
| 27 | + **/ | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +public class MinimaListOpsProvider extends AbstractOpsProvider { | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + private JschUtil jschUtil; | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + public OpenGaussVersionEnum version() { | ||
| 37 | + return OpenGaussVersionEnum.MINIMAL_LIST; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + public OpenGaussSupportOSEnum os() { | ||
| 42 | + return OpenGaussSupportOSEnum.CENTOS_X86_64; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + | ||
| 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 | + * lhf | ||
| 24 | + * 2022/8/12 09:23 | ||
| 25 | + **/ | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +public class OpenEulerArch64EnterpriseOpsProvider extends AbstractOpsProvider { | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + private JschUtil jschUtil; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + public OpenGaussVersionEnum version() { | ||
| 35 | + return OpenGaussVersionEnum.ENTERPRISE; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + public OpenGaussSupportOSEnum os() { | ||
| 40 | + return OpenGaussSupportOSEnum.OPENEULER_ARCH64; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + | ||
| 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 | + * lhf | ||
| 25 | + * 2022/8/12 09:26 | ||
| 26 | + **/ | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +public class OpenEulerArch64LiteOpsProvider extends AbstractOpsProvider { | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + private JschUtil jschUtil; | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + public OpenGaussVersionEnum version() { | ||
| 36 | + return OpenGaussVersionEnum.LITE; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + public OpenGaussSupportOSEnum os() { | ||
| 41 | + return OpenGaussSupportOSEnum.OPENEULER_ARCH64; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + | ||
| 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 | + * lhf | ||
| 26 | + * 2022/8/12 09:25 | ||
| 27 | + **/ | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +public class OpenEulerArch64MinimaListOpsProvider extends AbstractOpsProvider { | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + private JschUtil jschUtil; | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + public OpenGaussVersionEnum version() { | ||
| 37 | + return OpenGaussVersionEnum.MINIMAL_LIST; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + public OpenGaussSupportOSEnum os() { | ||
| 42 | + return OpenGaussSupportOSEnum.OPENEULER_ARCH64; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + | ||
| 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 | + * lhf | ||
| 24 | + * 2022/8/12 09:23 | ||
| 25 | + **/ | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +public class OpenEulerX86EnterpriseOpsProvider extends AbstractOpsProvider { | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + private JschUtil jschUtil; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + public OpenGaussVersionEnum version() { | ||
| 35 | + return OpenGaussVersionEnum.ENTERPRISE; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + public OpenGaussSupportOSEnum os() { | ||
| 40 | + return OpenGaussSupportOSEnum.OPENEULER_X86_64; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + | ||
| 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 | + * lhf | ||
| 25 | + * 2022/8/12 09:26 | ||
| 26 | + **/ | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +public class OpenEulerX86LiteOpsProvider extends AbstractOpsProvider { | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + private JschUtil jschUtil; | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + public OpenGaussVersionEnum version() { | ||
| 36 | + return OpenGaussVersionEnum.LITE; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + public OpenGaussSupportOSEnum os() { | ||
| 41 | + return OpenGaussSupportOSEnum.OPENEULER_X86_64; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + | ||
| 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 | + * lhf | ||
| 26 | + * 2022/8/12 09:25 | ||
| 27 | + **/ | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +public class OpenEulerX86MinimaListOpsProvider extends AbstractOpsProvider { | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + private JschUtil jschUtil; | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + public OpenGaussVersionEnum version() { | ||
| 37 | + return OpenGaussVersionEnum.MINIMAL_LIST; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + public OpenGaussSupportOSEnum os() { | ||
| 42 | + return OpenGaussSupportOSEnum.OPENEULER_X86_64; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + | ||
| 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 | + | ||
| 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 | + | ||
| 146 | + public void init(int op, String src, String dest, long max) { | ||
| 147 | + this.max = max; | ||
| 148 | + System.out.println(op); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | +} | ||
| @@ -1,7 +1,3 @@ | |||
| 1 | spring: | 1 | spring: |
| 2 | autoconfigure: | 2 | autoconfigure: |
| 3 | exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration | 3 | exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration |
| 4 | - | ||
| 5 | -prometheus: | ||
| 6 | - server: | ||
| 7 | - url: http://172.16.107.108:9090 | ||
| @@ -1,3 +0,0 @@ | |||
| 1 | -prometheus: | ||
| 2 | - server: | ||
| 3 | - url: http://119.3.170.242:9090 | ||
| @@ -31,7 +31,7 @@ server.compression: | |||
| 31 | mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain | 31 | mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain |
| 32 | mybatis-plus: | 32 | mybatis-plus: |
| 33 | # 搜索指定包别名 | 33 | # 搜索指定包别名 |
| 34 | - typeAliasesPackage: com.nctigba.observability.entity | 34 | + typeAliasesPackage: com.nctigba.observability.instance.entity |
| 35 | # 配置mapper的扫描,找到所有的mapper.xml映射文件 | 35 | # 配置mapper的扫描,找到所有的mapper.xml映射文件 |
| 36 | mapperLocations: classpath*:mapper/*.xml | 36 | mapperLocations: classpath*:mapper/*.xml |
| 37 | # 加载全局的配置文件 | 37 | # 加载全局的配置文件 |
| @@ -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 | +); | ||
| @@ -1,3 +1,4 @@ | |||
| 1 | { | 1 | { |
| 2 | + "printWidth": 800, | ||
| 2 | "tabWidth": 4 | 3 | "tabWidth": 4 |
| 3 | } | 4 | } |
| @@ -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", |
| @@ -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 | +} | ||
| @@ -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 | } |
| @@ -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) ; | ||
| 158 | -} | ||
| 159 | .in-active-path .el-cascader-node__label { | 156 | .in-active-path .el-cascader-node__label { |
| 160 | color: var(--el-color-tabbar-active) ; | 157 | color: var(--el-color-tabbar-active) ; |
| 161 | } | 158 | } |
| @@ -224,7 +221,7 @@ body { | |||
| 224 | 221 | ||
| 225 | // button for message box | 222 | // 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) ; | 225 | background-color: var(--primary-6) ; |
| 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 { |
| @@ -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) ; | ||
| 7 | +} | ||
| 8 | +.el-button { | ||
| 9 | + color: var(--color-text-2) ; | ||
| 10 | + background-color: var(--color-secondary) ; | ||
| 11 | + border: none ; | ||
| 12 | +} | ||
| 13 | +.el-button.el-button--primary { | ||
| 14 | + color: var(--color-bg-2) ; | ||
| 15 | + background-color: var(--primary-6) ; | ||
| 16 | +} | ||
| 17 | +.el-button.el-button--primary.search-button { | ||
| 18 | + color: var(--primary-6) ; | ||
| 19 | + background-color: rgb(255, 255, 255, 0) ; | ||
| 20 | + border: 1px solid var(--primary-6) ; | ||
| 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 ; | ||
| 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 | +} | ||
| @@ -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 | } |
| @@ -7,12 +7,16 @@ export {} | |||
| 7 | 7 | ||
| 8 | declare module '@vue/runtime-core' { | 8 | declare 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'] |
| @@ -0,0 +1,91 @@ | |||
| 1 | +<template> | ||
| 2 | + <div class="filter"> | ||
| 3 | + <span>{{ props.title }} </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> | ||
| @@ -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> | ||
| @@ -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> | ||
| @@ -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', |
| @@ -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', |
| @@ -6,6 +6,7 @@ import 'element-plus/theme-chalk/el-message.css' | |||
| 6 | import 'element-plus/theme-chalk/dark/css-vars.css'; | 6 | import 'element-plus/theme-chalk/dark/css-vars.css'; |
| 7 | import '@/assets/style/dark.scss' | 7 | import '@/assets/style/dark.scss' |
| 8 | import '@/assets/style/reset.scss'; | 8 | import '@/assets/style/reset.scss'; |
| 9 | +import '@/assets/style/common.scss'; | ||
| 9 | import App from "./App.vue"; | 10 | import App from "./App.vue"; |
| 10 | import { createPinia } from "pinia"; | 11 | import { createPinia } from "pinia"; |
| 11 | import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; | 12 | import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; |
| @@ -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"; | ||
| 10 | import ogRequest from "../../request"; | 12 | import ogRequest from "../../request"; |
| 11 | import { useRequest } from "vue-request"; | 13 | import { useRequest } from "vue-request"; |
| 14 | +import Install from "./install/Index.vue"; | ||
| 15 | +import SystemConfiguration from "./system_configuration/Index.vue"; | ||
| 12 | 16 | ||
| 13 | const { t } = useI18n(); | 17 | const { t } = useI18n(); |
| 14 | 18 | ||
| 15 | -type Res = [{ | 19 | +type Res = |
| 16 | - [propName:string]:string | number | 20 | + | [ |
| 17 | -}] | undefined; | 21 | + { |
| 22 | + [propName: string]: string | number; | ||
| 23 | + } | ||
| 24 | + ] | ||
| 25 | + | undefined; | ||
| 18 | 26 | ||
| 19 | -const datePickerRef = ref<HTMLDivElement>() | 27 | +const datePickerRef = ref<HTMLDivElement>(); |
| 20 | const clusterNodeId = ref(); | 28 | const clusterNodeId = ref(); |
| 21 | const clusterList = ref<Array<any>[]>([]); | 29 | const clusterList = ref<Array<any>[]>([]); |
| 22 | const connectStatus = ref<boolean | undefined>(undefined); | 30 | const connectStatus = ref<boolean | undefined>(undefined); |
| 23 | const curServerInfoText = ref(""); | 31 | const curServerInfoText = ref(""); |
| 24 | const nodeVersion = ref<string>(""); | 32 | const 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 once | 36 | // 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] = true | 40 | + 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 | ||
| 38 | const autoRefreshFn = () => { | 48 | const autoRefreshFn = () => { |
| 39 | - autoRefresh.value = !autoRefresh.value | 49 | + 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 node | 60 | // 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 | ||
| 68 | const showConnectStatus = (status: boolean | undefined) => { | 74 | const 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 | ||
| 75 | const onDatePackerVisible = (v: boolean) => { | 81 | const 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-ignore | 84 | // @ts-ignore |
| 79 | - docu[0]?.blur() | 85 | + docu[0]?.blur(); |
| 80 | } | 86 | } |
| 81 | -} | 87 | +}; |
| 82 | 88 | ||
| 83 | const getVersionByNodeId = (curNodeId: string) => { | 89 | const 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 | -// 集群与主机IP | 108 | +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 = null | 136 | + 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-picker | 181 | + <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> |
| @@ -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> | ||
| @@ -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> | ||
| @@ -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> | ||
| @@ -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`, |
| @@ -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> | ||
| @@ -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> | ||
| @@ -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") }} </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") }} </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") }} </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> | ||
| @@ -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> | ||
| @@ -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") }} </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> | ||
| @@ -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 | ||
| 75 | dayjs.extend(utc); | 75 | dayjs.extend(utc); |
| 76 | dayjs.extend(timezone); | 76 | dayjs.extend(timezone); |
| 77 | 77 | ||
| 78 | const props = withDefaults( | 78 | const props = withDefaults( |
| 79 | defineProps<{ | 79 | defineProps<{ |
| 80 | - dbid: any | 80 | + dbid: any; |
| 81 | - sqlId: string | string[] | 81 | + sqlId: string | string[]; |
| 82 | - sqlText: string | 82 | + sqlText: string; |
| 83 | - dbName: string | 83 | + 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 | +); |
| 92 | type Res = | 92 | type Res = |
| 93 | | { | 93 | | { |
| 94 | - tableData: string[] | 94 | + tableData: string[]; |
| 95 | - total: number | 95 | + total: number; |
| 96 | - current: number | 96 | + current: number; |
| 97 | - records: string[] | 97 | + records: string[]; |
| 98 | } | 98 | } |
| 99 | - | undefined | 99 | + | 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>>([]); |
| 104 | const page = reactive({ | 104 | const page = reactive({ |
| 105 | currentPage: 1, | 105 | currentPage: 1, |
| 106 | pageSize: 10, | 106 | pageSize: 10, |
| 107 | total: 10, | 107 | total: 10, |
| 108 | -}) | 108 | +}); |
| 109 | 109 | ||
| 110 | const queryData = computed(() => { | 110 | const queryData = computed(() => { |
| 111 | - const { pageSize: pagesize, currentPage: current } = page | 111 | + 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 queryObj | 118 | + return queryObj; |
| 119 | -}) | 119 | +}); |
| 120 | onMounted(() => { | 120 | onMounted(() => { |
| 121 | - requestData() | 121 | + requestData(); |
| 122 | -}) | 122 | +}); |
| 123 | const gotoTaskDetail = (id: string) => { | 123 | const 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 | +}; |
| 131 | const handleModal = () => { | 131 | const handleModal = () => { |
| 132 | - addModel.value = true | 132 | + addModel.value = true; |
| 133 | -} | 133 | +}; |
| 134 | 134 | ||
| 135 | const changeModalCurrent = (val: boolean) => { | 135 | const changeModalCurrent = (val: boolean) => { |
| 136 | - addModel.value = val | 136 | + addModel.value = val; |
| 137 | -} | 137 | +}; |
| 138 | const bandleCovey = (code: number) => { | 138 | const bandleCovey = (code: number) => { |
| 139 | - page.currentPage = 1 | 139 | + page.currentPage = 1; |
| 140 | - requestData() | 140 | + requestData(); |
| 141 | -} | 141 | +}; |
| 142 | const handleSizeChange = (val: number) => { | 142 | const handleSizeChange = (val: number) => { |
| 143 | - page.currentPage = 1 | 143 | + page.currentPage = 1; |
| 144 | - page.pageSize = val | 144 | + page.pageSize = val; |
| 145 | - requestData() | 145 | + requestData(); |
| 146 | -} | 146 | +}; |
| 147 | const handleCurrentChange = (val: number) => { | 147 | const handleCurrentChange = (val: number) => { |
| 148 | - page.currentPage = val | 148 | + page.currentPage = val; |
| 149 | - requestData() | 149 | + requestData(); |
| 150 | -} | 150 | +}; |
| 151 | const handleDelete = (val: any) => { | 151 | const 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 error | 158 | // catch error |
| 159 | - }) | 159 | + }); |
| 160 | -} | 160 | +}; |
| 161 | watch( | 161 | watch( |
| 162 | () => props.sqlText, | 162 | () => props.sqlText, |
| 163 | (newValue) => { | 163 | (newValue) => { |
| 164 | - sqlText.value = newValue | 164 | + sqlText.value = newValue; |
| 165 | }, | 165 | }, |
| 166 | { immediate: true } | 166 | { immediate: true } |
| 167 | -) | 167 | +); |
| 168 | 168 | ||
| 169 | const { data: res, run: requestData } = useRequest( | 169 | const { 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 task | 176 | // Delete one task |
| 177 | const hanleDelete = (id: string) => { | 177 | const 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 | +}; |
| 191 | watch(res, (res: Res) => { | 191 | watch(res, (res: Res) => { |
| 192 | if (res && Object.keys(res).length) { | 192 | if (res && Object.keys(res).length) { |
| 193 | - const { total, current } = res | 193 | + const { total } = res; |
| 194 | - tableData.value = res.records | 194 | + 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 { |
| @@ -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 | } |
| @@ -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; | ||
| @@ -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; | ||
| @@ -53,6 +53,16 @@ export const useMonitorStore = defineStore("monitor", { | |||
| 53 | rangeTime: 1, | 53 | rangeTime: 1, |
| 54 | time: null | 54 | 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, |
| @@ -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 | +}; | ||
| @@ -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 | +} | ||
| @@ -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' | ||
| @@ -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: true | 23 | + 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 | - mode | 43 | + 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 | +}); | ||