✨ 使用脚本执行命令.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
* 🔨 优化 sftp 上传文件重复处理可配置 `app.sftp`
|
||||
* 🔨 优化 用户状态调整交互逻辑
|
||||
* 🔨 优化 角色状态调整交互逻辑
|
||||
* 🔨 优化 命令执行添加内置参数
|
||||
* 🔨 删除 用户锁定状态
|
||||
|
||||
[如何升级](/update/v1.0.5.md)
|
||||
|
||||
@@ -75,13 +75,19 @@
|
||||
| source | 执行来源 (BATCH/JOB) |
|
||||
| sourceId | 执行来源id (JOB特有) |
|
||||
| seq | 执行序列 (JOB特有) |
|
||||
| userId | 执行用户id |
|
||||
| username | 执行用户名 |
|
||||
| execId | 执行记录id |
|
||||
| hostId | 执行主机id |
|
||||
| hostName | 执行主机名称 |
|
||||
| hostCode | 执行主机编码 |
|
||||
| hostAddress | 执行主机地址 |
|
||||
| userId | 执行用户id |
|
||||
| username | 执行用户名 |
|
||||
| hostUsername | 执行主机用户名 |
|
||||
| osType | 执行主机系统版本 |
|
||||
| port | SSH 端口 |
|
||||
| charset | SSH 编码集 |
|
||||
| scriptExec | 是否使用脚本执行 |
|
||||
| scriptPath | 脚本文件路径 |
|
||||
| uuid | 生成任务维度 uuid |
|
||||
| uuidShort | 生成任务维度 uuid 无 '-' |
|
||||
| hostUuid | 生成机器维度 uuid |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.orion.ops.framework.common.constant;
|
||||
|
||||
import com.orion.lang.constant.OrionConst;
|
||||
|
||||
/**
|
||||
* 项目常量
|
||||
*
|
||||
@@ -7,13 +9,15 @@ package com.orion.ops.framework.common.constant;
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/19 18:56
|
||||
*/
|
||||
public interface OrionOpsProConst {
|
||||
public interface AppConst extends OrionConst {
|
||||
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "1.0.5";
|
||||
|
||||
String ORION_OPS_PRO = "orion-ops-pro";
|
||||
|
||||
String GITHUB = "https://github.com/lijiahangmax/orion-ops-pro";
|
||||
|
||||
String GITEE = "https://gitee.com/lijiahangmax/orion-ops-pro";
|
||||
@@ -35,6 +35,4 @@ public interface Const extends com.orion.lang.constant.Const, FieldConst, CnCons
|
||||
|
||||
String SYSTEM_USERNAME = "system";
|
||||
|
||||
String ERROR_LOG = "error.log";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.orion.ops.framework.common.constant;
|
||||
|
||||
/**
|
||||
* 路径常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/4/17 10:35
|
||||
*/
|
||||
public interface PathConst {
|
||||
|
||||
String ERROR_LOG = "error.log";
|
||||
|
||||
String EXEC = "exec";
|
||||
|
||||
String SCRIPT = "script";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.orion.ops.framework.common.utils;
|
||||
|
||||
import com.orion.lang.utils.Objects1;
|
||||
import com.orion.ops.framework.common.constant.AppConst;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
|
||||
/**
|
||||
* 路径工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/4/17 10:28
|
||||
*/
|
||||
public class PathUtils {
|
||||
|
||||
private PathUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户根目录
|
||||
*
|
||||
* @param username 用户名
|
||||
* @return 用户目录
|
||||
*/
|
||||
public static String getHomePath(String username) {
|
||||
if (Const.ROOT.equals(username)) {
|
||||
return "/" + Const.ROOT;
|
||||
} else {
|
||||
return "/home/" + username;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用路径
|
||||
*
|
||||
* @param username username
|
||||
* @return path
|
||||
*/
|
||||
public static String getAppPath(String username) {
|
||||
return getHomePath(username)
|
||||
+ "/" + AppConst.ORION
|
||||
+ "/" + AppConst.ORION_OPS_PRO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建应用路径
|
||||
*
|
||||
* @param username username
|
||||
* @param paths paths
|
||||
* @return path
|
||||
*/
|
||||
public static String buildAppPath(String username, Object... paths) {
|
||||
StringBuilder path = new StringBuilder(getAppPath(username));
|
||||
for (Object o : paths) {
|
||||
path.append("/").append(Objects1.toString(o));
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.lang.utils.reflect.BeanMap;
|
||||
import com.orion.lang.utils.reflect.Fields;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.OrionOpsProConst;
|
||||
import com.orion.ops.framework.common.constant.AppConst;
|
||||
import com.orion.ops.framework.mybatis.core.generator.template.Table;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -97,7 +97,7 @@ public class CodeGeneratorEngine extends VelocityTemplateEngine {
|
||||
// http 注释标识
|
||||
objectMap.put("httpComment", "###");
|
||||
// 版本
|
||||
objectMap.put("version", OrionOpsProConst.VERSION);
|
||||
objectMap.put("version", AppConst.VERSION);
|
||||
// api 注释
|
||||
Map<String, String> apiComment = new HashMap<>(12);
|
||||
String comment = tableInfo.getComment();
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.orion.ops.module.asset.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 脚本执行枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/4/17 10:17
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ScriptExecEnum {
|
||||
|
||||
/**
|
||||
* 不使用
|
||||
*/
|
||||
DISABLED(0),
|
||||
|
||||
/**
|
||||
* 使用
|
||||
*/
|
||||
ENABLED(1),
|
||||
|
||||
;
|
||||
|
||||
private final Integer value;
|
||||
|
||||
public static ScriptExecEnum of(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (ScriptExecEnum val : values()) {
|
||||
if (val.value.equals(value)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否启用
|
||||
*
|
||||
* @param value value
|
||||
* @return 是否启用
|
||||
*/
|
||||
public static boolean isEnabled(Integer value) {
|
||||
return ENABLED.value.equals(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class ExecCommandDTO {
|
||||
private Integer timeout;
|
||||
|
||||
@Schema(description = "是否使用脚本执行")
|
||||
private Integer scriptExec;
|
||||
private Boolean scriptExec;
|
||||
|
||||
@Schema(description = "主机")
|
||||
private List<ExecCommandHostDTO> hosts;
|
||||
|
||||
@@ -35,7 +35,13 @@ public class ExecCommandHostDTO {
|
||||
@Schema(description = "执行命令")
|
||||
private String command;
|
||||
|
||||
@Schema(description = "超时时间")
|
||||
private Integer timeout;
|
||||
@Schema(description = "命令编码")
|
||||
private String charset;
|
||||
|
||||
@Schema(description = "文件名称编码")
|
||||
private String fileNameCharset;
|
||||
|
||||
@Schema(description = "文件内容编码")
|
||||
private String fileContentCharset;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,16 +3,21 @@ package com.orion.ops.module.asset.handler.host.exec.command.handler;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.exception.AuthenticationException;
|
||||
import com.orion.lang.exception.ConnectionRuntimeException;
|
||||
import com.orion.lang.exception.SftpException;
|
||||
import com.orion.lang.exception.argument.InvalidArgumentException;
|
||||
import com.orion.lang.support.timeout.TimeoutChecker;
|
||||
import com.orion.lang.utils.Booleans;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.net.host.sftp.SftpExecutor;
|
||||
import com.orion.net.host.ssh.command.CommandExecutor;
|
||||
import com.orion.ops.framework.common.file.FileClient;
|
||||
import com.orion.ops.module.asset.dao.ExecHostLogDAO;
|
||||
import com.orion.ops.module.asset.entity.domain.ExecHostLogDO;
|
||||
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;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.manager.ExecLogManager;
|
||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||
@@ -43,6 +48,8 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
|
||||
private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
|
||||
|
||||
private final ExecCommandDTO execCommand;
|
||||
|
||||
private final ExecCommandHostDTO execHostCommand;
|
||||
|
||||
private final TimeoutChecker timeoutChecker;
|
||||
@@ -60,8 +67,11 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
|
||||
private volatile boolean interrupted;
|
||||
|
||||
public ExecCommandHandler(ExecCommandHostDTO execHostCommand, TimeoutChecker timeoutChecker) {
|
||||
public ExecCommandHandler(ExecCommandDTO execCommand,
|
||||
ExecCommandHostDTO execHostCommand,
|
||||
TimeoutChecker timeoutChecker) {
|
||||
this.status = ExecHostStatusEnum.WAITING;
|
||||
this.execCommand = execCommand;
|
||||
this.execHostCommand = execHostCommand;
|
||||
this.timeoutChecker = timeoutChecker;
|
||||
}
|
||||
@@ -109,15 +119,45 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
this.logOutputStream = fileClient.getContentOutputStream(execHostCommand.getLogPath());
|
||||
// 打开会话
|
||||
this.sessionStore = hostTerminalService.openSessionStore(execHostCommand.getHostId());
|
||||
this.executor = sessionStore.getCommandExecutor(Strings.replaceCRLF(execHostCommand.getCommand()));
|
||||
if (Booleans.isTrue(execCommand.getScriptExec())) {
|
||||
// 上传脚本文件
|
||||
this.uploadScriptFile();
|
||||
// 执行脚本文件
|
||||
this.executor = sessionStore.getCommandExecutor(execHostCommand.getScriptPath());
|
||||
} else {
|
||||
// 执行命令
|
||||
byte[] command = Strings.replaceCRLF(execHostCommand.getCommand()).getBytes(execHostCommand.getCharset());
|
||||
this.executor = sessionStore.getCommandExecutor(command);
|
||||
}
|
||||
// 执行命令
|
||||
executor.timeout(execHostCommand.getTimeout(), TimeUnit.SECONDS, timeoutChecker);
|
||||
executor.timeout(execCommand.getTimeout(), TimeUnit.SECONDS, timeoutChecker);
|
||||
executor.merge();
|
||||
executor.transfer(logOutputStream);
|
||||
executor.connect();
|
||||
executor.exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传脚本文件
|
||||
*/
|
||||
private void uploadScriptFile() {
|
||||
SftpExecutor sftpExecutor = null;
|
||||
try {
|
||||
// 打开 sftp
|
||||
sftpExecutor = sessionStore.getSftpExecutor(execHostCommand.getFileNameCharset());
|
||||
sftpExecutor.connect();
|
||||
// 上传文件
|
||||
byte[] command = Strings.replaceCRLF(execHostCommand.getCommand()).getBytes(execHostCommand.getFileContentCharset());
|
||||
sftpExecutor.write(execHostCommand.getScriptPath(), command);
|
||||
// 修改权限
|
||||
sftpExecutor.changeMode(execHostCommand.getScriptPath(), 777);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.sftp(e);
|
||||
} finally {
|
||||
Streams.close(sftpExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
*
|
||||
@@ -199,6 +239,8 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
message = "连接失败";
|
||||
} else if (ex instanceof AuthenticationException) {
|
||||
message = "认证失败";
|
||||
} else if (ex instanceof SftpException) {
|
||||
message = "脚本上传失败";
|
||||
} else {
|
||||
message = "执行失败";
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
this.updateStatus(ExecStatusEnum.RUNNING);
|
||||
try {
|
||||
// 执行命令
|
||||
this.runHostCommand(execCommand.getHosts());
|
||||
this.runHostCommand();
|
||||
// 更新状态-执行完成
|
||||
log.info("ExecTaskHandler.run completed id: {}", id);
|
||||
this.updateStatus(ExecStatusEnum.COMPLETED);
|
||||
@@ -81,23 +81,24 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
/**
|
||||
* 执行主机命令
|
||||
*
|
||||
* @param hosts hosts
|
||||
* @throws Exception Exception
|
||||
*/
|
||||
private void runHostCommand(List<ExecCommandHostDTO> hosts) throws Exception {
|
||||
private void runHostCommand() throws Exception {
|
||||
// 超时检查
|
||||
if (execCommand.getTimeout() != 0) {
|
||||
this.timeoutChecker = TimeoutCheckers.create();
|
||||
AssetThreadPools.TIMEOUT_CHECK.execute(this.timeoutChecker);
|
||||
}
|
||||
// 执行命令
|
||||
List<ExecCommandHostDTO> hosts = execCommand.getHosts();
|
||||
if (hosts.size() == 1) {
|
||||
// 单个主机直接执行
|
||||
ExecCommandHandler handler = new ExecCommandHandler(hosts.get(0), timeoutChecker);
|
||||
ExecCommandHandler handler = new ExecCommandHandler(execCommand, hosts.get(0), timeoutChecker);
|
||||
handlers.add(handler);
|
||||
handler.run();
|
||||
} else {
|
||||
hosts.stream()
|
||||
.map(s -> new ExecCommandHandler(s, timeoutChecker))
|
||||
.map(s -> new ExecCommandHandler(execCommand, s, timeoutChecker))
|
||||
.forEach(handlers::add);
|
||||
// 多个主机异步阻塞执行
|
||||
Threads.blockRun(handlers, AssetThreadPools.EXEC_HOST);
|
||||
|
||||
@@ -84,7 +84,7 @@ public class ExecLogTracker implements IExecLogTracker {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
log.info("ExecLogTracker.close path: {}", absolutePath);
|
||||
log.info("ExecLogTracker.close path: {}, closed: {}", absolutePath, close);
|
||||
if (close) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.orion.ops.module.asset.entity.vo.HostConfigVO;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 主机配置 服务类
|
||||
@@ -31,6 +32,7 @@ public interface HostConfigService {
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param type type
|
||||
* @param <T> T
|
||||
* @return config
|
||||
*/
|
||||
<T extends GenericsDataModel> T getHostConfig(Long hostId, HostConfigTypeEnum type);
|
||||
@@ -52,6 +54,16 @@ public interface HostConfigService {
|
||||
*/
|
||||
List<HostConfigVO> getHostConfigList(List<Long> hostIdList, String type);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param hostIdList hostIdList
|
||||
* @param type type
|
||||
* @param <T> T
|
||||
* @return config
|
||||
*/
|
||||
<T extends GenericsDataModel> Map<Long, T> getHostConfigMap(List<Long> hostIdList, HostConfigTypeEnum type);
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*
|
||||
|
||||
@@ -13,8 +13,10 @@ import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.constant.PathConst;
|
||||
import com.orion.ops.framework.common.file.FileClient;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.framework.common.utils.PathUtils;
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.ops.module.asset.convert.ExecConvert;
|
||||
@@ -31,15 +33,14 @@ import com.orion.ops.module.asset.entity.request.exec.ExecCommandExecRequest;
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecCommandRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecHostLogVO;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
|
||||
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
|
||||
import com.orion.ops.module.asset.enums.ExecSourceEnum;
|
||||
import com.orion.ops.module.asset.enums.ExecStatusEnum;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
import com.orion.ops.module.asset.enums.*;
|
||||
import com.orion.ops.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.ExecTaskExecutors;
|
||||
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 com.orion.ops.module.asset.service.AssetAuthorizedDataService;
|
||||
import com.orion.ops.module.asset.service.ExecCommandService;
|
||||
import com.orion.ops.module.asset.service.HostConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -48,7 +49,6 @@ import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -80,15 +80,17 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
private HostDAO hostDAO;
|
||||
|
||||
@Resource
|
||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||
private HostConfigService hostConfigService;
|
||||
|
||||
// TODO 新增 修改 时候先检查 scriptExec, 然后测试一下
|
||||
@Resource
|
||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ExecLogVO execCommand(ExecCommandRequest request) {
|
||||
log.info("ExecService.startExecCommand start params: {}", JSON.toJSONString(request));
|
||||
LoginUser user = Objects.requireNonNull(SecurityUtils.getLoginUser());
|
||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
||||
LoginUser user = Valid.notNull(SecurityUtils.getLoginUser());
|
||||
Long userId = user.getId();
|
||||
List<Long> hostIdList = request.getHostIdList();
|
||||
// 检查主机权限
|
||||
@@ -109,7 +111,10 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
public ExecLogVO execCommandWithSource(ExecCommandExecRequest request) {
|
||||
String command = request.getCommand();
|
||||
List<Long> hostIdList = request.getHostIdList();
|
||||
// 查询主机信息
|
||||
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
|
||||
// 查询主机配置
|
||||
Map<Long, HostSshConfigModel> hostConfigMap = hostConfigService.getHostConfigMap(hostIdList, HostConfigTypeEnum.SSH);
|
||||
// 插入日志
|
||||
ExecLogDO execLog = ExecLogDO.builder()
|
||||
.userId(request.getUserId())
|
||||
@@ -136,26 +141,13 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(execId, request);
|
||||
// 设置主机日志
|
||||
List<ExecHostLogDO> execHostLogs = hosts.stream()
|
||||
.map(s -> {
|
||||
String parameter = JSON.toJSONString(this.getHostParams(builtinsParams, s));
|
||||
return ExecHostLogDO.builder()
|
||||
.logId(execId)
|
||||
.hostId(s.getId())
|
||||
.hostName(s.getName())
|
||||
.hostAddress(s.getAddress())
|
||||
.status(ExecHostStatusEnum.WAITING.name())
|
||||
.command(FORMATTER.format(command, parameter))
|
||||
.parameter(parameter)
|
||||
.logPath(this.buildLogPath(execId, s.getId()))
|
||||
// FIXME 测试 添加用户名
|
||||
// .scriptPath()
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
.map(s -> this.convertExecHostLog(s, execLog, hostConfigMap.get(s.getId()), builtinsParams))
|
||||
.collect(Collectors.toList());
|
||||
execHostLogDAO.insertBatch(execHostLogs);
|
||||
// 操作日志
|
||||
OperatorLogs.add(OperatorLogs.LOG_ID, execId);
|
||||
// 开始执行
|
||||
this.startExec(execLog, execHostLogs);
|
||||
this.startExec(execLog, execHostLogs, hostConfigMap);
|
||||
// 返回
|
||||
ExecLogVO result = ExecLogConvert.MAPPER.to(execLog);
|
||||
List<ExecHostLogVO> resultHosts = ExecHostLogConvert.MAPPER.to(execHostLogs);
|
||||
@@ -191,28 +183,74 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
/**
|
||||
* 开始执行命令
|
||||
*
|
||||
* @param execLog execLog
|
||||
* @param execHostLogs hostLogs
|
||||
* @param execLog execLog
|
||||
* @param execHostLogs hostLogs
|
||||
* @param hostConfigMap hostConfigMap
|
||||
*/
|
||||
private void startExec(ExecLogDO execLog, List<ExecHostLogDO> execHostLogs) {
|
||||
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())
|
||||
.command(s.getCommand())
|
||||
.logPath(s.getLogPath())
|
||||
.scriptPath(s.getScriptPath())
|
||||
.charset(config.getCharset())
|
||||
.fileNameCharset(config.getFileNameCharset())
|
||||
.fileContentCharset(config.getFileContentCharset())
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
// 执行信息
|
||||
ExecCommandDTO exec = ExecCommandDTO.builder()
|
||||
.logId(execLog.getId())
|
||||
.timeout(execLog.getTimeout())
|
||||
.scriptExec(execLog.getScriptExec())
|
||||
.hosts(execHostLogs.stream()
|
||||
.map(s -> ExecCommandHostDTO.builder()
|
||||
.hostId(s.getHostId())
|
||||
.hostLogId(s.getId())
|
||||
.command(s.getCommand())
|
||||
.timeout(execLog.getTimeout())
|
||||
.logPath(s.getLogPath())
|
||||
.scriptPath(s.getScriptPath())
|
||||
.build())
|
||||
.collect(Collectors.toList()))
|
||||
.scriptExec(ScriptExecEnum.isEnabled(execLog.getScriptExec()))
|
||||
.hosts(hosts)
|
||||
.build();
|
||||
// 开始执行
|
||||
ExecTaskExecutors.start(exec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 execHostLog
|
||||
*
|
||||
* @param host host
|
||||
* @param execLog execLog
|
||||
* @param config config
|
||||
* @param builtinsParams builtinsParams
|
||||
* @return execHostLog
|
||||
*/
|
||||
private ExecHostLogDO convertExecHostLog(HostDO host,
|
||||
ExecLogDO execLog,
|
||||
HostSshConfigModel config,
|
||||
Map<String, Object> builtinsParams) {
|
||||
Long execId = execLog.getId();
|
||||
Long hostId = host.getId();
|
||||
// 脚本路径
|
||||
String scriptPath = null;
|
||||
if (ScriptExecEnum.isEnabled(execLog.getScriptExec())) {
|
||||
scriptPath = this.buildScriptPath(config.getUsername(), config.getOsType(), execId, hostId);
|
||||
}
|
||||
// 获取参数
|
||||
String parameter = JSON.toJSONString(this.getHostParams(builtinsParams, 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础内置参数
|
||||
*
|
||||
@@ -232,13 +270,13 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
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));
|
||||
// TODO script Exec
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -247,9 +285,14 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
*
|
||||
* @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) {
|
||||
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());
|
||||
@@ -258,8 +301,11 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
params.put("hostAddress", host.getAddress());
|
||||
params.put("hostUuid", uuid);
|
||||
params.put("hostUuidShort", uuid.replace("-", Strings.EMPTY));
|
||||
// TODO host username
|
||||
// TODO scriptPath
|
||||
params.put("hostUsername", config.getUsername());
|
||||
params.put("osType", config.getOsType());
|
||||
params.put("port", config.getPort());
|
||||
params.put("charset", config.getCharset());
|
||||
params.put("scriptPath", scriptPath);
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -289,8 +335,25 @@ public class ExecCommandServiceImpl implements ExecCommandService {
|
||||
* @return logPath
|
||||
*/
|
||||
private String buildLogPath(Long logId, Long hostId) {
|
||||
String logFile = "/exec/" + logId + "/" + logId + "_" + hostId + ".log";
|
||||
String logFile = "/" + PathConst.EXEC + "/" + logId + "/" + logId + "_" + hostId + ".log";
|
||||
return logsFileClient.getReturnPath(logFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 侯建脚本路径
|
||||
*
|
||||
* @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) {
|
||||
String name = PathConst.EXEC
|
||||
+ "_" + logId
|
||||
+ "_" + hostId
|
||||
+ HostSshOsTypeEnum.of(osType).getScriptSuffix();
|
||||
return PathUtils.buildAppPath(username, PathConst.SCRIPT, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.orion.ops.module.asset.entity.vo.ExecLogVO;
|
||||
import com.orion.ops.module.asset.enums.ExecJobStatusEnum;
|
||||
import com.orion.ops.module.asset.enums.ExecSourceEnum;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
import com.orion.ops.module.asset.enums.ScriptExecEnum;
|
||||
import com.orion.ops.module.asset.handler.host.exec.job.ExecCommandJob;
|
||||
import com.orion.ops.module.asset.service.AssetAuthorizedDataService;
|
||||
import com.orion.ops.module.asset.service.ExecCommandService;
|
||||
@@ -79,6 +80,7 @@ public class ExecJobServiceImpl implements ExecJobService {
|
||||
log.info("ExecJobService-createExecJob request: {}", JSON.toJSONString(request));
|
||||
// 验证表达式是否正确
|
||||
Cron.of(request.getExpression());
|
||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
||||
// 转换
|
||||
ExecJobDO record = ExecJobConvert.MAPPER.to(request);
|
||||
// 查询数据是否冲突
|
||||
@@ -104,6 +106,7 @@ public class ExecJobServiceImpl implements ExecJobService {
|
||||
log.info("ExecJobService-updateExecJobById id: {}, request: {}", id, JSON.toJSONString(request));
|
||||
// 验证表达式是否正确
|
||||
Cron.of(request.getExpression());
|
||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
||||
// 查询
|
||||
ExecJobDO record = execJobDAO.selectById(id);
|
||||
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.constant.FieldConst;
|
||||
import com.orion.ops.framework.common.constant.PathConst;
|
||||
import com.orion.ops.framework.common.file.FileClient;
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisStrings;
|
||||
@@ -32,10 +32,10 @@ import com.orion.ops.module.asset.entity.request.exec.ExecLogTailRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecHostLogVO;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecLogStatusVO;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
|
||||
import com.orion.ops.module.asset.entity.vo.HostConfigVO;
|
||||
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
|
||||
import com.orion.ops.module.asset.enums.ExecStatusEnum;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.handler.IExecCommandHandler;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.handler.IExecTaskHandler;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.manager.ExecTaskManager;
|
||||
@@ -51,7 +51,6 @@ import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -365,9 +364,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
List<Long> hostIdList = hostLogs.stream()
|
||||
.map(ExecHostLogDO::getHostId)
|
||||
.collect(Collectors.toList());
|
||||
Map<Long, HostConfigVO> configMap = hostConfigService.getHostConfigList(hostIdList, HostConfigTypeEnum.SSH.getType())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(HostConfigVO::getId, Function.identity()));
|
||||
Map<Long, HostSshConfigModel> configMap = hostConfigService.getHostConfigMap(hostIdList, HostConfigTypeEnum.SSH);
|
||||
// 生成缓存
|
||||
String token = UUIds.random19();
|
||||
String cacheKey = ExecCacheKeyDefine.EXEC_TAIL.format(token);
|
||||
@@ -381,8 +378,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
.hostId(s.getHostId())
|
||||
.path(s.getLogPath())
|
||||
.charset(Optional.ofNullable(configMap.get(s.getHostId()))
|
||||
.map(HostConfigVO::getConfig)
|
||||
.map(c -> c.get(FieldConst.CHARSET))
|
||||
.map(HostSshConfigModel::getCharset)
|
||||
.map(Objects1::toString)
|
||||
.orElse(Const.UTF_8))
|
||||
.build())
|
||||
@@ -433,7 +429,7 @@ public class ExecLogServiceImpl implements ExecLogService {
|
||||
}
|
||||
// 响应错误信息
|
||||
try {
|
||||
Servlets.transfer(response, Strings.bytes(errorMessage), Const.ERROR_LOG);
|
||||
Servlets.transfer(response, Strings.bytes(errorMessage), PathConst.ERROR_LOG);
|
||||
} catch (Exception ex) {
|
||||
log.error("ExecLogService.downloadLogFile transfer-error id: {}", id, ex);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.orion.ops.module.asset.entity.request.exec.ExecTemplateCreateRequest;
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateUpdateRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecTemplateVO;
|
||||
import com.orion.ops.module.asset.enums.ScriptExecEnum;
|
||||
import com.orion.ops.module.asset.service.ExecTemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -35,6 +36,7 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
||||
@Override
|
||||
public Long createExecTemplate(ExecTemplateCreateRequest request) {
|
||||
log.info("ExecTemplateService-createExecTemplate request: {}", JSON.toJSONString(request));
|
||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
||||
// 转换
|
||||
ExecTemplateDO record = ExecTemplateConvert.MAPPER.to(request);
|
||||
// 查询数据是否冲突
|
||||
@@ -49,6 +51,7 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
||||
@Override
|
||||
public Integer updateExecTemplateById(ExecTemplateUpdateRequest request) {
|
||||
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
|
||||
Valid.valid(ScriptExecEnum::of, request.getScriptExec());
|
||||
log.info("ExecTemplateService-updateExecTemplateById id: {}, request: {}", id, JSON.toJSONString(request));
|
||||
// 查询
|
||||
ExecTemplateDO record = execTemplateDAO.selectById(id);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.ops.module.asset.service.impl;
|
||||
|
||||
import com.orion.lang.function.Functions;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
@@ -92,6 +93,18 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GenericsDataModel> Map<Long, T> getHostConfigMap(List<Long> hostIdList, HostConfigTypeEnum type) {
|
||||
// 查询
|
||||
List<HostConfigDO> configs = hostConfigDAO.getHostConfigByHostIdList(hostIdList, type.getType());
|
||||
// 返回
|
||||
return configs.stream()
|
||||
.collect(Collectors.toMap(
|
||||
HostConfigDO::getHostId,
|
||||
s -> type.parse(s.getConfig()),
|
||||
Functions.right()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer updateHostConfig(HostConfigUpdateRequest request) {
|
||||
// 查询原配置
|
||||
|
||||
@@ -18,6 +18,18 @@ export interface TemplateParam {
|
||||
// 内置参数
|
||||
export const builtinsParams: Array<TemplateParam> = [
|
||||
{
|
||||
name: 'source',
|
||||
desc: '执行来源'
|
||||
}, {
|
||||
name: 'userId',
|
||||
desc: '执行用户id'
|
||||
}, {
|
||||
name: 'username',
|
||||
desc: '执行用户名'
|
||||
}, {
|
||||
name: 'execId',
|
||||
desc: '执行记录id'
|
||||
}, {
|
||||
name: 'hostId',
|
||||
desc: '执行主机id'
|
||||
}, {
|
||||
@@ -30,14 +42,23 @@ export const builtinsParams: Array<TemplateParam> = [
|
||||
name: 'hostAddress',
|
||||
desc: '执行主机地址'
|
||||
}, {
|
||||
name: 'userId',
|
||||
desc: '执行用户id'
|
||||
name: 'hostUsername',
|
||||
desc: '执行主机用户名'
|
||||
}, {
|
||||
name: 'username',
|
||||
desc: '执行用户名'
|
||||
name: 'osType',
|
||||
desc: '执行主机系统版本'
|
||||
}, {
|
||||
name: 'execId',
|
||||
desc: '执行记录id'
|
||||
name: 'port',
|
||||
desc: 'SSH 端口'
|
||||
}, {
|
||||
name: 'charset',
|
||||
desc: 'SSH 编码集'
|
||||
}, {
|
||||
name: 'scriptExec',
|
||||
desc: '是否使用脚本执行'
|
||||
}, {
|
||||
name: 'scriptPath',
|
||||
desc: '脚本文件路径'
|
||||
}, {
|
||||
name: 'uuid',
|
||||
desc: '生成任务维度 uuid'
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
{{ getDictValue(execStatusKey, record.recentLogStatus) }}
|
||||
</a-tag>
|
||||
<!-- 执行时间 -->
|
||||
{{ dateFormat(new Date(record.recentLogTime), 'MM-dd HH:mm') }}
|
||||
{{ dateFormat(new Date(record.recentLogTime), 'MM-dd HH:mm:ss') }}
|
||||
</div>
|
||||
<!-- 无任务 -->
|
||||
<div v-else class="mx8">-</div>
|
||||
|
||||
@@ -42,7 +42,11 @@ const columns = [
|
||||
dataIndex: 'recentLog',
|
||||
slotName: 'recentLog',
|
||||
align: 'left',
|
||||
width: 184,
|
||||
headerCellStyle: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
width: 200,
|
||||
}, {
|
||||
title: '修改时间',
|
||||
dataIndex: 'updateTime',
|
||||
|
||||
Reference in New Issue
Block a user