🔨 优化批量执行模块.
This commit is contained in:
@@ -24,7 +24,8 @@ package org.dromara.visor.module.asset.handler.host.exec.command;
|
|||||||
|
|
||||||
import org.dromara.visor.module.asset.define.AssetThreadPools;
|
import org.dromara.visor.module.asset.define.AssetThreadPools;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.handler.ExecTaskHandler;
|
import org.dromara.visor.module.asset.handler.host.exec.command.handler.ExecTaskHandler;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量执行命令执行器
|
* 批量执行命令执行器
|
||||||
@@ -38,10 +39,11 @@ public class ExecTaskExecutors {
|
|||||||
/**
|
/**
|
||||||
* 执行命令
|
* 执行命令
|
||||||
*
|
*
|
||||||
* @param command command
|
* @param id id
|
||||||
|
* @param execHostIdList execHostIdList
|
||||||
*/
|
*/
|
||||||
public static void start(ExecCommandDTO command) {
|
public static void start(Long id, List<Long> execHostIdList) {
|
||||||
AssetThreadPools.EXEC_TASK.execute(new ExecTaskHandler(command));
|
AssetThreadPools.EXEC_TASK.execute(new ExecTaskHandler(id, execHostIdList));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ package org.dromara.visor.module.asset.handler.host.exec.command.handler;
|
|||||||
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
||||||
import cn.orionsec.kit.lang.exception.ConnectionRuntimeException;
|
import cn.orionsec.kit.lang.exception.ConnectionRuntimeException;
|
||||||
import cn.orionsec.kit.lang.exception.SftpException;
|
import cn.orionsec.kit.lang.exception.SftpException;
|
||||||
|
import cn.orionsec.kit.lang.id.UUIds;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
||||||
import cn.orionsec.kit.lang.utils.Booleans;
|
|
||||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||||
import cn.orionsec.kit.lang.utils.Strings;
|
import cn.orionsec.kit.lang.utils.Strings;
|
||||||
import cn.orionsec.kit.lang.utils.ansi.AnsiAppender;
|
import cn.orionsec.kit.lang.utils.ansi.AnsiAppender;
|
||||||
|
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||||
import cn.orionsec.kit.lang.utils.io.Streams;
|
import cn.orionsec.kit.lang.utils.io.Streams;
|
||||||
import cn.orionsec.kit.net.host.SessionStore;
|
import cn.orionsec.kit.net.host.SessionStore;
|
||||||
import cn.orionsec.kit.net.host.sftp.SftpExecutor;
|
import cn.orionsec.kit.net.host.sftp.SftpExecutor;
|
||||||
@@ -40,22 +41,29 @@ import com.alibaba.fastjson.JSON;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.visor.common.constant.ErrorMessage;
|
import org.dromara.visor.common.constant.ErrorMessage;
|
||||||
|
import org.dromara.visor.common.constant.FileConst;
|
||||||
|
import org.dromara.visor.common.enums.BooleanBit;
|
||||||
|
import org.dromara.visor.common.enums.EndpointDefine;
|
||||||
import org.dromara.visor.common.interfaces.FileClient;
|
import org.dromara.visor.common.interfaces.FileClient;
|
||||||
import org.dromara.visor.common.utils.PathUtils;
|
import org.dromara.visor.common.utils.PathUtils;
|
||||||
|
import org.dromara.visor.common.utils.Valid;
|
||||||
import org.dromara.visor.module.asset.dao.ExecHostLogDAO;
|
import org.dromara.visor.module.asset.dao.ExecHostLogDAO;
|
||||||
import org.dromara.visor.module.asset.entity.domain.ExecHostLogDO;
|
import org.dromara.visor.module.asset.entity.domain.ExecHostLogDO;
|
||||||
|
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
||||||
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
|
||||||
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
import org.dromara.visor.module.asset.enums.HostOsTypeEnum;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandHostDTO;
|
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.log.manager.ExecLogManager;
|
import org.dromara.visor.module.asset.handler.host.exec.log.manager.ExecLogManager;
|
||||||
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores;
|
import org.dromara.visor.module.asset.handler.host.jsch.SessionStores;
|
||||||
import org.dromara.visor.module.asset.service.TerminalService;
|
import org.dromara.visor.module.asset.service.TerminalService;
|
||||||
|
import org.dromara.visor.module.asset.utils.ExecUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令执行器 基类
|
* 命令执行器 基类
|
||||||
@@ -75,16 +83,21 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
|
|
||||||
private static final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
|
private static final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
|
||||||
|
|
||||||
protected final ExecCommandDTO execCommand;
|
@Getter
|
||||||
|
protected final Long id;
|
||||||
|
|
||||||
protected final ExecCommandHostDTO execHostCommand;
|
protected final Map<String, Object> builtParams;
|
||||||
|
|
||||||
private final TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
protected final TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
||||||
|
|
||||||
|
protected final ExecLogDO execLog;
|
||||||
|
|
||||||
|
protected ExecHostLogDO execHostLog;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected ExecHostStatusEnum status;
|
protected ExecHostStatusEnum status;
|
||||||
|
|
||||||
protected ExecHostLogDO updateRecord;
|
private TerminalConnectDTO connect;
|
||||||
|
|
||||||
private OutputStream logOutputStream;
|
private OutputStream logOutputStream;
|
||||||
|
|
||||||
@@ -99,24 +112,28 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
|
|
||||||
private volatile boolean interrupted;
|
private volatile boolean interrupted;
|
||||||
|
|
||||||
public BaseExecCommandHandler(ExecCommandDTO execCommand,
|
public BaseExecCommandHandler(Long id,
|
||||||
ExecCommandHostDTO execHostCommand,
|
ExecLogDO execLog,
|
||||||
|
Map<String, Object> builtParams,
|
||||||
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||||
this.status = ExecHostStatusEnum.WAITING;
|
this.id = id;
|
||||||
this.execCommand = execCommand;
|
this.execLog = execLog;
|
||||||
this.execHostCommand = execHostCommand;
|
this.builtParams = builtParams;
|
||||||
this.timeoutChecker = timeoutChecker;
|
this.timeoutChecker = timeoutChecker;
|
||||||
this.updateRecord = new ExecHostLogDO();
|
this.status = ExecHostStatusEnum.WAITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Long id = execHostCommand.getHostLogId();
|
|
||||||
Exception ex = null;
|
Exception ex = null;
|
||||||
log.info("ExecCommandHandler run start id: {}, info: {}", id, JSON.toJSONString(execHostCommand));
|
log.info("ExecCommandHandler run start id: {}", id);
|
||||||
// 更新状态
|
// 初始化数据以及修改状态
|
||||||
this.updateStatus(ExecHostStatusEnum.RUNNING, null);
|
if (!this.initData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
// 初始化日志
|
||||||
|
this.initLogOutputStream();
|
||||||
// 执行命令
|
// 执行命令
|
||||||
this.execCommand();
|
this.execCommand();
|
||||||
log.info("ExecCommandHandler run complete id: {}", id);
|
log.info("ExecCommandHandler run complete id: {}", id);
|
||||||
@@ -135,13 +152,47 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化日志输出流
|
* 初始化数据
|
||||||
*
|
*
|
||||||
* @throws Exception Exception
|
* @return pass
|
||||||
*/
|
*/
|
||||||
protected void initLogOutputStream() throws Exception {
|
private boolean initData() {
|
||||||
// 打开日志流
|
Exception ex = null;
|
||||||
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
|
try {
|
||||||
|
// 查询任务
|
||||||
|
this.execHostLog = execHostLogDAO.selectById(id);
|
||||||
|
Valid.notNull(this.execHostLog, ErrorMessage.TASK_ABSENT);
|
||||||
|
// 检查任务状态
|
||||||
|
this.status = ExecHostStatusEnum.of(execHostLog.getStatus());
|
||||||
|
Valid.eq(this.status, ExecHostStatusEnum.WAITING, ErrorMessage.TASK_ABSENT, ErrorMessage.ILLEGAL_STATUS);
|
||||||
|
// 获取主机会话
|
||||||
|
this.connect = terminalService.getTerminalConnectInfo(execHostLog.getHostId(), execLog.getUserId());
|
||||||
|
// 获取内置参数
|
||||||
|
Map<String, Object> commandParams = this.getCommandParams();
|
||||||
|
// 获取实际命令
|
||||||
|
String command = ExecUtils.format(execLog.getCommand(), commandParams);
|
||||||
|
// 获取日志路径
|
||||||
|
String logPath = fileClient.getReturnPath(EndpointDefine.EXEC_LOG.format(execHostLog.getLogId(), execHostLog.getHostId()));
|
||||||
|
// 获取脚本路径
|
||||||
|
String scriptPath = null;
|
||||||
|
if (BooleanBit.toBoolean(execLog.getScriptExec())) {
|
||||||
|
scriptPath = this.buildScriptPath();
|
||||||
|
}
|
||||||
|
execHostLog.setCommand(command);
|
||||||
|
execHostLog.setParameter(JSON.toJSONString(commandParams));
|
||||||
|
execHostLog.setLogPath(logPath);
|
||||||
|
execHostLog.setScriptPath(scriptPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("BaseExecCommandHandler.initData error id: {}", id, e);
|
||||||
|
ex = e;
|
||||||
|
}
|
||||||
|
boolean passed = ex == null;
|
||||||
|
// 更新状态
|
||||||
|
this.updateStatus(passed ? ExecHostStatusEnum.RUNNING : ExecHostStatusEnum.FAILED, ex, (s) -> {
|
||||||
|
// 修改其他参数
|
||||||
|
s.setCommand(execHostLog.getCommand());
|
||||||
|
});
|
||||||
|
return passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,29 +201,36 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
*/
|
*/
|
||||||
protected void execCommand() throws Exception {
|
protected void execCommand() throws Exception {
|
||||||
// 初始化日志
|
|
||||||
this.initLogOutputStream();
|
|
||||||
// 打开会话
|
// 打开会话
|
||||||
TerminalConnectDTO connect = terminalService.getTerminalConnectInfo(execHostCommand.getHostId(), execCommand.getUserId());
|
|
||||||
this.sessionStore = SessionStores.openSessionStore(connect);
|
this.sessionStore = SessionStores.openSessionStore(connect);
|
||||||
if (Booleans.isTrue(execCommand.getScriptExec())) {
|
if (BooleanBit.toBoolean(execLog.getScriptExec())) {
|
||||||
// 上传脚本文件
|
// 上传脚本文件
|
||||||
this.uploadScriptFile();
|
this.uploadScriptFile();
|
||||||
// 执行脚本文件
|
// 执行脚本文件
|
||||||
this.executor = sessionStore.getCommandExecutor(execHostCommand.getScriptPath());
|
this.executor = sessionStore.getCommandExecutor(execHostLog.getScriptPath());
|
||||||
} else {
|
} else {
|
||||||
// 执行命令
|
// 执行命令
|
||||||
byte[] command = Strings.replaceCRLF(execHostCommand.getCommand()).getBytes(execHostCommand.getCharset());
|
byte[] command = execHostLog.getCommand().getBytes(connect.getCharset());
|
||||||
this.executor = sessionStore.getCommandExecutor(command);
|
this.executor = sessionStore.getCommandExecutor(command);
|
||||||
}
|
}
|
||||||
// 执行命令
|
// 执行命令
|
||||||
executor.timeout(execCommand.getTimeout(), TimeUnit.SECONDS, timeoutChecker);
|
executor.timeout(execLog.getTimeout(), TimeUnit.SECONDS, timeoutChecker);
|
||||||
executor.merge();
|
executor.merge();
|
||||||
executor.transfer(logOutputStream);
|
executor.transfer(logOutputStream);
|
||||||
executor.connect();
|
executor.connect();
|
||||||
executor.exec();
|
executor.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化日志输出流
|
||||||
|
*
|
||||||
|
* @throws Exception Exception
|
||||||
|
*/
|
||||||
|
protected void initLogOutputStream() throws Exception {
|
||||||
|
// 打开日志流
|
||||||
|
this.logOutputStream = fileClient.getContentOutputStream(execHostLog.getLogPath());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传脚本文件
|
* 上传脚本文件
|
||||||
*/
|
*/
|
||||||
@@ -180,14 +238,14 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
SftpExecutor sftpExecutor = null;
|
SftpExecutor sftpExecutor = null;
|
||||||
try {
|
try {
|
||||||
// 打开 sftp
|
// 打开 sftp
|
||||||
sftpExecutor = sessionStore.getSftpExecutor(execHostCommand.getFileNameCharset());
|
sftpExecutor = sessionStore.getSftpExecutor(connect.getFileNameCharset());
|
||||||
sftpExecutor.connect();
|
sftpExecutor.connect();
|
||||||
// 文件上传必须要以 / 开头
|
// 文件上传必须要以 / 开头
|
||||||
String scriptPath = PathUtils.prependSeparator(execHostCommand.getScriptPath());
|
String scriptPath = PathUtils.prependSeparator(execHostLog.getScriptPath());
|
||||||
// 创建文件
|
// 创建文件
|
||||||
sftpExecutor.touch(scriptPath);
|
sftpExecutor.touch(scriptPath);
|
||||||
// 写入命令
|
// 写入命令
|
||||||
byte[] command = Strings.replaceCRLF(execHostCommand.getCommand()).getBytes(execHostCommand.getFileContentCharset());
|
byte[] command = execHostLog.getCommand().getBytes(connect.getFileContentCharset());
|
||||||
sftpExecutor.write(scriptPath, command);
|
sftpExecutor.write(scriptPath, command);
|
||||||
// 修改权限
|
// 修改权限
|
||||||
sftpExecutor.changeMode(scriptPath, 777);
|
sftpExecutor.changeMode(scriptPath, 777);
|
||||||
@@ -207,16 +265,16 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
// 执行回调
|
// 执行回调
|
||||||
if (this.interrupted) {
|
if (this.interrupted) {
|
||||||
// 中断执行
|
// 中断执行
|
||||||
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null);
|
this.updateStatus(ExecHostStatusEnum.INTERRUPTED, null, null);
|
||||||
} else if (e != null) {
|
} else if (e != null) {
|
||||||
// 执行失败
|
// 执行失败
|
||||||
this.updateStatus(ExecHostStatusEnum.FAILED, e);
|
this.updateStatus(ExecHostStatusEnum.FAILED, e, null);
|
||||||
} else if (executor.isTimeout()) {
|
} else if (executor.isTimeout()) {
|
||||||
// 更新执行超时
|
// 更新执行超时
|
||||||
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null);
|
this.updateStatus(ExecHostStatusEnum.TIMEOUT, null, null);
|
||||||
} else {
|
} else {
|
||||||
// 更新执行完成
|
// 更新执行完成
|
||||||
this.updateStatus(ExecHostStatusEnum.COMPLETED, null);
|
this.updateStatus(ExecHostStatusEnum.COMPLETED, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,33 +297,45 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
*
|
*
|
||||||
* @param status status
|
* @param status status
|
||||||
* @param ex ex
|
* @param ex ex
|
||||||
|
* @param filler filler
|
||||||
*/
|
*/
|
||||||
private void updateStatus(ExecHostStatusEnum status, Exception ex) {
|
private void updateStatus(ExecHostStatusEnum status, Exception ex, Consumer<ExecHostLogDO> filler) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
Long id = execHostCommand.getHostLogId();
|
|
||||||
String statusName = status.name();
|
String statusName = status.name();
|
||||||
|
execHostLog.setStatus(statusName);
|
||||||
log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
|
log.info("BaseExecCommandHandler.updateStatus start id: {}, status: {}", id, statusName);
|
||||||
try {
|
try {
|
||||||
updateRecord.setId(id);
|
|
||||||
updateRecord.setStatus(statusName);
|
|
||||||
if (ExecHostStatusEnum.RUNNING.equals(status)) {
|
if (ExecHostStatusEnum.RUNNING.equals(status)) {
|
||||||
// 运行中
|
// 运行中
|
||||||
updateRecord.setStartTime(new Date());
|
execHostLog.setStartTime(new Date());
|
||||||
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
|
} else if (ExecHostStatusEnum.COMPLETED.equals(status)) {
|
||||||
// 完成
|
// 完成
|
||||||
updateRecord.setFinishTime(new Date());
|
execHostLog.setFinishTime(new Date());
|
||||||
updateRecord.setExitCode(executor.getExitCode());
|
execHostLog.setExitCode(executor.getExitCode());
|
||||||
this.exitCode = executor.getExitCode();
|
this.exitCode = executor.getExitCode();
|
||||||
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
|
} else if (ExecHostStatusEnum.FAILED.equals(status)) {
|
||||||
// 失败
|
// 失败
|
||||||
updateRecord.setFinishTime(new Date());
|
execHostLog.setFinishTime(new Date());
|
||||||
updateRecord.setErrorMessage(this.getErrorMessage(ex));
|
execHostLog.setErrorMessage(this.getErrorMessage(ex));
|
||||||
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
|
} else if (ExecHostStatusEnum.TIMEOUT.equals(status)) {
|
||||||
// 超时
|
// 超时
|
||||||
updateRecord.setFinishTime(new Date());
|
execHostLog.setFinishTime(new Date());
|
||||||
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
|
} else if (ExecHostStatusEnum.INTERRUPTED.equals(status)) {
|
||||||
// 中断
|
// 中断
|
||||||
updateRecord.setFinishTime(new Date());
|
execHostLog.setFinishTime(new Date());
|
||||||
|
}
|
||||||
|
// 选择性更新
|
||||||
|
ExecHostLogDO updateRecord = ExecHostLogDO.builder()
|
||||||
|
.id(execHostLog.getId())
|
||||||
|
.status(execHostLog.getStatus())
|
||||||
|
.exitCode(execHostLog.getExitCode())
|
||||||
|
.startTime(execHostLog.getStartTime())
|
||||||
|
.finishTime(execHostLog.getFinishTime())
|
||||||
|
.errorMessage(execHostLog.getErrorMessage())
|
||||||
|
.build();
|
||||||
|
// 填充参数
|
||||||
|
if (filler != null) {
|
||||||
|
filler.accept(updateRecord);
|
||||||
}
|
}
|
||||||
int effect = execHostLogDAO.updateById(updateRecord);
|
int effect = execHostLogDAO.updateById(updateRecord);
|
||||||
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
|
log.info("BaseExecCommandHandler.updateStatus finish id: {}, effect: {}", id, effect);
|
||||||
@@ -281,8 +351,7 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
log.info("BaseExecCommandHandler.interrupt id: {}, interrupted: {}, closed: {}",
|
log.info("BaseExecCommandHandler.interrupt id: {}, interrupted: {}, closed: {}", id, interrupted, closed);
|
||||||
execHostCommand.getHostLogId(), interrupted, closed);
|
|
||||||
if (this.interrupted || this.closed) {
|
if (this.interrupted || this.closed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -294,8 +363,7 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
log.info("BaseExecCommandHandler.closed id: {}, closed: {}",
|
log.info("BaseExecCommandHandler.closed id: {}, closed: {}", id, closed);
|
||||||
execHostCommand.getHostLogId(), closed);
|
|
||||||
if (this.closed) {
|
if (this.closed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -303,7 +371,7 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
Streams.close(logOutputStream);
|
Streams.close(logOutputStream);
|
||||||
Streams.close(executor);
|
Streams.close(executor);
|
||||||
Streams.close(sessionStore);
|
Streams.close(sessionStore);
|
||||||
execLogManager.asyncCloseTailFile(execHostCommand.getLogPath());
|
execLogManager.asyncCloseTailFile(execHostLog.getLogPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,9 +404,40 @@ public abstract class BaseExecCommandHandler implements IExecCommandHandler {
|
|||||||
return Strings.retain(message, 250);
|
return Strings.retain(message, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Long getHostId() {
|
* 获取命令实际参数
|
||||||
return execHostCommand.getHostId();
|
*
|
||||||
|
* @return params
|
||||||
|
*/
|
||||||
|
private Map<String, Object> getCommandParams() {
|
||||||
|
String uuid = UUIds.random();
|
||||||
|
Map<String, Object> params = Maps.newMap(builtParams);
|
||||||
|
params.put("hostId", connect.getHostId());
|
||||||
|
params.put("hostName", connect.getHostName());
|
||||||
|
params.put("hostCode", connect.getHostCode());
|
||||||
|
params.put("hostAddress", connect.getHostAddress());
|
||||||
|
params.put("hostPort", connect.getHostPort());
|
||||||
|
params.put("hostUsername", connect.getUsername());
|
||||||
|
params.put("hostUuid", uuid);
|
||||||
|
params.put("hostUuidShort", uuid.replace("-", Strings.EMPTY));
|
||||||
|
params.put("osType", connect.getOsType());
|
||||||
|
params.put("charset", connect.getCharset());
|
||||||
|
params.put("scriptPath", execHostLog.getScriptPath());
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建脚本路径
|
||||||
|
*
|
||||||
|
* @return scriptPath
|
||||||
|
*/
|
||||||
|
private String buildScriptPath() {
|
||||||
|
HostOsTypeEnum os = HostOsTypeEnum.of(connect.getOsType());
|
||||||
|
String name = FileConst.EXEC
|
||||||
|
+ "/" + execHostLog.getLogId()
|
||||||
|
+ "/" + id
|
||||||
|
+ os.getScriptSuffix();
|
||||||
|
return PathUtils.buildAppPath(HostOsTypeEnum.WINDOWS.equals(os), connect.getUsername(), FileConst.SCRIPT, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,16 @@ package org.dromara.visor.module.asset.handler.host.exec.command.handler;
|
|||||||
|
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
||||||
import cn.orionsec.kit.lang.utils.Booleans;
|
|
||||||
import cn.orionsec.kit.lang.utils.ansi.AnsiAppender;
|
import cn.orionsec.kit.lang.utils.ansi.AnsiAppender;
|
||||||
import cn.orionsec.kit.lang.utils.ansi.style.color.AnsiForeground;
|
import cn.orionsec.kit.lang.utils.ansi.style.color.AnsiForeground;
|
||||||
import cn.orionsec.kit.lang.utils.time.Dates;
|
import cn.orionsec.kit.lang.utils.time.Dates;
|
||||||
import cn.orionsec.kit.net.host.ssh.ExitCode;
|
import cn.orionsec.kit.net.host.ssh.ExitCode;
|
||||||
import org.dromara.visor.common.constant.Const;
|
import org.dromara.visor.common.constant.Const;
|
||||||
|
import org.dromara.visor.common.enums.BooleanBit;
|
||||||
|
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
||||||
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandHostDTO;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令执行器 ansi 日志输出
|
* 命令执行器 ansi 日志输出
|
||||||
@@ -41,10 +42,13 @@ import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecComman
|
|||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @since 2024/4/25 18:19
|
* @since 2024/4/25 18:19
|
||||||
*/
|
*/
|
||||||
public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
|
public class ExecCommandDetailHandler extends BaseExecCommandHandler {
|
||||||
|
|
||||||
public ExecCommandAnsiHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
public ExecCommandDetailHandler(Long id,
|
||||||
super(execCommand, execHostCommand, timeoutChecker);
|
ExecLogDO execLog,
|
||||||
|
Map<String, Object> builtParams,
|
||||||
|
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||||
|
super(id, execLog, builtParams, timeoutChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,52 +60,52 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
|
|||||||
.append(this.getCurrentTime())
|
.append(this.getCurrentTime())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "执行记录: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "执行记录: ")
|
||||||
.append(execCommand.getLogId())
|
.append(execLog.getId())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "执行描述: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "执行描述: ")
|
||||||
.append(execCommand.getDescription())
|
.append(execLog.getDescription())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "执行用户: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "执行用户: ")
|
||||||
.append(execCommand.getUsername());
|
.append(execLog.getUsername());
|
||||||
// 非系统用户执行添加 userId
|
// 非系统用户执行添加 userId
|
||||||
if (Const.SYSTEM_USER_ID.equals(execCommand.getUserId())) {
|
if (Const.SYSTEM_USER_ID.equals(execLog.getUserId())) {
|
||||||
appender.newLine();
|
appender.newLine();
|
||||||
} else {
|
} else {
|
||||||
appender.append(" (")
|
appender.append(" (")
|
||||||
.append(execCommand.getUserId())
|
.append(execLog.getUserId())
|
||||||
.append(")")
|
.append(")")
|
||||||
.newLine();
|
.newLine();
|
||||||
}
|
}
|
||||||
// 执行序列
|
// 执行序列
|
||||||
if (execCommand.getExecSeq() != null) {
|
if (execLog.getExecSeq() != null) {
|
||||||
appender.append(AnsiForeground.BRIGHT_BLUE, "执行序列: ")
|
appender.append(AnsiForeground.BRIGHT_BLUE, "执行序列: ")
|
||||||
.append('#')
|
.append('#')
|
||||||
.append(execCommand.getExecSeq())
|
.append(execLog.getExecSeq())
|
||||||
.newLine();
|
.newLine();
|
||||||
}
|
}
|
||||||
appender.append(AnsiForeground.BRIGHT_BLUE, "执行主机: ")
|
appender.append(AnsiForeground.BRIGHT_BLUE, "执行主机: ")
|
||||||
.append(execHostCommand.getHostName())
|
.append(execHostLog.getHostName())
|
||||||
.append(" (")
|
.append(" (")
|
||||||
.append(execHostCommand.getHostId())
|
.append(execHostLog.getHostId())
|
||||||
.append(")")
|
.append(")")
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "主机地址: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "主机地址: ")
|
||||||
.append(execHostCommand.getHostAddress())
|
.append(execHostLog.getHostAddress())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "超时时间: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "超时时间: ")
|
||||||
.append(execCommand.getTimeout())
|
.append(execLog.getTimeout())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "脚本执行: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "脚本执行: ")
|
||||||
.append(execCommand.getScriptExec())
|
.append(execLog.getScriptExec())
|
||||||
.newLine()
|
.newLine()
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_GREEN, "> 执行命令 ")
|
.append(AnsiForeground.BRIGHT_GREEN, "> 执行命令 ")
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(execHostCommand.getCommand())
|
.append(execHostLog.getCommand())
|
||||||
.newLine()
|
.newLine()
|
||||||
.newLine();
|
.newLine();
|
||||||
// 非脚本执行拼接开始执行日志
|
// 非脚本执行拼接开始执行日志
|
||||||
if (!Booleans.isTrue(execCommand.getScriptExec())) {
|
if (!BooleanBit.toBoolean(execLog.getScriptExec())) {
|
||||||
appender.append(AnsiForeground.BRIGHT_GREEN, "> 开始执行命令 ")
|
appender.append(AnsiForeground.BRIGHT_GREEN, "> 开始执行命令 ")
|
||||||
.append(this.getCurrentTime())
|
.append(this.getCurrentTime())
|
||||||
.newLine();
|
.newLine();
|
||||||
@@ -119,7 +123,7 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
|
|||||||
.append(this.getCurrentTime())
|
.append(this.getCurrentTime())
|
||||||
.newLine()
|
.newLine()
|
||||||
.append(AnsiForeground.BRIGHT_BLUE, "文件路径: ")
|
.append(AnsiForeground.BRIGHT_BLUE, "文件路径: ")
|
||||||
.append(execHostCommand.getScriptPath())
|
.append(execHostLog.getScriptPath())
|
||||||
.newLine();
|
.newLine();
|
||||||
this.appendLog(startAppender);
|
this.appendLog(startAppender);
|
||||||
// 上传脚本文件
|
// 上传脚本文件
|
||||||
@@ -170,9 +174,9 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
|
|||||||
appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行超时 ")
|
appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行超时 ")
|
||||||
.append(this.getCurrentTime())
|
.append(this.getCurrentTime())
|
||||||
.newLine();
|
.newLine();
|
||||||
} else {
|
} else if (this.status == ExecHostStatusEnum.COMPLETED) {
|
||||||
long ms = updateRecord.getFinishTime().getTime() - updateRecord.getStartTime().getTime();
|
long ms = execHostLog.getFinishTime().getTime() - execHostLog.getStartTime().getTime();
|
||||||
Integer exitCode = updateRecord.getExitCode();
|
Integer exitCode = execHostLog.getExitCode();
|
||||||
// 执行完成
|
// 执行完成
|
||||||
appender.append(AnsiForeground.BRIGHT_GREEN, "< 命令执行完成 ")
|
appender.append(AnsiForeground.BRIGHT_GREEN, "< 命令执行完成 ")
|
||||||
.append(this.getCurrentTime())
|
.append(this.getCurrentTime())
|
||||||
@@ -25,8 +25,9 @@ package org.dromara.visor.module.asset.handler.host.exec.command.handler;
|
|||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandHostDTO;
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令执行器 原始日志输出
|
* 命令执行器 原始日志输出
|
||||||
@@ -38,8 +39,11 @@ import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecComman
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ExecCommandOriginHandler extends BaseExecCommandHandler {
|
public class ExecCommandOriginHandler extends BaseExecCommandHandler {
|
||||||
|
|
||||||
public ExecCommandOriginHandler(ExecCommandDTO execCommand, ExecCommandHostDTO execHostCommand, TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
public ExecCommandOriginHandler(Long id,
|
||||||
super(execCommand, execHostCommand, timeoutChecker);
|
ExecLogDO execLog,
|
||||||
|
Map<String, Object> builtParams,
|
||||||
|
TimeoutChecker<TimeoutEndpoint> timeoutChecker) {
|
||||||
|
super(id, execLog, builtParams, timeoutChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.dromara.visor.module.asset.handler.host.exec.command.handler;
|
package org.dromara.visor.module.asset.handler.host.exec.command.handler;
|
||||||
|
|
||||||
|
import cn.orionsec.kit.lang.id.UUIds;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutChecker;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutCheckers;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutCheckers;
|
||||||
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
import cn.orionsec.kit.lang.support.timeout.TimeoutEndpoint;
|
||||||
import cn.orionsec.kit.lang.utils.Booleans;
|
import cn.orionsec.kit.lang.utils.Booleans;
|
||||||
|
import cn.orionsec.kit.lang.utils.Strings;
|
||||||
import cn.orionsec.kit.lang.utils.Threads;
|
import cn.orionsec.kit.lang.utils.Threads;
|
||||||
|
import cn.orionsec.kit.lang.utils.Valid;
|
||||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||||
import cn.orionsec.kit.lang.utils.io.Streams;
|
import cn.orionsec.kit.lang.utils.io.Streams;
|
||||||
import cn.orionsec.kit.lang.utils.time.Dates;
|
import cn.orionsec.kit.lang.utils.time.Dates;
|
||||||
@@ -34,17 +37,17 @@ import cn.orionsec.kit.net.host.ssh.ExitCode;
|
|||||||
import cn.orionsec.kit.spring.SpringHolder;
|
import cn.orionsec.kit.spring.SpringHolder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.visor.common.constant.ErrorMessage;
|
||||||
import org.dromara.visor.common.constant.ExtraFieldConst;
|
import org.dromara.visor.common.constant.ExtraFieldConst;
|
||||||
import org.dromara.visor.module.asset.dao.ExecLogDAO;
|
import org.dromara.visor.module.asset.dao.ExecLogDAO;
|
||||||
import org.dromara.visor.module.asset.define.AssetThreadPools;
|
import org.dromara.visor.module.asset.define.AssetThreadPools;
|
||||||
import org.dromara.visor.module.asset.define.config.AppExecLogConfig;
|
import org.dromara.visor.module.asset.define.config.AppLogConfig;
|
||||||
import org.dromara.visor.module.asset.define.message.ExecMessageDefine;
|
import org.dromara.visor.module.asset.define.message.ExecMessageDefine;
|
||||||
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
||||||
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
import org.dromara.visor.module.asset.enums.ExecHostStatusEnum;
|
||||||
import org.dromara.visor.module.asset.enums.ExecStatusEnum;
|
import org.dromara.visor.module.asset.enums.ExecStatusEnum;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.manager.ExecTaskManager;
|
import org.dromara.visor.module.asset.handler.host.exec.command.manager.ExecTaskManager;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
import org.dromara.visor.module.asset.utils.ExecUtils;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandHostDTO;
|
|
||||||
import org.dromara.visor.module.infra.api.SystemMessageApi;
|
import org.dromara.visor.module.infra.api.SystemMessageApi;
|
||||||
import org.dromara.visor.module.infra.entity.dto.message.SystemMessageDTO;
|
import org.dromara.visor.module.infra.entity.dto.message.SystemMessageDTO;
|
||||||
|
|
||||||
@@ -63,15 +66,21 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ExecTaskHandler implements IExecTaskHandler {
|
public class ExecTaskHandler implements IExecTaskHandler {
|
||||||
|
|
||||||
|
private static final AppLogConfig appLogConfig = SpringHolder.getBean(AppLogConfig.class);
|
||||||
|
|
||||||
private static final ExecLogDAO execLogDAO = SpringHolder.getBean(ExecLogDAO.class);
|
private static final ExecLogDAO execLogDAO = SpringHolder.getBean(ExecLogDAO.class);
|
||||||
|
|
||||||
private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class);
|
private static final ExecTaskManager execTaskManager = SpringHolder.getBean(ExecTaskManager.class);
|
||||||
|
|
||||||
private static final AppExecLogConfig appExecLogConfig = SpringHolder.getBean(AppExecLogConfig.class);
|
|
||||||
|
|
||||||
private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class);
|
private static final SystemMessageApi systemMessageApi = SpringHolder.getBean(SystemMessageApi.class);
|
||||||
|
|
||||||
private final ExecCommandDTO execCommand;
|
private final Long id;
|
||||||
|
|
||||||
|
private final List<Long> execHostIdList;
|
||||||
|
|
||||||
|
private ExecLogDO execLog;
|
||||||
|
|
||||||
|
private Map<String, Object> builtParams;
|
||||||
|
|
||||||
private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
private TimeoutChecker<TimeoutEndpoint> timeoutChecker;
|
||||||
|
|
||||||
@@ -80,14 +89,19 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
|
|
||||||
private Date startTime;
|
private Date startTime;
|
||||||
|
|
||||||
public ExecTaskHandler(ExecCommandDTO execCommand) {
|
public ExecTaskHandler(Long id, List<Long> execHostIdList) {
|
||||||
this.execCommand = execCommand;
|
this.id = id;
|
||||||
|
this.execHostIdList = execHostIdList;
|
||||||
this.handlers = Lists.newList();
|
this.handlers = Lists.newList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Long id = execCommand.getLogId();
|
log.info("ExecTaskHandler start id: {}", id);
|
||||||
|
// 初始化数据
|
||||||
|
if (!this.initData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 添加任务
|
// 添加任务
|
||||||
execTaskManager.addTask(id, this);
|
execTaskManager.addTask(id, this);
|
||||||
log.info("ExecTaskHandler.run start id: {}", id);
|
log.info("ExecTaskHandler.run start id: {}", id);
|
||||||
@@ -115,17 +129,37 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
log.info("ExecTaskHandler-interrupt id: {}", execCommand.getLogId());
|
log.info("ExecTaskHandler-interrupt id: {}", id);
|
||||||
handlers.forEach(IExecCommandHandler::interrupt);
|
handlers.forEach(IExecCommandHandler::interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
log.info("ExecTaskHandler-close id: {}", execCommand.getLogId());
|
log.info("ExecTaskHandler-close id: {}", id);
|
||||||
Streams.close(timeoutChecker);
|
Streams.close(timeoutChecker);
|
||||||
this.handlers.forEach(Streams::close);
|
this.handlers.forEach(Streams::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化数据
|
||||||
|
*
|
||||||
|
* @return pass
|
||||||
|
*/
|
||||||
|
private boolean initData() {
|
||||||
|
try {
|
||||||
|
// 查询任务
|
||||||
|
this.execLog = execLogDAO.selectById(id);
|
||||||
|
Valid.notNull(execLog, ErrorMessage.TASK_ABSENT);
|
||||||
|
Valid.eq(execLog.getStatus(), ExecStatusEnum.WAITING.name(), ErrorMessage.ILLEGAL_STATUS);
|
||||||
|
// 获取内置变量
|
||||||
|
this.builtParams = this.getBaseBuiltinParams();
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("ExecTaskHandler.init error id: {}", id, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行主机命令
|
* 执行主机命令
|
||||||
*
|
*
|
||||||
@@ -133,19 +167,18 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
*/
|
*/
|
||||||
private void runHostCommand() throws Exception {
|
private void runHostCommand() throws Exception {
|
||||||
// 超时检查
|
// 超时检查
|
||||||
if (execCommand.getTimeout() != 0) {
|
if (execLog.getTimeout() != 0) {
|
||||||
this.timeoutChecker = TimeoutCheckers.create();
|
this.timeoutChecker = TimeoutCheckers.create();
|
||||||
AssetThreadPools.TIMEOUT_CHECK.execute(this.timeoutChecker);
|
AssetThreadPools.TIMEOUT_CHECK.execute(this.timeoutChecker);
|
||||||
}
|
}
|
||||||
// 执行命令
|
// 执行命令
|
||||||
List<ExecCommandHostDTO> hosts = execCommand.getHosts();
|
if (execHostIdList.size() == 1) {
|
||||||
if (hosts.size() == 1) {
|
|
||||||
// 单个主机直接执行
|
// 单个主机直接执行
|
||||||
IExecCommandHandler handler = this.createCommandHandler(hosts.get(0));
|
IExecCommandHandler handler = this.createCommandHandler(execHostIdList.get(0));
|
||||||
handlers.add(handler);
|
handlers.add(handler);
|
||||||
handler.run();
|
handler.run();
|
||||||
} else {
|
} else {
|
||||||
hosts.stream()
|
execHostIdList.stream()
|
||||||
.map(this::createCommandHandler)
|
.map(this::createCommandHandler)
|
||||||
.forEach(handlers::add);
|
.forEach(handlers::add);
|
||||||
// 多个主机异步阻塞执行
|
// 多个主机异步阻塞执行
|
||||||
@@ -156,16 +189,16 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
/**
|
/**
|
||||||
* 创建命令执行器
|
* 创建命令执行器
|
||||||
*
|
*
|
||||||
* @param host host
|
* @param execHostId execHostId
|
||||||
* @return handler
|
* @return handler
|
||||||
*/
|
*/
|
||||||
private IExecCommandHandler createCommandHandler(ExecCommandHostDTO host) {
|
private IExecCommandHandler createCommandHandler(Long execHostId) {
|
||||||
if (Booleans.isTrue(appExecLogConfig.getAppendAnsi())) {
|
if (Booleans.isTrue(appLogConfig.getExecAppendAnsi())) {
|
||||||
// ansi 日志
|
// 详细日志
|
||||||
return new ExecCommandAnsiHandler(execCommand, host, timeoutChecker);
|
return new ExecCommandDetailHandler(id, execLog, builtParams, timeoutChecker);
|
||||||
} else {
|
} else {
|
||||||
// 原始日志
|
// 原始日志
|
||||||
return new ExecCommandOriginHandler(execCommand, host, timeoutChecker);
|
return new ExecCommandOriginHandler(id, execLog, builtParams, timeoutChecker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +208,6 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
* @param status status
|
* @param status status
|
||||||
*/
|
*/
|
||||||
private void updateStatus(ExecStatusEnum status) {
|
private void updateStatus(ExecStatusEnum status) {
|
||||||
Long id = execCommand.getLogId();
|
|
||||||
try {
|
try {
|
||||||
String statusName = status.name();
|
String statusName = status.name();
|
||||||
log.info("ExecTaskHandler-updateStatus start id: {}, status: {}", id, statusName);
|
log.info("ExecTaskHandler-updateStatus start id: {}, status: {}", id, statusName);
|
||||||
@@ -214,16 +246,43 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
|||||||
}
|
}
|
||||||
// 参数
|
// 参数
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put(ExtraFieldConst.ID, execCommand.getLogId());
|
params.put(ExtraFieldConst.ID, id);
|
||||||
params.put(ExtraFieldConst.TIME, Dates.format(this.startTime, Dates.MD_HM));
|
params.put(ExtraFieldConst.TIME, Dates.format(this.startTime, Dates.MD_HM));
|
||||||
SystemMessageDTO message = SystemMessageDTO.builder()
|
SystemMessageDTO message = SystemMessageDTO.builder()
|
||||||
.receiverId(execCommand.getUserId())
|
.receiverId(execLog.getUserId())
|
||||||
.receiverUsername(execCommand.getUsername())
|
.receiverUsername(execLog.getUsername())
|
||||||
.relKey(String.valueOf(execCommand.getLogId()))
|
.relKey(String.valueOf(id))
|
||||||
.params(params)
|
.params(params)
|
||||||
.build();
|
.build();
|
||||||
// 发送
|
// 发送
|
||||||
systemMessageApi.create(ExecMessageDefine.EXEC_FAILED, message);
|
systemMessageApi.create(ExecMessageDefine.EXEC_FAILED, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取基础内置参数
|
||||||
|
*
|
||||||
|
* @return params
|
||||||
|
*/
|
||||||
|
private Map<String, Object> getBaseBuiltinParams() {
|
||||||
|
String uuid = UUIds.random();
|
||||||
|
Date date = new Date();
|
||||||
|
// 输入参数
|
||||||
|
Map<String, Object> params = ExecUtils.extraSchemaParams(execLog.getParameterSchema());
|
||||||
|
// 添加内置参数
|
||||||
|
params.put("userId", execLog.getUserId());
|
||||||
|
params.put("username", execLog.getUsername());
|
||||||
|
params.put("source", execLog.getSource());
|
||||||
|
params.put("sourceId", execLog.getSourceId());
|
||||||
|
params.put("seq", execLog.getExecSeq());
|
||||||
|
params.put("execId", id);
|
||||||
|
params.put("scriptExec", execLog.getScriptExec());
|
||||||
|
params.put("uuid", uuid);
|
||||||
|
params.put("uuidShort", uuid.replace("-", Strings.EMPTY));
|
||||||
|
params.put("timestampMillis", date.getTime());
|
||||||
|
params.put("timestamp", date.getTime() / Dates.SECOND_STAMP);
|
||||||
|
params.put("date", Dates.format(date, Dates.YMD));
|
||||||
|
params.put("datetime", Dates.format(date, Dates.YMD_HMS));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ public interface IExecCommandHandler extends Runnable, SafeCloseable {
|
|||||||
Integer getExitCode();
|
Integer getExitCode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取主机 id
|
* 获取任务 id
|
||||||
*
|
*
|
||||||
* @return hostId
|
* @return hostId
|
||||||
*/
|
*/
|
||||||
Long getHostId();
|
Long getId();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
|
||||||
*
|
|
||||||
* https://visor.dromara.org
|
|
||||||
* https://visor.dromara.org.cn
|
|
||||||
* https://visor.orionsec.cn
|
|
||||||
*
|
|
||||||
* Members:
|
|
||||||
* Jiahang Li - ljh1553488six@139.com - author
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.dromara.visor.module.asset.handler.host.exec.command.model;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量执行启动对象
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2024/3/11 15:46
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class ExecCommandDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* logId
|
|
||||||
*/
|
|
||||||
private Long logId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户id
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户名
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行序列
|
|
||||||
*/
|
|
||||||
private Integer execSeq;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 超时时间
|
|
||||||
*/
|
|
||||||
private Integer timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否使用脚本执行
|
|
||||||
*/
|
|
||||||
private Boolean scriptExec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行主机
|
|
||||||
*/
|
|
||||||
private List<ExecCommandHostDTO> hosts;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
|
||||||
*
|
|
||||||
* https://visor.dromara.org
|
|
||||||
* https://visor.dromara.org.cn
|
|
||||||
* https://visor.orionsec.cn
|
|
||||||
*
|
|
||||||
* Members:
|
|
||||||
* Jiahang Li - ljh1553488six@139.com - author
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.dromara.visor.module.asset.handler.host.exec.command.model;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量执行启动主机对象
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.0
|
|
||||||
* @since 2024/3/11 15:46
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class ExecCommandHostDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hostLogId
|
|
||||||
*/
|
|
||||||
private Long hostLogId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hostId
|
|
||||||
*/
|
|
||||||
private Long hostId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机名称
|
|
||||||
*/
|
|
||||||
private String hostName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机地址
|
|
||||||
*/
|
|
||||||
private String hostAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日志文件路径
|
|
||||||
*/
|
|
||||||
private String logPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 脚本路径
|
|
||||||
*/
|
|
||||||
private String scriptPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行命令
|
|
||||||
*/
|
|
||||||
private String command;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机用户
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 命令编码
|
|
||||||
*/
|
|
||||||
private String charset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件名称编码
|
|
||||||
*/
|
|
||||||
private String fileNameCharset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件内容编码
|
|
||||||
*/
|
|
||||||
private String fileContentCharset;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -82,6 +82,9 @@ public class ExecLogManager {
|
|||||||
* @param path path
|
* @param path path
|
||||||
*/
|
*/
|
||||||
public void asyncCloseTailFile(String path) {
|
public void asyncCloseTailFile(String path) {
|
||||||
|
if (path == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Threads.start(() -> {
|
Threads.start(() -> {
|
||||||
try {
|
try {
|
||||||
// 获取当前路径的全部追踪器
|
// 获取当前路径的全部追踪器
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ import cn.orionsec.kit.ext.tail.mode.FileOffsetMode;
|
|||||||
import cn.orionsec.kit.spring.SpringHolder;
|
import cn.orionsec.kit.spring.SpringHolder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.visor.common.constant.Const;
|
||||||
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
import org.dromara.visor.framework.websocket.core.utils.WebSockets;
|
||||||
import org.dromara.visor.module.asset.define.config.AppTrackerConfig;
|
import org.dromara.visor.module.asset.define.config.AppLogConfig;
|
||||||
import org.dromara.visor.module.asset.entity.dto.ExecHostLogTailDTO;
|
import org.dromara.visor.module.asset.entity.dto.ExecHostLogTailDTO;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.log.constant.LogConst;
|
import org.dromara.visor.module.asset.handler.host.exec.log.constant.LogConst;
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
@@ -45,7 +46,7 @@ import org.springframework.web.socket.WebSocketSession;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ExecLogTracker implements IExecLogTracker {
|
public class ExecLogTracker implements IExecLogTracker {
|
||||||
|
|
||||||
private static final AppTrackerConfig trackerConfig = SpringHolder.getBean(AppTrackerConfig.class);
|
private static final AppLogConfig appLogConfig = SpringHolder.getBean(AppLogConfig.class);
|
||||||
|
|
||||||
private final WebSocketSession session;
|
private final WebSocketSession session;
|
||||||
|
|
||||||
@@ -76,9 +77,9 @@ public class ExecLogTracker implements IExecLogTracker {
|
|||||||
try {
|
try {
|
||||||
this.tracker = new DelayTrackerListener(absolutePath, this);
|
this.tracker = new DelayTrackerListener(absolutePath, this);
|
||||||
tracker.charset(config.getCharset());
|
tracker.charset(config.getCharset());
|
||||||
tracker.delayMillis(trackerConfig.getDelay());
|
tracker.delayMillis(appLogConfig.getTrackerDelay());
|
||||||
tracker.offset(FileOffsetMode.LINE, trackerConfig.getOffset());
|
tracker.offset(FileOffsetMode.LINE, appLogConfig.getTrackerOffset());
|
||||||
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, trackerConfig.getWaitTimes());
|
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, Const.N_10);
|
||||||
// 开始监听文件
|
// 开始监听文件
|
||||||
tracker.run();
|
tracker.run();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -36,21 +36,13 @@ import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
|
|||||||
public interface ExecCommandService {
|
public interface ExecCommandService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量执行命令
|
* 执行命令
|
||||||
*
|
*
|
||||||
* @param request request
|
* @param request request
|
||||||
* @return result
|
* @return result
|
||||||
*/
|
*/
|
||||||
ExecLogVO execCommand(ExecCommandRequest request);
|
ExecLogVO execCommand(ExecCommandRequest request);
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量执行命令
|
|
||||||
*
|
|
||||||
* @param request request
|
|
||||||
* @return result
|
|
||||||
*/
|
|
||||||
ExecLogVO execCommandWithSource(ExecCommandExecDTO request);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新执行命令
|
* 重新执行命令
|
||||||
*
|
*
|
||||||
@@ -59,4 +51,20 @@ public interface ExecCommandService {
|
|||||||
*/
|
*/
|
||||||
ExecLogVO reExecCommand(Long logId);
|
ExecLogVO reExecCommand(Long logId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行命令
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
ExecLogVO execCommandWithSource(ExecCommandExecDTO request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建执行命令
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return result
|
||||||
|
*/
|
||||||
|
ExecLogVO createCommandWithSource(ExecCommandExecDTO request);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,26 +22,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.dromara.visor.module.asset.service.impl;
|
package org.dromara.visor.module.asset.service.impl;
|
||||||
|
|
||||||
import cn.orionsec.kit.lang.annotation.Keep;
|
|
||||||
import cn.orionsec.kit.lang.function.Functions;
|
|
||||||
import cn.orionsec.kit.lang.id.UUIds;
|
|
||||||
import cn.orionsec.kit.lang.utils.Strings;
|
import cn.orionsec.kit.lang.utils.Strings;
|
||||||
|
import cn.orionsec.kit.lang.utils.Valid;
|
||||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
import cn.orionsec.kit.spring.SpringHolder;
|
||||||
import cn.orionsec.kit.lang.utils.json.matcher.NoMatchStrategy;
|
|
||||||
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatter;
|
|
||||||
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatters;
|
|
||||||
import cn.orionsec.kit.lang.utils.time.Dates;
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.visor.common.constant.Const;
|
import org.dromara.visor.common.constant.Const;
|
||||||
import org.dromara.visor.common.constant.ErrorMessage;
|
import org.dromara.visor.common.constant.ErrorMessage;
|
||||||
import org.dromara.visor.common.constant.FileConst;
|
|
||||||
import org.dromara.visor.common.enums.EndpointDefine;
|
|
||||||
import org.dromara.visor.common.interfaces.FileClient;
|
|
||||||
import org.dromara.visor.common.security.LoginUser;
|
import org.dromara.visor.common.security.LoginUser;
|
||||||
import org.dromara.visor.common.utils.PathUtils;
|
|
||||||
import org.dromara.visor.common.utils.Valid;
|
|
||||||
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
import org.dromara.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||||
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
|
import org.dromara.visor.framework.security.core.utils.SecurityUtils;
|
||||||
import org.dromara.visor.module.asset.convert.ExecConvert;
|
import org.dromara.visor.module.asset.convert.ExecConvert;
|
||||||
@@ -54,25 +43,19 @@ import org.dromara.visor.module.asset.entity.domain.ExecHostLogDO;
|
|||||||
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
import org.dromara.visor.module.asset.entity.domain.ExecLogDO;
|
||||||
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
import org.dromara.visor.module.asset.entity.domain.HostDO;
|
||||||
import org.dromara.visor.module.asset.entity.dto.ExecCommandExecDTO;
|
import org.dromara.visor.module.asset.entity.dto.ExecCommandExecDTO;
|
||||||
import org.dromara.visor.module.asset.entity.dto.ExecParameterSchemaDTO;
|
|
||||||
import org.dromara.visor.module.asset.entity.request.exec.ExecCommandRequest;
|
import org.dromara.visor.module.asset.entity.request.exec.ExecCommandRequest;
|
||||||
import org.dromara.visor.module.asset.entity.vo.ExecHostLogVO;
|
import org.dromara.visor.module.asset.entity.vo.ExecHostLogVO;
|
||||||
import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
|
import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
|
||||||
import org.dromara.visor.module.asset.enums.*;
|
import org.dromara.visor.module.asset.enums.*;
|
||||||
import org.dromara.visor.module.asset.handler.host.config.model.HostSshConfigModel;
|
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.ExecTaskExecutors;
|
import org.dromara.visor.module.asset.handler.host.exec.command.ExecTaskExecutors;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandDTO;
|
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.command.model.ExecCommandHostDTO;
|
|
||||||
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
||||||
import org.dromara.visor.module.asset.service.ExecCommandService;
|
import org.dromara.visor.module.asset.service.ExecCommandService;
|
||||||
import org.dromara.visor.module.asset.service.HostConfigService;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,15 +69,8 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class ExecCommandServiceImpl implements ExecCommandService {
|
public class ExecCommandServiceImpl implements ExecCommandService {
|
||||||
|
|
||||||
private static final ReplacementFormatter FORMATTER = ReplacementFormatters.create("@{{ ", " }}")
|
|
||||||
.noMatchStrategy(NoMatchStrategy.KEEP);
|
|
||||||
|
|
||||||
private static final int DESC_OMIT = 60;
|
private static final int DESC_OMIT = 60;
|
||||||
|
|
||||||
@Keep
|
|
||||||
@Resource
|
|
||||||
private FileClient logsFileClient;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ExecLogDAO execLogDAO;
|
private ExecLogDAO execLogDAO;
|
||||||
|
|
||||||
@@ -104,17 +80,12 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
|||||||
@Resource
|
@Resource
|
||||||
private HostDAO hostDAO;
|
private HostDAO hostDAO;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private HostConfigService hostConfigService;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public ExecLogVO execCommand(ExecCommandRequest request) {
|
public ExecLogVO execCommand(ExecCommandRequest request) {
|
||||||
log.info("ExecService.startExecCommand start params: {}", JSON.toJSONString(request));
|
log.info("ExecService.execCommand start params: {}", JSON.toJSONString(request));
|
||||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
|
||||||
LoginUser user = Valid.notNull(SecurityUtils.getLoginUser());
|
LoginUser user = Valid.notNull(SecurityUtils.getLoginUser());
|
||||||
Long userId = user.getId();
|
Long userId = user.getId();
|
||||||
List<Long> hostIdList = request.getHostIdList();
|
List<Long> hostIdList = request.getHostIdList();
|
||||||
@@ -123,26 +94,58 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
|||||||
hostIdList.removeIf(s -> !authorizedHostIdList.contains(s));
|
hostIdList.removeIf(s -> !authorizedHostIdList.contains(s));
|
||||||
log.info("ExecService.startExecCommand host hostList: {}", hostIdList);
|
log.info("ExecService.startExecCommand host hostList: {}", hostIdList);
|
||||||
Valid.notEmpty(hostIdList, ErrorMessage.CHECK_AUTHORIZED_HOST);
|
Valid.notEmpty(hostIdList, ErrorMessage.CHECK_AUTHORIZED_HOST);
|
||||||
// 执行命令
|
// 创建命令
|
||||||
ExecCommandExecDTO execRequest = ExecConvert.MAPPER.to(request);
|
ExecCommandExecDTO execRequest = ExecConvert.MAPPER.to(request);
|
||||||
execRequest.setUserId(userId);
|
execRequest.setUserId(userId);
|
||||||
execRequest.setUsername(user.getUsername());
|
execRequest.setUsername(user.getUsername());
|
||||||
execRequest.setSource(ExecSourceEnum.BATCH.name());
|
execRequest.setSource(ExecSourceEnum.BATCH.name());
|
||||||
execRequest.setExecMode(ExecModeEnum.MANUAL.name());
|
execRequest.setExecMode(ExecModeEnum.MANUAL.name());
|
||||||
|
// 调用执行
|
||||||
return this.execCommandWithSource(execRequest);
|
return this.execCommandWithSource(execRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
public ExecLogVO reExecCommand(Long logId) {
|
||||||
|
log.info("ExecService.reExecCommand start logId: {}", logId);
|
||||||
|
// 获取执行记录
|
||||||
|
ExecLogDO execLog = execLogDAO.selectByIdSource(logId, ExecSourceEnum.BATCH.name());
|
||||||
|
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
|
||||||
|
// 获取执行主机
|
||||||
|
List<ExecHostLogDO> hostLogs = execHostLogDAO.selectByLogId(logId);
|
||||||
|
Valid.notEmpty(hostLogs, ErrorMessage.DATA_ABSENT);
|
||||||
|
List<Long> hostIdList = hostLogs.stream()
|
||||||
|
.map(ExecHostLogDO::getHostId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
// 调用创建任务
|
||||||
|
ExecCommandRequest request = ExecCommandRequest.builder()
|
||||||
|
.description(execLog.getDescription())
|
||||||
|
.timeout(execLog.getTimeout())
|
||||||
|
.scriptExec(execLog.getScriptExec())
|
||||||
|
.command(execLog.getCommand())
|
||||||
|
.parameterSchema(execLog.getParameterSchema())
|
||||||
|
.hostIdList(hostIdList)
|
||||||
|
.build();
|
||||||
|
// 调用执行
|
||||||
|
return SpringHolder.getBean(ExecCommandService.class).execCommand(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ExecLogVO execCommandWithSource(ExecCommandExecDTO request) {
|
public ExecLogVO execCommandWithSource(ExecCommandExecDTO request) {
|
||||||
log.info("ExecService.execCommandWithSource start params: {}", JSON.toJSONString(request));
|
// 上下文调用执行
|
||||||
|
ExecLogVO result = SpringHolder.getBean(ExecCommandService.class).createCommandWithSource(request);
|
||||||
|
// 执行命令
|
||||||
|
ExecTaskExecutors.start(result.getId(), Lists.map(result.getHosts(), ExecHostLogVO::getId));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
|
||||||
|
public ExecLogVO createCommandWithSource(ExecCommandExecDTO request) {
|
||||||
|
log.info("ExecService.createCommandWithSource start params: {}", JSON.toJSONString(request));
|
||||||
String command = request.getCommand();
|
String command = request.getCommand();
|
||||||
List<Long> hostIdList = request.getHostIdList();
|
List<Long> hostIdList = request.getHostIdList();
|
||||||
// 查询主机信息
|
// 查询主机信息
|
||||||
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
|
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
|
||||||
// 查询主机配置
|
|
||||||
// TODO 待优化
|
|
||||||
Map<Long, HostSshConfigModel> hostConfigMap = hostConfigService.buildHostConfigMap(hosts, HostTypeEnum.SSH);
|
|
||||||
// 插入日志
|
// 插入日志
|
||||||
ExecLogDO execLog = ExecLogDO.builder()
|
ExecLogDO execLog = ExecLogDO.builder()
|
||||||
.userId(request.getUserId())
|
.userId(request.getUserId())
|
||||||
@@ -166,17 +169,19 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
|||||||
.build();
|
.build();
|
||||||
execLogDAO.insert(execLog);
|
execLogDAO.insert(execLog);
|
||||||
Long execId = execLog.getId();
|
Long execId = execLog.getId();
|
||||||
// 获取内置参数
|
|
||||||
Map<String, Object> builtinParams = this.getBaseBuiltinParams(execId, request);
|
|
||||||
// 设置主机日志
|
// 设置主机日志
|
||||||
List<ExecHostLogDO> execHostLogs = hosts.stream()
|
List<ExecHostLogDO> execHostLogs = hosts.stream()
|
||||||
.map(s -> this.convertExecHostLog(s, execLog, hostConfigMap.get(s.getId()), builtinParams))
|
.map(s -> ExecHostLogDO.builder()
|
||||||
|
.logId(execLog.getId())
|
||||||
|
.hostId(s.getId())
|
||||||
|
.hostName(s.getName())
|
||||||
|
.hostAddress(s.getAddress())
|
||||||
|
.status(ExecHostStatusEnum.WAITING.name())
|
||||||
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
execHostLogDAO.insertBatch(execHostLogs);
|
execHostLogDAO.insertBatch(execHostLogs);
|
||||||
// 操作日志
|
// 操作日志
|
||||||
OperatorLogs.add(OperatorLogs.LOG_ID, execId);
|
OperatorLogs.add(OperatorLogs.LOG_ID, execId);
|
||||||
// 开始执行
|
|
||||||
this.startExec(execLog, execHostLogs, hostConfigMap);
|
|
||||||
// 返回
|
// 返回
|
||||||
ExecLogVO result = ExecLogConvert.MAPPER.to(execLog);
|
ExecLogVO result = ExecLogConvert.MAPPER.to(execLog);
|
||||||
List<ExecHostLogVO> resultHosts = ExecHostLogConvert.MAPPER.to(execHostLogs);
|
List<ExecHostLogVO> resultHosts = ExecHostLogConvert.MAPPER.to(execHostLogs);
|
||||||
@@ -184,218 +189,4 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public ExecLogVO reExecCommand(Long logId) {
|
|
||||||
log.info("ExecService.reExecCommand start logId: {}", logId);
|
|
||||||
// 获取执行记录
|
|
||||||
ExecLogDO execLog = execLogDAO.selectByIdSource(logId, ExecSourceEnum.BATCH.name());
|
|
||||||
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
|
|
||||||
// 获取执行主机
|
|
||||||
List<ExecHostLogDO> hostLogs = execHostLogDAO.selectByLogId(logId);
|
|
||||||
Valid.notEmpty(hostLogs, ErrorMessage.DATA_ABSENT);
|
|
||||||
List<Long> hostIdList = hostLogs.stream()
|
|
||||||
.map(ExecHostLogDO::getHostId)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
// 调用执行方法
|
|
||||||
ExecCommandRequest request = ExecCommandRequest.builder()
|
|
||||||
.description(execLog.getDescription())
|
|
||||||
.timeout(execLog.getTimeout())
|
|
||||||
.scriptExec(execLog.getScriptExec())
|
|
||||||
.command(execLog.getCommand())
|
|
||||||
.parameterSchema(execLog.getParameterSchema())
|
|
||||||
.hostIdList(hostIdList)
|
|
||||||
.build();
|
|
||||||
return this.execCommand(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开始执行命令
|
|
||||||
*
|
|
||||||
* @param execLog execLog
|
|
||||||
* @param execHostLogs hostLogs
|
|
||||||
* @param hostConfigMap hostConfigMap
|
|
||||||
*/
|
|
||||||
private void startExec(ExecLogDO execLog,
|
|
||||||
List<ExecHostLogDO> execHostLogs,
|
|
||||||
Map<Long, HostSshConfigModel> hostConfigMap) {
|
|
||||||
// 执行主机
|
|
||||||
List<ExecCommandHostDTO> hosts = execHostLogs.stream()
|
|
||||||
.map(s -> {
|
|
||||||
HostSshConfigModel config = hostConfigMap.get(s.getHostId());
|
|
||||||
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())
|
|
||||||
.build();
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
// 执行信息
|
|
||||||
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)
|
|
||||||
.build();
|
|
||||||
// 开始执行
|
|
||||||
ExecTaskExecutors.start(exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转换为 execHostLog
|
|
||||||
*
|
|
||||||
* @param host host
|
|
||||||
* @param execLog execLog
|
|
||||||
* @param config config
|
|
||||||
* @param builtinParams builtinParams
|
|
||||||
* @return execHostLog
|
|
||||||
*/
|
|
||||||
private ExecHostLogDO convertExecHostLog(HostDO host,
|
|
||||||
ExecLogDO execLog,
|
|
||||||
HostSshConfigModel config,
|
|
||||||
Map<String, Object> builtinParams) {
|
|
||||||
Long execId = execLog.getId();
|
|
||||||
Long hostId = host.getId();
|
|
||||||
// 脚本路径
|
|
||||||
String scriptPath = null;
|
|
||||||
if (ScriptExecEnum.isEnabled(execLog.getScriptExec())) {
|
|
||||||
scriptPath = this.buildScriptPath(config.getUsername(), host.getOsType(), execId, hostId);
|
|
||||||
}
|
|
||||||
// 获取参数
|
|
||||||
String parameter = JSON.toJSONString(this.getHostParams(builtinParams, host, config, scriptPath));
|
|
||||||
return ExecHostLogDO.builder()
|
|
||||||
.logId(execId)
|
|
||||||
.hostId(hostId)
|
|
||||||
.hostName(host.getName())
|
|
||||||
.hostAddress(host.getAddress())
|
|
||||||
.status(ExecHostStatusEnum.WAITING.name())
|
|
||||||
.command(FORMATTER.format(execLog.getCommand(), parameter))
|
|
||||||
.parameter(parameter)
|
|
||||||
.logPath(this.buildLogPath(execId, hostId))
|
|
||||||
.scriptPath(scriptPath)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取基础内置参数
|
|
||||||
*
|
|
||||||
* @param execId execId
|
|
||||||
* @param request request
|
|
||||||
* @return params
|
|
||||||
*/
|
|
||||||
private Map<String, Object> getBaseBuiltinParams(Long execId, ExecCommandExecDTO request) {
|
|
||||||
String uuid = UUIds.random();
|
|
||||||
Date date = new Date();
|
|
||||||
// 输入参数
|
|
||||||
Map<String, Object> params = this.extraSchemaParams(request.getParameterSchema());
|
|
||||||
// 添加内置参数
|
|
||||||
params.put("userId", request.getUserId());
|
|
||||||
params.put("username", request.getUsername());
|
|
||||||
params.put("source", request.getSource());
|
|
||||||
params.put("sourceId", request.getSourceId());
|
|
||||||
params.put("seq", request.getExecSeq());
|
|
||||||
params.put("execId", execId);
|
|
||||||
params.put("scriptExec", request.getScriptExec());
|
|
||||||
params.put("uuid", uuid);
|
|
||||||
params.put("uuidShort", uuid.replace("-", Strings.EMPTY));
|
|
||||||
params.put("timestampMillis", date.getTime());
|
|
||||||
params.put("timestamp", date.getTime() / Dates.SECOND_STAMP);
|
|
||||||
params.put("date", Dates.format(date, Dates.YMD));
|
|
||||||
params.put("datetime", Dates.format(date, Dates.YMD_HMS));
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取主机参数
|
|
||||||
*
|
|
||||||
* @param baseParams baseParams
|
|
||||||
* @param host host
|
|
||||||
* @param config config
|
|
||||||
* @param scriptPath scriptPath
|
|
||||||
* @return params
|
|
||||||
*/
|
|
||||||
private Map<String, Object> getHostParams(Map<String, Object> baseParams,
|
|
||||||
HostDO host,
|
|
||||||
HostSshConfigModel config,
|
|
||||||
String scriptPath) {
|
|
||||||
String uuid = UUIds.random();
|
|
||||||
Map<String, Object> params = Maps.newMap(baseParams);
|
|
||||||
params.put("hostId", host.getId());
|
|
||||||
params.put("hostName", host.getName());
|
|
||||||
params.put("hostCode", host.getCode());
|
|
||||||
params.put("hostAddress", host.getAddress());
|
|
||||||
params.put("hostPort", host.getPort());
|
|
||||||
params.put("hostUuid", uuid);
|
|
||||||
params.put("hostUuidShort", uuid.replace("-", Strings.EMPTY));
|
|
||||||
params.put("hostUsername", config.getUsername());
|
|
||||||
params.put("osType", host.getOsType());
|
|
||||||
params.put("charset", config.getCharset());
|
|
||||||
params.put("scriptPath", scriptPath);
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取参数
|
|
||||||
*
|
|
||||||
* @param parameterSchema parameterSchema
|
|
||||||
* @return params
|
|
||||||
*/
|
|
||||||
private Map<String, Object> extraSchemaParams(String parameterSchema) {
|
|
||||||
List<ExecParameterSchemaDTO> schemaList = JSON.parseArray(parameterSchema, ExecParameterSchemaDTO.class);
|
|
||||||
if (Lists.isEmpty(schemaList)) {
|
|
||||||
return Maps.newMap();
|
|
||||||
}
|
|
||||||
// 解析参数
|
|
||||||
return schemaList.stream()
|
|
||||||
.collect(Collectors.toMap(ExecParameterSchemaDTO::getName,
|
|
||||||
s -> {
|
|
||||||
Object value = s.getValue();
|
|
||||||
if (value == null) {
|
|
||||||
value = Const.EMPTY;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
Functions.right()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建日志路径
|
|
||||||
*
|
|
||||||
* @param logId logId
|
|
||||||
* @param hostId hostId
|
|
||||||
* @return logPath
|
|
||||||
*/
|
|
||||||
private String buildLogPath(Long logId, Long hostId) {
|
|
||||||
return logsFileClient.getReturnPath(EndpointDefine.EXEC_LOG.format(logId, hostId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 侯建脚本路径
|
|
||||||
*
|
|
||||||
* @param username username
|
|
||||||
* @param osType osType
|
|
||||||
* @param logId logId
|
|
||||||
* @param hostId hostId
|
|
||||||
* @return scriptPath
|
|
||||||
*/
|
|
||||||
private String buildScriptPath(String username, String osType, Long logId, Long hostId) {
|
|
||||||
HostOsTypeEnum os = HostOsTypeEnum.of(osType);
|
|
||||||
String name = FileConst.EXEC
|
|
||||||
+ "/" + logId
|
|
||||||
+ "/" + hostId
|
|
||||||
+ os.getScriptSuffix();
|
|
||||||
return PathUtils.buildAppPath(HostOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class ExecHostLogServiceImpl implements ExecHostLogService {
|
|||||||
.map(execTaskManager::getTask)
|
.map(execTaskManager::getTask)
|
||||||
.map(IExecTaskHandler::getHandlers)
|
.map(IExecTaskHandler::getHandlers)
|
||||||
.flatMap(s -> s.stream()
|
.flatMap(s -> s.stream()
|
||||||
.filter(h -> h.getHostId().equals(record.getHostId()))
|
.filter(h -> id.equals(h.getId()))
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.ifPresent(IExecCommandHandler::interrupt);
|
.ifPresent(IExecCommandHandler::interrupt);
|
||||||
// 删除
|
// 删除
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import cn.orionsec.kit.lang.utils.Booleans;
|
|||||||
import cn.orionsec.kit.lang.utils.Strings;
|
import cn.orionsec.kit.lang.utils.Strings;
|
||||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||||
import cn.orionsec.kit.lang.utils.time.cron.Cron;
|
import cn.orionsec.kit.lang.utils.time.cron.Cron;
|
||||||
|
import cn.orionsec.kit.spring.SpringHolder;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -49,7 +50,10 @@ import org.dromara.visor.module.asset.entity.request.exec.*;
|
|||||||
import org.dromara.visor.module.asset.entity.vo.ExecJobVO;
|
import org.dromara.visor.module.asset.entity.vo.ExecJobVO;
|
||||||
import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
|
import org.dromara.visor.module.asset.entity.vo.ExecLogVO;
|
||||||
import org.dromara.visor.module.asset.entity.vo.HostBaseVO;
|
import org.dromara.visor.module.asset.entity.vo.HostBaseVO;
|
||||||
import org.dromara.visor.module.asset.enums.*;
|
import org.dromara.visor.module.asset.enums.ExecJobStatusEnum;
|
||||||
|
import org.dromara.visor.module.asset.enums.ExecModeEnum;
|
||||||
|
import org.dromara.visor.module.asset.enums.ExecSourceEnum;
|
||||||
|
import org.dromara.visor.module.asset.enums.HostTypeEnum;
|
||||||
import org.dromara.visor.module.asset.handler.host.exec.job.ExecCommandJob;
|
import org.dromara.visor.module.asset.handler.host.exec.job.ExecCommandJob;
|
||||||
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
import org.dromara.visor.module.asset.service.AssetAuthorizedDataService;
|
||||||
import org.dromara.visor.module.asset.service.ExecCommandService;
|
import org.dromara.visor.module.asset.service.ExecCommandService;
|
||||||
@@ -57,6 +61,7 @@ import org.dromara.visor.module.asset.service.ExecJobHostService;
|
|||||||
import org.dromara.visor.module.asset.service.ExecJobService;
|
import org.dromara.visor.module.asset.service.ExecJobService;
|
||||||
import org.dromara.visor.module.infra.api.SystemUserApi;
|
import org.dromara.visor.module.infra.api.SystemUserApi;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -107,7 +112,6 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
// 验证表达式是否正确
|
// 验证表达式是否正确
|
||||||
Cron.of(request.getExpression());
|
Cron.of(request.getExpression());
|
||||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
|
||||||
// 转换
|
// 转换
|
||||||
ExecJobDO record = ExecJobConvert.MAPPER.to(request);
|
ExecJobDO record = ExecJobConvert.MAPPER.to(request);
|
||||||
// 查询数据是否冲突
|
// 查询数据是否冲突
|
||||||
@@ -137,7 +141,6 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
log.info("ExecJobService-updateExecJobById id: {}, request: {}", id, JSON.toJSONString(request));
|
log.info("ExecJobService-updateExecJobById id: {}, request: {}", id, JSON.toJSONString(request));
|
||||||
// 验证表达式是否正确
|
// 验证表达式是否正确
|
||||||
Cron.of(request.getExpression());
|
Cron.of(request.getExpression());
|
||||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
|
||||||
// 查询
|
// 查询
|
||||||
ExecJobDO record = execJobDAO.selectById(id);
|
ExecJobDO record = execJobDAO.selectById(id);
|
||||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||||
@@ -164,7 +167,6 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
public Integer updateExecJobStatus(ExecJobUpdateStatusRequest request) {
|
public Integer updateExecJobStatus(ExecJobUpdateStatusRequest request) {
|
||||||
Long id = request.getId();
|
Long id = request.getId();
|
||||||
ExecJobStatusEnum status = ExecJobStatusEnum.of(request.getStatus());
|
ExecJobStatusEnum status = ExecJobStatusEnum.of(request.getStatus());
|
||||||
Valid.notNull(status, ErrorMessage.PARAM_ERROR);
|
|
||||||
log.info("ExecJobService-updateExecJobStatus id: {}, status: {}", id, status);
|
log.info("ExecJobService-updateExecJobStatus id: {}, status: {}", id, status);
|
||||||
// 查询任务
|
// 查询任务
|
||||||
ExecJobDO record = execJobDAO.selectById(id);
|
ExecJobDO record = execJobDAO.selectById(id);
|
||||||
@@ -203,7 +205,7 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
// 设置日志参数
|
// 设置日志参数
|
||||||
OperatorLogs.add(OperatorLogs.NAME, job.getName());
|
OperatorLogs.add(OperatorLogs.NAME, job.getName());
|
||||||
OperatorLogs.add(OperatorLogs.USERNAME, username);
|
OperatorLogs.add(OperatorLogs.USERNAME, username);
|
||||||
log.info("ExecJobService-setExecJobExecUser effect: {}", effect);
|
log.info("ExecJobService-updateExecJobExecUser effect: {}", effect);
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +322,6 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public void manualTriggerExecJob(Long id) {
|
public void manualTriggerExecJob(Long id) {
|
||||||
log.info("ExecJobService.manualTriggerExecJob start id: {}", id);
|
log.info("ExecJobService.manualTriggerExecJob start id: {}", id);
|
||||||
// 查询任务
|
// 查询任务
|
||||||
@@ -336,12 +337,12 @@ public class ExecJobServiceImpl implements ExecJobService {
|
|||||||
request.setUserId(user.getId());
|
request.setUserId(user.getId());
|
||||||
request.setUsername(user.getUsername());
|
request.setUsername(user.getUsername());
|
||||||
}
|
}
|
||||||
// 触发任务
|
// 上下文触发任务
|
||||||
this.triggerExecJob(request, job);
|
SpringHolder.getBean(ExecJobService.class).triggerExecJob(request, job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
|
||||||
public void triggerExecJob(ExecJobTriggerRequest request, ExecJobDO job) {
|
public void triggerExecJob(ExecJobTriggerRequest request, ExecJobDO job) {
|
||||||
Long id = request.getId();
|
Long id = request.getId();
|
||||||
// 查询任务主机
|
// 查询任务主机
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
|||||||
log.info("ExecLogService.interruptHostExec interrupt logId: {}, hostLogId: {}", logId, hostLogId);
|
log.info("ExecLogService.interruptHostExec interrupt logId: {}, hostLogId: {}", logId, hostLogId);
|
||||||
IExecCommandHandler handler = task.getHandlers()
|
IExecCommandHandler handler = task.getHandlers()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(s -> s.getHostId().equals(hostLog.getHostId()))
|
.filter(s -> hostLogId.equals(s.getId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
// 中断
|
// 中断
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ public class TerminalServiceImpl implements TerminalService {
|
|||||||
TerminalConnectDTO conn = new TerminalConnectDTO();
|
TerminalConnectDTO conn = new TerminalConnectDTO();
|
||||||
conn.setHostId(host.getId());
|
conn.setHostId(host.getId());
|
||||||
conn.setHostName(host.getName());
|
conn.setHostName(host.getName());
|
||||||
|
conn.setHostCode(host.getCode());
|
||||||
conn.setHostAddress(host.getAddress());
|
conn.setHostAddress(host.getAddress());
|
||||||
conn.setHostPort(host.getPort());
|
conn.setHostPort(host.getPort());
|
||||||
conn.setOsType(host.getOsType());
|
conn.setOsType(host.getOsType());
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||||
|
*
|
||||||
|
* https://visor.dromara.org
|
||||||
|
* https://visor.dromara.org.cn
|
||||||
|
* https://visor.orionsec.cn
|
||||||
|
*
|
||||||
|
* Members:
|
||||||
|
* Jiahang Li - ljh1553488six@139.com - author
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.dromara.visor.module.asset.utils;
|
||||||
|
|
||||||
|
import cn.orionsec.kit.lang.function.Functions;
|
||||||
|
import cn.orionsec.kit.lang.utils.Strings;
|
||||||
|
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||||
|
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||||
|
import cn.orionsec.kit.lang.utils.json.matcher.NoMatchStrategy;
|
||||||
|
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatter;
|
||||||
|
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatters;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import org.dromara.visor.common.constant.Const;
|
||||||
|
import org.dromara.visor.module.asset.entity.dto.ExecParameterSchemaDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行工具类
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2025/1/16 17:28
|
||||||
|
*/
|
||||||
|
public class ExecUtils {
|
||||||
|
|
||||||
|
private static final ReplacementFormatter FORMATTER = ReplacementFormatters.create("@{{ ", " }}")
|
||||||
|
.noMatchStrategy(NoMatchStrategy.KEEP);
|
||||||
|
|
||||||
|
private ExecUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换命令
|
||||||
|
*
|
||||||
|
* @param command command
|
||||||
|
* @param params params
|
||||||
|
* @return command
|
||||||
|
*/
|
||||||
|
public static String format(String command, Map<String, Object> params) {
|
||||||
|
return Strings.replaceCRLF(FORMATTER.format(command, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取参数
|
||||||
|
*
|
||||||
|
* @param parameterSchema parameterSchema
|
||||||
|
* @return params
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> extraSchemaParams(String parameterSchema) {
|
||||||
|
List<ExecParameterSchemaDTO> schemaList = JSON.parseArray(parameterSchema, ExecParameterSchemaDTO.class);
|
||||||
|
if (Lists.isEmpty(schemaList)) {
|
||||||
|
return Maps.newMap();
|
||||||
|
}
|
||||||
|
// 解析参数
|
||||||
|
return schemaList.stream()
|
||||||
|
.collect(Collectors.toMap(ExecParameterSchemaDTO::getName,
|
||||||
|
s -> {
|
||||||
|
Object value = s.getValue();
|
||||||
|
if (value == null) {
|
||||||
|
value = Const.EMPTY;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
Functions.right()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user