feat ssh 基础信息监控(非报警监控)

This commit is contained in:
bwcx_jzy 2023-03-15 15:06:30 +08:00
parent 1e59ccabd6
commit 3b403b9dd6
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
14 changed files with 441 additions and 148 deletions

View File

@ -2,6 +2,10 @@
## 2.10.33
### 🐣 新增功能
1. 【server】新增 ssh 基础信息监控(非报警监控)
### 🐞 解决BUG、优化功能
1. 【server】修复 插件端在线升级页面无法正常使用(调用到服务端在线升级接口)

View File

@ -103,14 +103,14 @@ public class HttpTransportServer implements TransportServer {
throw new IllegalArgumentException("不支持的类型:" + o.getClass());
}
} else if (dataContentType == DataContentType.JSON) {
httpRequest.body(encryptor.encrypt(JSONObject.toJSONString(o)), cn.hutool.http.ContentType.JSON.getValue());
httpRequest.body(encryptor.encrypt(JSONObject.toJSONString(o)), ContentType.JSON.getValue());
} else {
throw new IllegalArgumentException("不支持的 contentType");
}
} catch (Exception e) {
throw Lombok.sneakyThrow(TransformServerFactory.get().transformException(e, nodeInfo));
log.error("编码异常", e);
throw new AgentException("节点传输信息编码异常:" + e.getMessage());
}
});
}

View File

@ -495,6 +495,10 @@ public class NodeForward {
* @param nodeModel 插件端
*/
public static AgentException responseException(Exception exception, INodeInfo nodeModel) {
if (exception instanceof NullPointerException) {
log.error("{}节点,程序空指针异常", nodeModel.name(), exception);
return new AgentException(nodeModel.name() + "节点异常,空指针");
}
String message = exception.getMessage();
Throwable cause = exception.getCause();
log.error("node [{}] connect failed...message: [{}]", nodeModel.name(), message);

View File

@ -25,16 +25,13 @@ package io.jpom.func.assets.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.extra.ssh.JschUtil;
import com.alibaba.fastjson2.JSONObject;
import com.jcraft.jsch.Session;
import io.jpom.common.JsonMessage;
import io.jpom.common.Type;
import io.jpom.common.interceptor.PermissionInterceptor;
import io.jpom.common.validator.ValidatorItem;
import io.jpom.common.validator.ValidatorRule;
@ -196,35 +193,6 @@ public class MachineSshController extends BaseGroupNameController {
return JsonMessage.success("操作成功");
}
/**
* 检查 ssh 是否安装插件端
*
* @param ids ids
* @return json
*/
@GetMapping(value = "check-agent", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<JSONObject> checkAgent(String ids) {
List<MachineSshModel> sshModels = machineSshServer.listById(StrUtil.split(ids, StrUtil.COMMA), false);
Assert.notEmpty(sshModels, "没有任何节点信息");
JSONObject result = new JSONObject();
for (MachineSshModel sshModel : sshModels) {
JSONObject data = new JSONObject();
try {
Integer pid = sshService.checkSshRunPid(sshModel, Type.Agent.getTag());
data.put("pid", ObjectUtil.defaultIfNull(pid, 0));
//
String javaVersion = sshService.checkCommand(sshModel, "java");
data.put("javaVersion", javaVersion);
} catch (Exception e) {
log.error("检查运行状态异常:{}", e.getMessage());
data.put("error", e.getMessage());
}
result.put(sshModel.getId(), data);
}
return JsonMessage.success("", result);
}
@PostMapping(value = "delete", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.DEL)

View File

@ -89,6 +89,67 @@ public class MachineSshModel extends BaseGroupNameModel {
private String statusMsg;
private String allowEditSuffix;
/**
* 系统名称
*/
private String osName;
/**
* 机器主机名
*/
private String hostName;
/**
* 系统版本
*/
private String osVersion;
/**
* 总内存
*/
private Long osMoneyTotal;
/**
* 硬盘总大小
*/
private Long osFileStoreTotal;
/**
* CPU 型号
*/
private String osCpuIdentifierName;
/**
* CPU数
*/
private Integer osCpuCores;
/**
* 占用cpu
*/
private Double osOccupyCpu;
/**
* 占用内存 总共
*/
private Double osOccupyMemory;
/**
* 占用磁盘
*/
private Double osMaxOccupyDisk;
/**
* 占用磁盘 分区名
*/
private String osMaxOccupyDiskName;
/**
* 负载
*/
private String osLoadAverage;
/**
* 系统运行时间自启动以来的时间
* 自启动以来的毫秒数
*/
private Long osSystemUptime;
/**
* jpom 查询进程号
*/
private Integer jpomAgentPid;
/**
* java 版本
*/
private String javaVersion;
public MachineSshModel(String id) {
setId(id);

View File

@ -212,12 +212,15 @@ public class MachineDockerServer extends BaseDbService<MachineDockerModel> imple
//
return true;
} catch (Exception e) {
String message = e.getMessage();
if (ExceptionUtil.isCausedBy(e, NoSuchFileException.class)) {
log.error("监控 docker[{}] 异常 {}", dockerInfoModel.getName(), e.getMessage());
log.error("监控 docker[{}] 异常 {}", dockerInfoModel.getName(), message);
} else if (StrUtil.containsIgnoreCase(message, "Connection timed out")) {
log.error("监控 docker[{}] 超时 {}", dockerInfoModel.getName(), message);
} else {
log.error("监控 docker[{}] 异常", dockerInfoModel.getName(), e);
}
this.updateStatus(dockerInfoModel.getId(), 0, e.getMessage());
this.updateStatus(dockerInfoModel.getId(), 0, message);
return false;
}
}

View File

@ -25,17 +25,28 @@ package io.jpom.func.assets.server;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.*;
import cn.hutool.cron.task.Task;
import cn.hutool.db.Entity;
import cn.hutool.extra.ssh.JschRuntimeException;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import io.jpom.JpomApplication;
import io.jpom.common.Const;
import io.jpom.common.ILoadEvent;
import io.jpom.common.ServerConst;
import io.jpom.common.Type;
import io.jpom.cron.CronUtils;
import io.jpom.cron.IAsyncLoad;
import io.jpom.func.assets.model.MachineSshModel;
import io.jpom.model.data.SshModel;
@ -43,7 +54,9 @@ import io.jpom.plugin.IWorkspaceEnvPlugin;
import io.jpom.plugin.PluginFactory;
import io.jpom.service.h2db.BaseDbService;
import io.jpom.service.node.ssh.SshService;
import io.jpom.system.ExtConfigBean;
import io.jpom.util.JschUtils;
import io.jpom.util.StringUtil;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
@ -52,11 +65,11 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;
/**
* @author bwcx_jzy
@ -64,12 +77,18 @@ import java.util.Optional;
*/
@Service
@Slf4j
public class MachineSshServer extends BaseDbService<MachineSshModel> implements ILoadEvent, IAsyncLoad, Runnable {
public class MachineSshServer extends BaseDbService<MachineSshModel> implements ILoadEvent, IAsyncLoad, Task {
private static final String CRON_ID = "ssh-monitor";
@Resource
@Lazy
private SshService sshService;
private final JpomApplication jpomApplication;
public MachineSshServer(JpomApplication jpomApplication) {
this.jpomApplication = jpomApplication;
}
@Override
protected void fillInsert(MachineSshModel machineSshModel) {
super.fillInsert(machineSshModel);
@ -156,12 +175,163 @@ public class MachineSshServer extends BaseDbService<MachineSshModel> implements
@Override
public void startLoad() {
CronUtils.add(CRON_ID, "0 0/1 * * * ?", () -> MachineSshServer.this);
}
@Override
public void run() {
public void execute() {
List<MachineSshModel> list = this.list(false);
if (CollUtil.isEmpty(list)) {
return;
}
this.checkList(list);
}
private void checkList(List<MachineSshModel> monitorModels) {
monitorModels.forEach(monitorModel -> ThreadUtil.execute(() -> this.updateMonitor(monitorModel)));
}
private void updateMonitor(MachineSshModel machineSshModel) {
File tempCommand;
try {
String tempId = IdUtil.fastSimpleUUID();
tempCommand = FileUtil.file(jpomApplication.getTempPath(), "ssh_temp", tempId + ".sh");
InputStream sshExecTemplateInputStream = ExtConfigBean.getConfigResourceInputStream("/ssh/monitor-script.sh");
String sshExecTemplate = IoUtil.readUtf8(sshExecTemplateInputStream);
Map<String, String> map = new HashMap<>(10);
map.put("JPOM_AGENT_PID_TAG", Type.Agent.getTag());
sshExecTemplate = StringUtil.formatStrByMap(sshExecTemplate, map);
Charset charset = machineSshModel.charset();
FileUtil.writeString(sshExecTemplate, tempCommand, charset);
//
Session session = this.getSessionByModelNoFill(machineSshModel);
try (Sftp sftp = new Sftp(session, charset, machineSshModel.timeout())) {
String path = StrUtil.format("{}/.jpom/", sftp.home());
String destFile = StrUtil.format("{}{}.sh", path, tempId);
sftp.mkDirs(path);
sftp.upload(destFile, tempCommand);
// 执行命令
try (ByteArrayOutputStream errStream = new ByteArrayOutputStream()) {
String commandSh = "bash " + destFile;
String exec = JschUtil.exec(session, commandSh, charset, errStream);
String error = new String(errStream.toByteArray(), charset);
if (StrUtil.isNotEmpty(error)) {
log.error("{} ssh 监控执行存在异常信息:{}", machineSshModel.getName(), error);
}
log.debug("{} ssh 监控信息结果:{} {}", machineSshModel.getName(), exec, error);
this.updateMonitorInfo(machineSshModel, exec, error);
}
try {
// 删除 ssh 中临时文件
sftp.delFile(destFile);
} catch (Exception e) {
log.warn("删除 ssh 临时文件失败", e);
}
}
} catch (Exception e) {
String message = e.getMessage();
if (StrUtil.containsIgnoreCase(message, "timeout")) {
log.error("监控 ssh[{}] 超时 {}", machineSshModel.getName(), message);
} else {
log.error("监控 ssh[{}] 异常", machineSshModel.getName(), e);
}
this.updateStatus(machineSshModel.getId(), 0, message);
}
}
private void updateMonitorInfo(MachineSshModel machineSshModel, String result, String error) {
error = StrUtil.emptyToDefault(error, StrUtil.EMPTY);
if (StrUtil.isEmpty(result)) {
this.updateStatus(machineSshModel.getId(), 1, "执行结果为空," + error);
return;
}
List<String> listStr = StrUtil.splitTrim(result, StrUtil.LF);
Map<String, List<String>> map = new CaseInsensitiveMap<>(listStr.size());
for (String strItem : listStr) {
String key = StrUtil.subBefore(strItem, StrUtil.COLON, false);
List<String> list = map.computeIfAbsent(key, s2 -> new ArrayList<>());
list.add(StrUtil.subAfter(strItem, StrUtil.COLON, false));
}
MachineSshModel update = new MachineSshModel();
update.setId(machineSshModel.getId());
update.setStatus(1);
update.setOsName(this.getFirstValue(map, "os name"));
update.setOsVersion(this.getFirstValue(map, "os version"));
update.setOsLoadAverage(CollUtil.join(map.get("load average"), StrUtil.COMMA));
String uptime = this.getFirstValue(map, "uptime");
if (StrUtil.isNotEmpty(uptime)) {
try {
// 可能有时区问题
DateTime dateTime = DateUtil.parse(uptime);
update.setOsSystemUptime((SystemClock.now() - dateTime.getTime()));
} catch (Exception e) {
error = error + " 解析系统启动时间错误:" + e.getMessage();
update.setOsSystemUptime(0L);
}
}
update.setOsCpuCores(Convert.toInt(this.getFirstValue(map, "cpu core"), 0));
update.setHostName(this.getFirstValue(map, "hostname"));
update.setOsCpuIdentifierName(this.getFirstValue(map, "model name"));
// kb
Long memoryTotal = Convert.toLong(this.getFirstValue(map, "memory total"), 0L);
Long memoryAvailable = Convert.toLong(this.getFirstValue(map, "memory available"), 0L);
update.setOsMoneyTotal(memoryTotal * 1024);
update.setStatusMsg("执行成功,错误信息:" + error);
update.setOsOccupyCpu(Convert.toDouble(this.getFirstValue(map, "cpu usage"), -0D));
if (memoryTotal > 0) {
update.setOsOccupyMemory(NumberUtil.div(memoryAvailable, memoryTotal, 2).doubleValue());
} else {
update.setOsOccupyMemory(-0D);
}
List<String> list = map.get("disk info");
update.setOsMaxOccupyDisk(-0D);
update.setOsMaxOccupyDiskName(StrUtil.EMPTY);
if (CollUtil.isNotEmpty(list)) {
long total = 0;
for (String s : list) {
List<String> trim = StrUtil.splitTrim(s, StrUtil.COLON);
long total1 = Convert.toLong(CollUtil.get(trim, 1), 0L);
total += total1;
long available1 = Convert.toLong(CollUtil.get(trim, 2), 0L);
// 计算最大的硬盘占用
if (total1 > 0) {
Double osMaxOccupyDisk = update.getOsMaxOccupyDisk();
osMaxOccupyDisk = ObjectUtil.defaultIfNull(osMaxOccupyDisk, 0D);
double occupyDisk = NumberUtil.div(available1, total1, 2);
if (occupyDisk > osMaxOccupyDisk) {
update.setOsMaxOccupyDisk(occupyDisk);
update.setOsMaxOccupyDiskName(CollUtil.getFirst(trim));
}
}
}
update.setOsFileStoreTotal(total * 1024);
}
update.setJavaVersion(this.getFirstValue(map, "java version"));
update.setJpomAgentPid(Convert.toInt(this.getFirstValue(map, "jpom agent pid")));
this.updateById(update);
}
private String getFirstValue(Map<String, List<String>> map, String name) {
List<String> list = map.get(name);
String first = CollUtil.getFirst(list);
// 内存获取可能最后存在
return StrUtil.removeSuffix(first, StrUtil.COLON);
}
/**
* 更新 容器状态
*
* @param id ID
* @param status 状态值
* @param msg 错误消息
*/
private void updateStatus(String id, int status, String msg) {
MachineSshModel machineSshModel = new MachineSshModel();
machineSshModel.setId(id);
machineSshModel.setStatus(status);
machineSshModel.setStatusMsg(msg);
super.update(machineSshModel);
}
/**

View File

@ -263,6 +263,8 @@ public class CommandService extends BaseWorkspaceService<CommandModel> implement
}
//
EnvironmentMapBuilder environmentMapBuilder = workspaceEnvVarService.getEnv(commandModel.getWorkspaceId());
environmentMapBuilder.put("JPOM_SSH_ID", sshModel.getId());
environmentMapBuilder.put("JPOM_COMMAND_ID", commandModel.getId());
Map<String, String> environment = environmentMapBuilder.environment();
String[] commands = StrUtil.splitToArray(commandModel.getCommand(), StrUtil.LF);
for (int i = 0; i < commands.length; i++) {

View File

@ -22,8 +22,6 @@
*/
package io.jpom.service.node.ssh;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ArrayUtil;
@ -50,8 +48,6 @@ import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
/**
@ -125,38 +121,6 @@ public class SshService extends BaseGroupService<SshModel> implements Logger {
return machineSshServer.getByKey(sshModel.getMachineSshId(), false);
}
/**
* 获取 ssh 中的 环境 版本
*
* @param sshModel ssh
* @return 返回
* @throws IOException IO
*/
public String checkCommand(MachineSshModel sshModel, String command) throws IOException {
// 检查 环境
return this.exec(sshModel, "command -v " + command);
// return CollUtil.join(commandResult, StrUtil.COMMA);
}
/**
* 检查是否存在正在运行的进程
*
* @param sshModel ssh
* @param tag 标识
* @return true 存在运行中的
* @throws IOException IO
*/
public Integer checkSshRunPid(MachineSshModel sshModel, String tag) throws IOException {
String ps = StrUtil.format("ps -ef | grep -v 'grep' | egrep {}", tag);
// 运行中
String exec = this.exec(sshModel, ps);
List<String> result = StrUtil.splitTrim(exec, StrUtil.LF);
return result.stream().map(s -> {
List<String> split = StrUtil.splitTrim(s, StrUtil.SPACE);
return Convert.toInt(CollUtil.get(split, 1));
}).filter(Objects::nonNull).findAny().orElse(null);
}
/**
* ssh 执行模版命令
*
@ -238,7 +202,8 @@ public class SshService extends BaseGroupService<SshModel> implements Logger {
try {
// 删除 ssh 中临时文件
sftp.delFile(destFile);
} catch (Exception ignored) {
} catch (Exception e) {
log.warn("删除 ssh 临时文件失败", e);
}
// 删除临时文件
FileUtil.del(buildSsh);

View File

@ -1,3 +1,26 @@
#
# The MIT License (MIT)
#
# Copyright (c) 2019 Code Technology Studio
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
user="$(id -un 2>/dev/null || true)"
if [ "$user" == 'root' ]; then
@ -18,10 +41,50 @@ for element in "${userProfiles[@]}"; do
fi
done
free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}'
function getPid() {
cygwin=false
linux=false
case "$(uname)" in
CYGWIN*)
cygwin=true
;;
Linux*)
linux=true
;;
esac
if $cygwin; then
JAVA_CMD="$JAVA_HOME\bin\java"
JAVA_CMD=$(cygpath --path --unix "$JAVA_CMD")
JAVA_PID=$(ps | grep "$JAVA_CMD" | awk '{print $1}')
else
if $linux; then
JAVA_PID=$(ps -C java -f --width 1000 | grep "$1" | grep -v grep | awk '{print $2}')
else
JAVA_PID=$(ps aux | grep "$1" | grep -v grep | awk '{print $2}')
fi
fi
echo "$JAVA_PID"
}
LANG=C lscpu | awk -F: '/Model name/ {print $2}'
awk '/processor/{core++} END{print core}' /proc/cpuinfo
uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs
vmstat 1 1 | awk 'NR==3{print $11}'
vmstat 1 1 | awk 'NR==3{print $12}'
echo "os name:$(/usr/bin/lsb_release -a | grep Description | awk -F : '{print $2}' | sed 's/^[ \t]*//g')"
echo "os version:$(uname -r)"
echo "hostname:$(hostname)"
# 启动时间
echo "uptime:$(uptime -s)"
# 系统负载
uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++) print "load average:"$i }'
# cpu 型号
LANG=C lscpu | awk -F: '/Model name/ {print$2}' | awk '$1=$1' | awk '{print "model name:"$0}'
# cpu 数量
awk '/processor/{core++} END{print "cpu core:"core}' /proc/cpuinfo
# 内存信息
free -k | grep -iv swap | awk -F ':' '{print $NF}' | awk -F ' ' '{for(i=1;i<=NF;i++) a[i,NR]=$i}END{for(i=1;i<=NF;i++) { printf "memory "; for(j=1;j<=NR;j++) printf a[i,j] ":";print ""}}'
# cpu 利用率
export TERM=xterm
top -b -n 1 -d.2 | grep -i 'cpu' | awk '{for(i=NR;i<=1;i++)print "cpu usage:"$2 }'
# disk
df -k | awk 'NR>1' | awk '/^\/dev/{print "disk info:"$1":"$2":"$3}'
# jpom 相关
jpom_agent_pid=$(getPid "${JPOM_AGENT_PID_TAG}")
echo "jpom agent pid:$jpom_agent_pid"
echo "java version:$(command -v java)"

View File

@ -17,4 +17,18 @@ ADD,USEROPERATELOGV1,workspaceName,String,50,,工作空间名
ADD,USEROPERATELOGV1,username,String,50,,用户名
ADD,MACHINE_NODE_INFO,transportEncryption,TINYINT,,,传输加密方式 0 不加密 1 BASE64 2 AES
DROP,COMMAND_INFO,type,
ADD,MACHINE_SSH_INFO,osName,String,50,,系统名称,false
ADD,MACHINE_SSH_INFO,hostName,String,255,,机器主机名,false
ADD,MACHINE_SSH_INFO,osLoadAverage,String,100,,系统负载,false
ADD,MACHINE_SSH_INFO,osSystemUptime,Long,,,系统运行时间(自启动以来的时间),false
ADD,MACHINE_SSH_INFO,osVersion,String,255,,系统版本,false
ADD,MACHINE_SSH_INFO,osCpuCores,Integer,,,CPU数,false
ADD,MACHINE_SSH_INFO,osMoneyTotal,Long,,,总内存,false
ADD,MACHINE_SSH_INFO,osFileStoreTotal,Long,,,硬盘大小,false
ADD,MACHINE_SSH_INFO,osCpuIdentifierName,String,255,,CPU 型号,false
ADD,MACHINE_SSH_INFO,osOccupyCpu,Double,,,占用cpu,false
ADD,MACHINE_SSH_INFO,osOccupyMemory,Double,,,占用内存,false
ADD,MACHINE_SSH_INFO,osMaxOccupyDisk,Double,,,占用磁盘,false
ADD,MACHINE_SSH_INFO,osMaxOccupyDiskName,String,255,,占用磁盘 分区名,false
ADD,MACHINE_SSH_INFO,javaVersion,String,255,,java版本,false
ADD,MACHINE_SSH_INFO,jpomAgentPid,Integer,,,jpom agent进程号

1 alterType,tableName,name,type,len,defaultValue,comment,notNull
17 ADD,USEROPERATELOGV1,username,String,50,,用户名
18 ADD,MACHINE_NODE_INFO,transportEncryption,TINYINT,,,传输加密方式 0 不加密 1 BASE64 2 AES
19 DROP,COMMAND_INFO,type,
20 ADD,MACHINE_SSH_INFO,osName,String,50,,系统名称,false
21 ADD,MACHINE_SSH_INFO,hostName,String,255,,机器主机名,false
22 ADD,MACHINE_SSH_INFO,osLoadAverage,String,100,,系统负载,false
23 ADD,MACHINE_SSH_INFO,osSystemUptime,Long,,,系统运行时间(自启动以来的时间),false
24 ADD,MACHINE_SSH_INFO,osVersion,String,255,,系统版本,false
25 ADD,MACHINE_SSH_INFO,osCpuCores,Integer,,,CPU数,false
26 ADD,MACHINE_SSH_INFO,osMoneyTotal,Long,,,总内存,false
27 ADD,MACHINE_SSH_INFO,osFileStoreTotal,Long,,,硬盘大小,false
28 ADD,MACHINE_SSH_INFO,osCpuIdentifierName,String,255,,CPU 型号,false
29 ADD,MACHINE_SSH_INFO,osOccupyCpu,Double,,,占用cpu,false
30 ADD,MACHINE_SSH_INFO,osOccupyMemory,Double,,,占用内存,false
31 ADD,MACHINE_SSH_INFO,osMaxOccupyDisk,Double,,,占用磁盘,false
32 ADD,MACHINE_SSH_INFO,osMaxOccupyDiskName,String,255,,占用磁盘 分区名,false
33 ADD,MACHINE_SSH_INFO,javaVersion,String,255,,java版本,false
34 ADD,MACHINE_SSH_INFO,jpomAgentPid,Integer,,,jpom agent进程号

View File

@ -26,19 +26,6 @@ export function machineSshEdit(params) {
});
}
// 检查 agent
export function machineSshCheckAgent(params) {
return axios({
url: "/system/assets/ssh/check-agent",
method: "get",
params: params,
timeout: 0,
headers: {
loading: "no",
},
});
}
// 删除 ssh
export function machineSshDelete(params) {
return axios({

View File

@ -28,6 +28,39 @@
</a-space>
</template>
<a-tooltip slot="tooltip" slot-scope="text" :title="text"> {{ text }}</a-tooltip>
<a-tooltip slot="host" slot-scope="text, record" :title="`${record.machineSsh && record.machineSsh.host}:${record.machineSsh && record.machineSsh.port}`">
{{ record.machineSsh && record.machineSsh.host }}:{{ record.machineSsh && record.machineSsh.port }}
</a-tooltip>
<template slot="status" slot-scope="text, record">
<a-tooltip :title="record.machineSsh && record.machineSsh.statusMsg">
<a-tag :color="record.machineSsh && record.machineSsh.status === 1 ? 'green' : 'red'">{{ record.machineSsh && record.machineSsh.status === 1 ? "正常" : "无法连接" }}</a-tag>
</a-tooltip>
</template>
<a-popover title="系统信息" slot="osName" slot-scope="text, record">
<template slot="content">
<p>系统名{{ record.machineSsh && record.machineSsh.osName }}</p>
<p>系统版本{{ record.machineSsh && record.machineSsh.osVersion }}</p>
<p>CPU型号{{ record.machineSsh && record.machineSsh.osCpuIdentifierName }}</p>
<p>开机时间{{ formatDuration(record.machineSsh && record.machineSsh.osSystemUptime) }}</p>
</template>
{{ text || "未知" }}
</a-popover>
<a-tooltip
slot="osOccupyMemory"
slot-scope="text, record"
placement="topLeft"
:title="`内存使用率:${record.machineSsh && record.machineSsh.osOccupyMemory},总内存:${renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)}`"
>
<span>{{ formatPercent2Number(record.machineSsh && record.machineSsh.osOccupyMemory) + "%" }}/{{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</span>
</a-tooltip>
<a-tooltip
slot="osMaxOccupyDisk"
slot-scope="text, record"
placement="topLeft"
:title="`最大的硬盘使用率:${record.machineSsh && record.machineSsh.osMaxOccupyDisk},硬盘总量:${renderSize(record.machineSsh && record.machineSsh.osMoneyTotal)}`"
>
<span>{{ formatPercent2Number(record.machineSsh && record.machineSsh.osMaxOccupyDisk) + "%" }} / {{ renderSize(record.machineSsh && record.machineSsh.osMoneyTotal) }}</span>
</a-tooltip>
<template slot="nodeId" slot-scope="text, record">
<template v-if="record.linkNode">
<a-tooltip placement="topLeft" :title="`节点名称:${record.linkNode.name}`">
@ -156,7 +189,7 @@
import { deleteSsh, editSsh, getSshList, syncToWorkspace, getSshGroupAll } from "@/api/ssh";
import SshFile from "@/pages/ssh/ssh-file";
import Terminal from "@/pages/ssh/terminal";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime, formatPercent2Number, renderSize, formatDuration } from "@/utils/const";
import { getWorkSpaceListAll } from "@/api/workspace";
import { mapGetters } from "vuex";
@ -195,15 +228,23 @@ export default {
columns: [
{ title: "名称", dataIndex: "name", sorter: true, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "Host", dataIndex: "machineSsh.host", sorter: true, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "Port", dataIndex: "machineSsh.port", sorter: true, width: 80, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "用户名", dataIndex: "machineSsh.user", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "编码格式", dataIndex: "machineSsh.charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "Host", dataIndex: "machineSsh.host", sorter: true, ellipsis: true, scopedSlots: { customRender: "host" } },
// { title: "Port", dataIndex: "machineSsh.port", sorter: true, width: 80, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "用户名", dataIndex: "machineSsh.user", sorter: true, width: "100px", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "系统名", dataIndex: "machineSsh.osName", sorter: true, ellipsis: true, scopedSlots: { customRender: "osName" } },
// { title: "", dataIndex: "machineSsh.osVersion", sorter: true, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "CPU数", dataIndex: "machineSsh.osCpuCores", sorter: true, width: "80px", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "内存", dataIndex: "machineSsh.osOccupyMemory", sorter: true, ellipsis: true, scopedSlots: { customRender: "osOccupyMemory" } },
{ title: "硬盘", dataIndex: "machineSsh.osMaxOccupyDisk", sorter: true, ellipsis: true, scopedSlots: { customRender: "osMaxOccupyDisk" } },
// { title: "", dataIndex: "charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "连接状态", dataIndex: "machineSsh.status", ellipsis: true, align: "center", width: "100px", scopedSlots: { customRender: "status" } },
// { title: "", dataIndex: "machineSsh.charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{
title: "关联节点",
dataIndex: "nodeId",
scopedSlots: { customRender: "nodeId" },
width: 120,
width: "120px",
ellipsis: true,
},
{
@ -254,6 +295,9 @@ export default {
this.loadGroupList();
},
methods: {
formatPercent2Number,
renderSize,
formatDuration,
//
loadData(pointerEvent) {
this.loading = true;

View File

@ -34,28 +34,41 @@
<a-button style="padding: 0" type="link" size="small" @click="handleEdit(item)"> {{ text }}</a-button>
</a-tooltip>
<a-tooltip slot="tooltip" slot-scope="text" :title="text"> {{ text }}</a-tooltip>
<template slot="nodeId" slot-scope="text, record">
<template v-if="sshAgentInfo[record.id]">
<a-tooltip v-if="sshAgentInfo[record.id].error" :title="sshAgentInfo[record.id].error">
<a-tag>连接异常</a-tag>
</a-tooltip>
<template v-else>
<div v-if="sshAgentInfo[record.id].javaVersion">
<a-tooltip
v-if="sshAgentInfo[record.id].pid > 0"
placement="topLeft"
:title="` ssh 中已经运行了插件端进程ID${sshAgentInfo[record.id].pid},java : ${sshAgentInfo[record.id].javaVersion}`"
>
<a-tag> {{ sshAgentInfo[record.id].pid }}</a-tag>
</a-tooltip>
<a-button v-else size="small" type="primary" @click="install(record)">安装节点</a-button>
</div>
<a-tooltip slot="host" slot-scope="text, record" :title="text"> {{ text }}:{{ record.port }}</a-tooltip>
<a-tag v-else>没有Java环境</a-tag>
</template>
<a-popover title="系统信息" slot="osName" slot-scope="text, record">
<template slot="content">
<p>系统名{{ record.osName }}</p>
<p>系统版本{{ record.osVersion }}</p>
<p>CPU型号{{ record.osCpuIdentifierName }}</p>
<p>开机时间{{ formatDuration(record.osSystemUptime) }}</p>
</template>
<div v-else>- {{ sshAgentInfo[record.id] }}</div>
{{ text || "未知" }}
</a-popover>
<template slot="nodeId" slot-scope="text, record">
<div v-if="record.javaVersion">
<a-tooltip v-if="record.jpomAgentPid > 0" placement="topLeft" :title="` ssh 中已经运行了插件端进程ID${record.jpomAgentPid},java : ${record.javaVersion}`">
<a-tag> {{ record.jpomAgentPid }}</a-tag>
</a-tooltip>
<a-button v-else size="small" type="primary" @click="install(record)">安装节点</a-button>
</div>
<a-tag color="orange" v-else>no java</a-tag>
</template>
<template slot="status" slot-scope="text, record">
<a-tooltip :title="record.statusMsg">
<a-tag :color="record.status === 1 ? 'green' : 'red'">{{ record.status === 1 ? "正常" : "无法连接" }}</a-tag>
</a-tooltip>
</template>
<a-tooltip slot="renderSize" slot-scope="text" placement="topLeft" :title="renderSize(text)">
<span>{{ renderSize(text) }}</span>
</a-tooltip>
<a-tooltip slot="osOccupyMemory" slot-scope="text, record" placement="topLeft" :title="`内存使用率:${record.osOccupyMemory},总内存:${renderSize(record.osMoneyTotal)}`">
<span>{{ formatPercent2Number(record.osOccupyMemory) + "%" }}/{{ renderSize(record.osMoneyTotal) }}</span>
</a-tooltip>
<a-tooltip slot="osMaxOccupyDisk" slot-scope="text, record" placement="topLeft" :title="`最大的硬盘使用率:${record.osMaxOccupyDisk},硬盘总量:${renderSize(record.osMoneyTotal)}`">
<span>{{ formatPercent2Number(record.osMaxOccupyDisk) + "%" }} / {{ renderSize(record.osMoneyTotal) }}</span>
</a-tooltip>
<template slot="operation" slot-scope="text, record">
<a-space>
<a-dropdown>
@ -309,14 +322,13 @@ import {
machineSshListData,
machineSshListGroup,
machineSshEdit,
machineSshCheckAgent,
machineSshDelete,
machineListGroupWorkspaceSsh,
machineSshSaveWorkspaceConfig,
machineSshDistribute,
restHideField,
} from "@/api/system/assets-ssh";
import { COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime, CHANGE_PAGE } from "@/utils/const";
import { COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime, CHANGE_PAGE, renderSize, formatPercent2Number, formatDuration } from "@/utils/const";
import fastInstall from "@/pages/node/fast-install.vue";
import CustomSelect from "@/components/customSelect";
import SshFile from "@/pages/ssh/ssh-file";
@ -346,16 +358,21 @@ export default {
],
columns: [
{ title: "名称", dataIndex: "name", sorter: true, ellipsis: true, scopedSlots: { customRender: "name" } },
{ title: "系统名", dataIndex: "osName", sorter: true, ellipsis: true, scopedSlots: { customRender: "osName" } },
{ title: "Host", dataIndex: "host", sorter: true, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "Port", dataIndex: "port", sorter: true, width: 80, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "用户名", dataIndex: "user", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "编码格式", dataIndex: "charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "CPU数", dataIndex: "osCpuCores", sorter: true, width: "80px", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "Host", dataIndex: "host", sorter: true, ellipsis: true, scopedSlots: { customRender: "host" } },
// { title: "Port", dataIndex: "port", sorter: true, width: 80, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "用户名", dataIndex: "user", sorter: true, width: "80px", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "内存", dataIndex: "osOccupyMemory", sorter: true, ellipsis: true, scopedSlots: { customRender: "osOccupyMemory" } },
{ title: "硬盘", dataIndex: "osMaxOccupyDisk", sorter: true, ellipsis: true, scopedSlots: { customRender: "osMaxOccupyDisk" } },
// { title: "", dataIndex: "charset", sorter: true, width: 120, ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "连接状态", dataIndex: "status", ellipsis: true, align: "center", width: "100px", scopedSlots: { customRender: "status" } },
{
title: "节点状态",
dataIndex: "nodeId",
scopedSlots: { customRender: "nodeId" },
width: "120px",
width: "80px",
ellipsis: true,
},
{
@ -375,6 +392,7 @@ export default {
width: "300px",
align: "center",
// ellipsis: true,
fixed: "right",
},
],
//
@ -393,7 +411,7 @@ export default {
password: [{ required: true, message: "请输入登录密码", trigger: "blur" }],
},
nodeVisible: false,
sshAgentInfo: {},
terminalVisible: false,
terminalFullscreen: false,
viewOperationLog: false,
@ -410,6 +428,9 @@ export default {
this.loadGroupList();
},
methods: {
formatDuration,
renderSize,
formatPercent2Number,
//
loadData(pointerEvent) {
this.loading = true;
@ -419,19 +440,6 @@ export default {
this.list = res.data.result;
this.listQuery.total = res.data.total;
//
let ids = this.list
.map((item) => {
return item.id;
})
.join(",");
if (ids.length > 0) {
machineSshCheckAgent({
ids: ids,
}).then((res) => {
this.sshAgentInfo = { ...res.data };
});
}
}
this.loading = false;
});