✨ ansi 执行日志.
This commit is contained in:
@@ -25,13 +25,25 @@ public class ExecCommandDTO {
|
||||
@Schema(description = "hostId")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "执行描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "执行序列")
|
||||
private Integer execSeq;
|
||||
|
||||
@Schema(description = "超时时间")
|
||||
private Integer timeout;
|
||||
|
||||
@Schema(description = "是否使用脚本执行")
|
||||
private Boolean scriptExec;
|
||||
|
||||
@Schema(description = "主机")
|
||||
@Schema(description = "执行主机")
|
||||
private List<ExecCommandHostDTO> hosts;
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ public class ExecCommandHostDTO {
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "主机名称")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "日志文件路径")
|
||||
private String logPath;
|
||||
|
||||
@@ -35,6 +41,9 @@ public class ExecCommandHostDTO {
|
||||
@Schema(description = "执行命令")
|
||||
private String command;
|
||||
|
||||
@Schema(description = "主机用户")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "命令编码")
|
||||
private String charset;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.orion.lang.support.timeout.TimeoutEndpoint;
|
||||
import com.orion.lang.utils.Booleans;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.ansi.AnsiAppender;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.net.host.sftp.SftpExecutor;
|
||||
@@ -32,14 +33,14 @@ import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 命令执行器
|
||||
* 命令执行器 基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/12 11:30
|
||||
* @since 2024/4/25 18:35
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExecCommandHandler implements IExecCommandHandler {
|
||||
public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
||||
|
||||
private final FileClient fileClient = SpringHolder.getBean("logsFileClient");
|
||||
|
||||
@@ -49,32 +50,35 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
|
||||
private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
|
||||
|
||||
private final ExecCommandDTO execCommand;
|
||||
protected final ExecCommandDTO execCommand;
|
||||
|
||||
private final ExecCommandHostDTO execHostCommand;
|
||||
protected final ExecCommandHostDTO execHostCommand;
|
||||
|
||||
private final TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
||||
|
||||
@Getter
|
||||
private ExecHostStatusEnum status;
|
||||
protected ExecHostStatusEnum status;
|
||||
|
||||
protected ExecHostLogDO updateRecord;
|
||||
|
||||
private OutputStream logOutputStream;
|
||||
|
||||
private SessionStore sessionStore;
|
||||
|
||||
private CommandExecutor executor;
|
||||
|
||||
private OutputStream logOutputStream;
|
||||
|
||||
private volatile boolean closed;
|
||||
|
||||
private volatile boolean interrupted;
|
||||
|
||||
public ExecCommandHandler(ExecCommandDTO execCommand,
|
||||
ExecCommandHostDTO execHostCommand,
|
||||
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||
public BaseExecCommandHandler(ExecCommandDTO execCommand,
|
||||
ExecCommandHostDTO execHostCommand,
|
||||
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||
this.status = ExecHostStatusEnum.WAITING;
|
||||
this.execCommand = execCommand;
|
||||
this.execHostCommand = execHostCommand;
|
||||
this.timeoutChecker = timeoutChecker;
|
||||
this.updateRecord = new ExecHostLogDO();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,23 +95,25 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
} catch (Exception e) {
|
||||
log.error("ExecCommandHandler run error id: {}", id, e);
|
||||
ex = e;
|
||||
}
|
||||
// 执行完成回调
|
||||
try {
|
||||
// 回调
|
||||
this.onFinishCallback(ex);
|
||||
} finally {
|
||||
// 释放资源
|
||||
Streams.close(this);
|
||||
}
|
||||
// 执行回调
|
||||
if (this.interrupted) {
|
||||
// 中断执行
|
||||
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
|
||||
} else if (ex != null) {
|
||||
// 执行失败
|
||||
this.updateStatus(ExecHostStatusEnum.FAILED, ex);
|
||||
} else if (executor.isTimeout()) {
|
||||
// 更新执行超时
|
||||
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
|
||||
} else {
|
||||
// 更新执行完成
|
||||
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化日志输出流
|
||||
*
|
||||
* @throws Exception Exception
|
||||
*/
|
||||
protected void initLogOutputStream() throws Exception {
|
||||
// 打开日志流
|
||||
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,9 +121,9 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void execCommand() throws Exception {
|
||||
// 打开日志流
|
||||
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
|
||||
protected void execCommand() throws Exception {
|
||||
// 初始化日志
|
||||
this.initLogOutputStream();
|
||||
// 打开会话
|
||||
this.sessionStore = hostTerminalService.openSessionStore(execHostCommand.getHostId());
|
||||
if (Booleans.isTrue(execCommand.getScriptExec())) {
|
||||
@@ -141,7 +147,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
/**
|
||||
* 上传脚本文件
|
||||
*/
|
||||
private void uploadScriptFile() {
|
||||
protected void uploadScriptFile() {
|
||||
SftpExecutor sftpExecutor = null;
|
||||
try {
|
||||
// 打开 sftp
|
||||
@@ -166,6 +172,42 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行完成回调
|
||||
*
|
||||
* @param e e
|
||||
*/
|
||||
protected void onFinishCallback(Exception e) {
|
||||
// 执行回调
|
||||
if (this.interrupted) {
|
||||
// 中断执行
|
||||
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
|
||||
} else if (e != null) {
|
||||
// 执行失败
|
||||
this.updateStatus(ExecHostStatusEnum.FAILED, e);
|
||||
} else if (executor.isTimeout()) {
|
||||
// 更新执行超时
|
||||
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
|
||||
} else {
|
||||
// 更新执行完成
|
||||
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接日志
|
||||
*
|
||||
* @param appender appender
|
||||
*/
|
||||
protected void appendLog(AnsiAppender appender) {
|
||||
try {
|
||||
logOutputStream.write(Strings.bytes(appender.toString()));
|
||||
logOutputStream.flush();
|
||||
} catch (Exception e) {
|
||||
log.error("BaseExecCommandHandler.appendLog error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
*
|
||||
@@ -176,30 +218,29 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
this.status = status;
|
||||
Long id = execHostCommand.getHostLogId();
|
||||
String statusName = status.name();
|
||||
log.info("ExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
|
||||
ExecHostLogDO update = new ExecHostLogDO();
|
||||
update.setId(id);
|
||||
update.setStatus(statusName);
|
||||
log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
|
||||
updateRecord.setId(id);
|
||||
updateRecord.setStatus(statusName);
|
||||
if (ExecHostStatusEnum.RUNNING.equals(status)) {
|
||||
// 运行中
|
||||
update.setStartTime(new Date());
|
||||
updateRecord.setStartTime(new Date());
|
||||
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
|
||||
// 完成
|
||||
update.setFinishTime(new Date());
|
||||
update.setExitStatus(executor.getExitCode());
|
||||
updateRecord.setFinishTime(new Date());
|
||||
updateRecord.setExitStatus(executor.getExitCode());
|
||||
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
|
||||
// 失败
|
||||
update.setFinishTime(new Date());
|
||||
update.setErrorMessage(this.getErrorMessage(ex));
|
||||
updateRecord.setFinishTime(new Date());
|
||||
updateRecord.setErrorMessage(this.getErrorMessage(ex));
|
||||
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
|
||||
// 超时
|
||||
update.setFinishTime(new Date());
|
||||
updateRecord.setFinishTime(new Date());
|
||||
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
|
||||
// 中断
|
||||
update.setFinishTime(new Date());
|
||||
updateRecord.setFinishTime(new Date());
|
||||
}
|
||||
int effect = execHostLogDAO.updateById(update);
|
||||
log.info("ExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
|
||||
int effect = execHostLogDAO.updateById(updateRecord);
|
||||
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,20 +249,21 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupted() {
|
||||
log.info("ExecCommandHandler.interrupted id: {}, interrupted: {}, closed: {}",
|
||||
public void interrupt() {
|
||||
log.info("BaseExecCommandHandler.interrupt id: {}, interrupted: {}, closed: {}",
|
||||
execHostCommand.getHostLogId(), interrupted, closed);
|
||||
if (this.interrupted || this.closed) {
|
||||
return;
|
||||
}
|
||||
// 关闭
|
||||
this.interrupted = true;
|
||||
this.close();
|
||||
Streams.close(executor);
|
||||
Streams.close(sessionStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
log.info("ExecCommandHandler.closed id: {}, closed: {}",
|
||||
log.info("BaseExecCommandHandler.closed id: {}, closed: {}",
|
||||
execHostCommand.getHostLogId(), closed);
|
||||
if (this.closed) {
|
||||
return;
|
||||
@@ -239,7 +281,7 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
* @param ex ex
|
||||
* @return errorMessage
|
||||
*/
|
||||
private String getErrorMessage(Exception ex) {
|
||||
protected String getErrorMessage(Exception ex) {
|
||||
String message;
|
||||
if (ex instanceof InvalidArgumentException) {
|
||||
message = ex.getMessage();
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.command.handler;
|
||||
|
||||
import com.orion.lang.support.timeout.TimeoutChecker;
|
||||
import com.orion.lang.support.timeout.TimeoutEndpoint;
|
||||
import com.orion.lang.utils.Booleans;
|
||||
import com.orion.lang.utils.ansi.AnsiAppender;
|
||||
import com.orion.lang.utils.ansi.style.AnsiFont;
|
||||
import com.orion.lang.utils.ansi.style.color.AnsiForeground;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.net.host.ssh.ExitCode;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
|
||||
|
||||
/**
|
||||
* 命令执行器 ansi 日志输出
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/4/25 18:19
|
||||
*/
|
||||
public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
|
||||
|
||||
public ExecCommandAnsiHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||
super(execCommand, execHostCommand, timeoutChecker);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initLogOutputStream() throws Exception {
|
||||
super.initLogOutputStream();
|
||||
// 拼接启动日志
|
||||
AnsiAppender appender = AnsiAppender.create()
|
||||
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备执行命令 ")
|
||||
.append(Dates.current())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行记录: ")
|
||||
.append(execCommand.getLogId())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行描述: ")
|
||||
.append(execCommand.getDescription())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行用户: ")
|
||||
.append(execCommand.getUsername());
|
||||
// 非系统用户执行添加 userId
|
||||
if (Const.SYSTEM_USER_ID.equals(execCommand.getUserId())) {
|
||||
appender.newLine();
|
||||
} else {
|
||||
appender.append(" (")
|
||||
.append(execCommand.getUserId())
|
||||
.append(")")
|
||||
.newLine();
|
||||
}
|
||||
// 执行序列
|
||||
if (execCommand.getExecSeq() != null) {
|
||||
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行序列: ")
|
||||
.append('#')
|
||||
.append(execCommand.getExecSeq())
|
||||
.newLine();
|
||||
}
|
||||
appender.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "执行主机: ")
|
||||
.append(execHostCommand.getHostName())
|
||||
.append(" (")
|
||||
.append(execHostCommand.getHostId())
|
||||
.append(")")
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "主机地址: ")
|
||||
.append(execHostCommand.getHostAddress())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "超时时间: ")
|
||||
.append(execCommand.getTimeout())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "脚本执行: ")
|
||||
.append(execCommand.getScriptExec())
|
||||
.newLine()
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 执行命令 ")
|
||||
.newLine()
|
||||
.append(execHostCommand.getCommand())
|
||||
.newLine()
|
||||
.newLine();
|
||||
// 非脚本执行拼接开始执行日志
|
||||
if (!Booleans.isTrue(execCommand.getScriptExec())) {
|
||||
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行命令 ")
|
||||
.append(Dates.current())
|
||||
.newLine();
|
||||
}
|
||||
this.appendLog(appender);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uploadScriptFile() {
|
||||
try {
|
||||
// 拼接上传日志
|
||||
AnsiAppender startAppender = AnsiAppender.create()
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 准备上传脚本 ")
|
||||
.append(Dates.current())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "文件路径: ")
|
||||
.append(execHostCommand.getScriptPath())
|
||||
.newLine();
|
||||
this.appendLog(startAppender);
|
||||
// 上传脚本文件
|
||||
super.uploadScriptFile();
|
||||
// 拼接完成日志
|
||||
AnsiAppender finishAppender = AnsiAppender.create()
|
||||
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 脚本上传成功 ")
|
||||
.append(Dates.current())
|
||||
.newLine()
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "> 开始执行脚本 ")
|
||||
.append(Dates.current())
|
||||
.newLine();
|
||||
this.appendLog(finishAppender);
|
||||
} catch (Exception e) {
|
||||
// 拼接失败日志
|
||||
AnsiAppender errorAppender = AnsiAppender.create()
|
||||
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 脚本上传失败 ")
|
||||
.append(Dates.current())
|
||||
.newLine();
|
||||
this.appendLog(errorAppender);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishCallback(Exception e) {
|
||||
// 执行完成回调
|
||||
super.onFinishCallback(e);
|
||||
// 拼接日志
|
||||
AnsiAppender appender = AnsiAppender.create()
|
||||
.newLine();
|
||||
if (this.status == ExecHostStatusEnum.INTERRUPTED) {
|
||||
// 中断执行
|
||||
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行中断 ")
|
||||
.append(Dates.current())
|
||||
.newLine();
|
||||
} else if (this.status == ExecHostStatusEnum.FAILED) {
|
||||
// 执行失败
|
||||
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "< 命令执行失败 ")
|
||||
.append(Dates.current())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), "错误原因: ")
|
||||
.append(this.getErrorMessage(e))
|
||||
.newLine();
|
||||
} else if (this.status == ExecHostStatusEnum.TIMEOUT) {
|
||||
// 更新执行超时
|
||||
appender.append(AnsiForeground.BRIGHT_YELLOW.and(AnsiFont.BOLD), "< 命令执行超时 ")
|
||||
.append(Dates.current())
|
||||
.newLine();
|
||||
} else {
|
||||
long ms = this.updateRecord.getFinishTime().getTime() - this.updateRecord.getStartTime().getTime();
|
||||
Integer exitStatus = this.updateRecord.getExitStatus();
|
||||
// 执行完成
|
||||
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), "< 命令执行完成 ")
|
||||
.append(Dates.current())
|
||||
.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "exit: ");
|
||||
if (ExitCode.isSuccess(exitStatus)) {
|
||||
appender.append(AnsiForeground.BRIGHT_GREEN.and(AnsiFont.BOLD), exitStatus);
|
||||
} else {
|
||||
appender.append(AnsiForeground.BRIGHT_RED.and(AnsiFont.BOLD), exitStatus);
|
||||
}
|
||||
appender.newLine()
|
||||
.append(AnsiForeground.BRIGHT_BLUE.and(AnsiFont.BOLD), "used: ")
|
||||
.append(Dates.interval(ms, false, "d ", "h ", "m ", "s"))
|
||||
.append(" (")
|
||||
.append(ms)
|
||||
.append(" ms)")
|
||||
.newLine();
|
||||
}
|
||||
// 拼接日志
|
||||
this.appendLog(appender);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.command.handler;
|
||||
|
||||
import com.orion.lang.support.timeout.TimeoutChecker;
|
||||
import com.orion.lang.support.timeout.TimeoutEndpoint;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 命令执行器 原始日志输出
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/12 11:30
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExecCommandOriginHandler extends BaseExecCommandHandler {
|
||||
|
||||
public ExecCommandOriginHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||
super(execCommand, execHostCommand, timeoutChecker);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package com.orion.ops.module.asset.handler.host.exec.command.handler;
|
||||
import com.orion.lang.support.timeout.TimeoutChecker;
|
||||
import com.orion.lang.support.timeout.TimeoutCheckers;
|
||||
import com.orion.lang.support.timeout.TimeoutEndpoint;
|
||||
import com.orion.lang.utils.Booleans;
|
||||
import com.orion.lang.utils.Threads;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.ops.module.asset.dao.ExecLogDAO;
|
||||
import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||
import com.orion.ops.module.asset.define.config.AppExecLogConfig;
|
||||
import com.orion.ops.module.asset.entity.domain.ExecLogDO;
|
||||
import com.orion.ops.module.asset.enums.ExecStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandDTO;
|
||||
@@ -34,6 +36,8 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
|
||||
private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class);
|
||||
|
||||
private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class);
|
||||
|
||||
private final ExecCommandDTO execCommand;
|
||||
|
||||
private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
||||
@@ -73,9 +77,9 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupted() {
|
||||
log.info("ExecTaskHandler-interrupted id: {}", execCommand.getLogId());
|
||||
handlers.forEach(IExecCommandHandler::interrupted);
|
||||
public void interrupt() {
|
||||
log.info("ExecTaskHandler-interrupt id: {}", execCommand.getLogId());
|
||||
handlers.forEach(IExecCommandHandler::interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,18 +97,34 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
List<ExecCommandHostDTO> hosts = execCommand.getHosts();
|
||||
if (hosts.size() == 1) {
|
||||
// 单个主机直接执行
|
||||
ExecCommandHandler handler = new ExecCommandHandler(execCommand, hosts.get(0), timeoutChecker);
|
||||
IExecCommandHandler handler = this.createCommandHandler(hosts.get(0));
|
||||
handlers.add(handler);
|
||||
handler.run();
|
||||
} else {
|
||||
hosts.stream()
|
||||
.map(s -> new ExecCommandHandler(execCommand, s, timeoutChecker))
|
||||
.map(this::createCommandHandler)
|
||||
.forEach(handlers::add);
|
||||
// 多个主机异步阻塞执行
|
||||
Threads.blockRun(handlers, AssetThreadPools.EXEC_HOST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建命令执行器
|
||||
*
|
||||
* @param host host
|
||||
* @return handler
|
||||
*/
|
||||
private IExecCommandHandler createCommandHandler(ExecCommandHostDTO host) {
|
||||
if (Booleans.isTrue(appExecLogConfig.getAppendAnsi())) {
|
||||
// ansi 日志
|
||||
return new ExecCommandAnsiHandler(execCommand, host, timeoutChecker);
|
||||
} else {
|
||||
// 原始日志
|
||||
return new ExecCommandOriginHandler(execCommand, host, timeoutChecker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
|
||||
/**
|
||||
* 中断执行
|
||||
*/
|
||||
void interrupted();
|
||||
void interrupt();
|
||||
|
||||
/**
|
||||
* 获取当前状态
|
||||
|
||||
@@ -23,6 +23,6 @@ public interface IExecTaskHandler extends Runnable, SafeCloseable {
|
||||
/**
|
||||
* 中断执行
|
||||
*/
|
||||
void interrupted();
|
||||
void interrupt();
|
||||
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
|
||||
throw Exceptions.ioRuntime(e);
|
||||
} finally {
|
||||
// 同关闭 transfer downloader
|
||||
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor
|
||||
// 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
|
||||
Streams.close(this.executor);
|
||||
this.connect();
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class DownloadSession extends TransferHostSession implements IDownloadSes
|
||||
|
||||
@Override
|
||||
protected void closeStream() {
|
||||
// 关闭 inputStream 可能会被阻塞 ??..?? 只能关闭 executor
|
||||
// 关闭 inputStream 可能会被阻塞 ???...??? 只能关闭 executor
|
||||
Streams.close(this.executor);
|
||||
this.executor = null;
|
||||
this.inputStream = null;
|
||||
|
||||
@@ -197,9 +197,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
return ExecCommandHostDTO.builder()
|
||||
.hostId(s.getHostId())
|
||||
.hostLogId(s.getId())
|
||||
.hostName(s.getHostName())
|
||||
.hostAddress(s.getHostAddress())
|
||||
.command(s.getCommand())
|
||||
.logPath(s.getLogPath())
|
||||
.scriptPath(s.getScriptPath())
|
||||
.username(config.getUsername())
|
||||
.charset(config.getCharset())
|
||||
.fileNameCharset(config.getFileNameCharset())
|
||||
.fileContentCharset(config.getFileContentCharset())
|
||||
@@ -208,6 +211,10 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
// 执行信息
|
||||
ExecCommandDTO exec = ExecCommandDTO.builder()
|
||||
.logId(execLog.getId())
|
||||
.userId(execLog.getUserId())
|
||||
.username(execLog.getUsername())
|
||||
.description(execLog.getDescription())
|
||||
.execSeq(execLog.getExecSeq())
|
||||
.timeout(execLog.getTimeout())
|
||||
.scriptExec(ScriptExecEnum.isEnabled(execLog.getScriptExec()))
|
||||
.hosts(hosts)
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ExecHostLogServiceImpl implements ExecHostLogService {
|
||||
.flatMap(s -> s.stream()
|
||||
.filter(h -> h.getHostId().equals(record.getHostId()))
|
||||
.findFirst())
|
||||
.ifPresent(IExecCommandHandler::interrupted);
|
||||
.ifPresent(IExecCommandHandler::interrupt);
|
||||
// 删除
|
||||
int effect = execHostLogDAO.deleteById(id);
|
||||
log.info("ExecHostLogService-deleteExecHostLogById id: {}, effect: {}", id, effect);
|
||||
|
||||
@@ -176,7 +176,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
ExecLogDO record = execLogDAO.selectByIdSource(id, source);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
// 中断命令执行
|
||||
this.interruptedTask(Lists.singleton(id));
|
||||
this.interruptTask(Lists.singleton(id));
|
||||
// 删除执行日志
|
||||
int effect = execLogDAO.deleteById(id);
|
||||
// 删除主机日志
|
||||
@@ -200,7 +200,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
.intValue();
|
||||
Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED);
|
||||
// 中断命令执行
|
||||
this.interruptedTask(idList);
|
||||
this.interruptTask(idList);
|
||||
// 删除执行日志
|
||||
int effect = execLogDAO.deleteBatchIds(idList);
|
||||
// 删除主机日志
|
||||
@@ -230,7 +230,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
int effect = 0;
|
||||
if (!idList.isEmpty()) {
|
||||
// 中断命令执行
|
||||
this.interruptedTask(idList);
|
||||
this.interruptTask(idList);
|
||||
// 删除执行日志
|
||||
effect = execLogDAO.delete(wrapper);
|
||||
// 删除主机日志
|
||||
@@ -256,9 +256,9 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
// 中断执行
|
||||
IExecTaskHandler task = execTaskManager.getTask(logId);
|
||||
if (task != null) {
|
||||
log.info("ExecLogService.interruptExec interrupted logId: {}", logId);
|
||||
log.info("ExecLogService.interruptExec interrupt logId: {}", logId);
|
||||
// 中断
|
||||
task.interrupted();
|
||||
task.interrupt();
|
||||
} else {
|
||||
log.info("ExecLogService.interruptExec updateStatus start logId: {}", logId);
|
||||
// 不存在则直接修改状态
|
||||
@@ -299,7 +299,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
// 中断执行
|
||||
IExecTaskHandler task = execTaskManager.getTask(logId);
|
||||
if (task != null) {
|
||||
log.info("ExecLogService.interruptHostExec interrupted logId: {}, hostLogId: {}", logId, hostLogId);
|
||||
log.info("ExecLogService.interruptHostExec interrupt logId: {}, hostLogId: {}", logId, hostLogId);
|
||||
IExecCommandHandler handler = task.getHandlers()
|
||||
.stream()
|
||||
.filter(s -> s.getHostId().equals(hostLog.getHostId()))
|
||||
@@ -307,7 +307,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
.orElse(null);
|
||||
// 中断
|
||||
if (handler != null) {
|
||||
handler.interrupted();
|
||||
handler.interrupt();
|
||||
}
|
||||
} else {
|
||||
log.info("ExecLogService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId);
|
||||
@@ -465,11 +465,11 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
*
|
||||
* @param idList idList
|
||||
*/
|
||||
private void interruptedTask(List<Long> idList) {
|
||||
private void interruptTask(List<Long> idList) {
|
||||
idList.stream()
|
||||
.map(execTaskManager::getTask)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(IExecTaskHandler::interrupted);
|
||||
.forEach(IExecTaskHandler::interrupt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true")
|
||||
@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true", matchIfMissing = true)
|
||||
public class ExecLogFileAutoClearTask {
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user