mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-02 11:58:01 +08:00
feat ssh 基础信息监控(非报警监控)
This commit is contained in:
parent
1e59ccabd6
commit
3b403b9dd6
@ -2,6 +2,10 @@
|
||||
|
||||
## 2.10.33
|
||||
|
||||
### 🐣 新增功能
|
||||
|
||||
1. 【server】新增 ssh 基础信息监控(非报警监控)
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
||||
1. 【server】修复 插件端在线升级页面无法正常使用(调用到服务端在线升级接口)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
|
@ -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)"
|
||||
|
@ -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进程号
|
||||
|
|
@ -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({
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user