diff --git a/docs/about/change-log.md b/docs/about/change-log.md index ab49b9fa..89505bae 100644 --- a/docs/about/change-log.md +++ b/docs/about/change-log.md @@ -16,6 +16,7 @@ * 🔨 优化 sftp 上传文件重复处理可配置 `app.sftp` * 🔨 优化 用户状态调整交互逻辑 * 🔨 优化 角色状态调整交互逻辑 +* 🔨 优化 命令执行添加内置参数 * 🔨 删除 用户锁定状态 [如何升级](/update/v1.0.5.md) diff --git a/docs/operator/exec.md b/docs/operator/exec.md index b79178e0..4a474498 100644 --- a/docs/operator/exec.md +++ b/docs/operator/exec.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 | diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/OrionOpsProConst.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java similarity index 77% rename from orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/OrionOpsProConst.java rename to orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java index 6e8da5f3..4b1eb8f7 100644 --- a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/OrionOpsProConst.java +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/AppConst.java @@ -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"; diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/Const.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/Const.java index 63e72e96..9aa812d8 100644 --- a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/Const.java +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/Const.java @@ -35,6 +35,4 @@ public interface Const extends com.orion.lang.constant.Const, FieldConst, CnCons String SYSTEM_USERNAME = "system"; - String ERROR_LOG = "error.log"; - } diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/PathConst.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/PathConst.java new file mode 100644 index 00000000..05f79581 --- /dev/null +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/constant/PathConst.java @@ -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"; + +} diff --git a/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/utils/PathUtils.java b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/utils/PathUtils.java new file mode 100644 index 00000000..fba5569b --- /dev/null +++ b/orion-ops-framework/orion-ops-framework-common/src/main/java/com/orion/ops/framework/common/utils/PathUtils.java @@ -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(); + } + +} diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/core/CodeGeneratorEngine.java b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/core/CodeGeneratorEngine.java index 3e6d1506..374a1385 100644 --- a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/core/CodeGeneratorEngine.java +++ b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/core/generator/core/CodeGeneratorEngine.java @@ -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 apiComment = new HashMap<>(12); String comment = tableInfo.getComment(); diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/ScriptExecEnum.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/ScriptExecEnum.java new file mode 100644 index 00000000..ab075318 --- /dev/null +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/enums/ScriptExecEnum.java @@ -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); + } + +} diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java index bd38280a..a8b4244d 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandDTO.java @@ -29,7 +29,7 @@ public class ExecCommandDTO { private Integer timeout; @Schema(description = "是否使用脚本执行") - private Integer scriptExec; + private Boolean scriptExec; @Schema(description = "主机") private List hosts; diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandHostDTO.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandHostDTO.java index f6a59365..397b0459 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandHostDTO.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/dto/ExecCommandHostDTO.java @@ -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; } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandHandler.java index 137fa8dd..864397b2 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecCommandHandler.java @@ -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 = "执行失败"; } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java index 02150b59..585c2604 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/command/handler/ExecTaskHandler.java @@ -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 hosts) throws Exception { + private void runHostCommand() throws Exception { // 超时检查 if (execCommand.getTimeout() != 0) { this.timeoutChecker = TimeoutCheckers.create(); AssetThreadPools.TIMEOUT_CHECK.execute(this.timeoutChecker); } + // 执行命令 + List 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); diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java index a2991cf9..c7dc891a 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/handler/host/exec/log/tracker/ExecLogTracker.java @@ -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; } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConfigService.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConfigService.java index 6023e69f..718df264 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConfigService.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/HostConfigService.java @@ -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 * @return config */ T getHostConfig(Long hostId, HostConfigTypeEnum type); @@ -52,6 +54,16 @@ public interface HostConfigService { */ List getHostConfigList(List hostIdList, String type); + /** + * 获取配置 + * + * @param hostIdList hostIdList + * @param type type + * @param T + * @return config + */ + Map getHostConfigMap(List hostIdList, HostConfigTypeEnum type); + /** * 更新配置 * diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecCommandServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecCommandServiceImpl.java index b3207c96..68da1e32 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecCommandServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecCommandServiceImpl.java @@ -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 hostIdList = request.getHostIdList(); // 检查主机权限 @@ -109,7 +111,10 @@ public class ExecCommandServiceImpl implements ExecCommandService { public ExecLogVO execCommandWithSource(ExecCommandExecRequest request) { String command = request.getCommand(); List hostIdList = request.getHostIdList(); + // 查询主机信息 List hosts = hostDAO.selectBatchIds(hostIdList); + // 查询主机配置 + Map hostConfigMap = hostConfigService.getHostConfigMap(hostIdList, HostConfigTypeEnum.SSH); // 插入日志 ExecLogDO execLog = ExecLogDO.builder() .userId(request.getUserId()) @@ -136,26 +141,13 @@ public class ExecCommandServiceImpl implements ExecCommandService { Map builtinsParams = this.getBaseBuiltinsParams(execId, request); // 设置主机日志 List 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 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 execHostLogs) { + private void startExec(ExecLogDO execLog, + List execHostLogs, + Map hostConfigMap) { + // 执行主机 + List 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 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 getHostParams(Map baseParams, HostDO host) { + private Map getHostParams(Map baseParams, + HostDO host, + HostSshConfigModel config, + String scriptPath) { String uuid = UUIds.random(); Map 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); + } + } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecJobServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecJobServiceImpl.java index d16c6fb6..f10e9618 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecJobServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecJobServiceImpl.java @@ -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); diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java index 8ab3f575..3e07df17 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecLogServiceImpl.java @@ -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 hostIdList = hostLogs.stream() .map(ExecHostLogDO::getHostId) .collect(Collectors.toList()); - Map configMap = hostConfigService.getHostConfigList(hostIdList, HostConfigTypeEnum.SSH.getType()) - .stream() - .collect(Collectors.toMap(HostConfigVO::getId, Function.identity())); + Map 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); } diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecTemplateServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecTemplateServiceImpl.java index f5339346..e502dc2b 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecTemplateServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/ExecTemplateServiceImpl.java @@ -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); diff --git a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConfigServiceImpl.java b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConfigServiceImpl.java index f9c2e540..654e82c4 100644 --- a/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConfigServiceImpl.java +++ b/orion-ops-module-asset/orion-ops-module-asset-service/src/main/java/com/orion/ops/module/asset/service/impl/HostConfigServiceImpl.java @@ -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 Map getHostConfigMap(List hostIdList, HostConfigTypeEnum type) { + // 查询 + List 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) { // 查询原配置 diff --git a/orion-ops-ui/src/components/view/exec-editor/const.ts b/orion-ops-ui/src/components/view/exec-editor/const.ts index 9cfb857b..3f89542c 100644 --- a/orion-ops-ui/src/components/view/exec-editor/const.ts +++ b/orion-ops-ui/src/components/view/exec-editor/const.ts @@ -18,6 +18,18 @@ export interface TemplateParam { // 内置参数 export const builtinsParams: Array = [ { + 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 = [ 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' diff --git a/orion-ops-ui/src/views/exec/exec-job/components/exec-job-table.vue b/orion-ops-ui/src/views/exec/exec-job/components/exec-job-table.vue index 43f287cf..0c65c007 100644 --- a/orion-ops-ui/src/views/exec/exec-job/components/exec-job-table.vue +++ b/orion-ops-ui/src/views/exec/exec-job/components/exec-job-table.vue @@ -115,7 +115,7 @@ {{ getDictValue(execStatusKey, record.recentLogStatus) }} - {{ dateFormat(new Date(record.recentLogTime), 'MM-dd HH:mm') }} + {{ dateFormat(new Date(record.recentLogTime), 'MM-dd HH:mm:ss') }}
-
diff --git a/orion-ops-ui/src/views/exec/exec-job/types/table.columns.ts b/orion-ops-ui/src/views/exec/exec-job/types/table.columns.ts index ebfa626d..085ad219 100644 --- a/orion-ops-ui/src/views/exec/exec-job/types/table.columns.ts +++ b/orion-ops-ui/src/views/exec/exec-job/types/table.columns.ts @@ -42,7 +42,11 @@ const columns = [ dataIndex: 'recentLog', slotName: 'recentLog', align: 'left', - width: 184, + headerCellStyle: { + display: 'flex', + justifyContent: 'center' + }, + width: 200, }, { title: '修改时间', dataIndex: 'updateTime',