使用脚本执行命令.

This commit is contained in:
lijiahang
2024-04-17 13:13:01 +08:00
parent 339d86fc87
commit 256e54ffd8
22 changed files with 384 additions and 79 deletions

View File

@@ -16,6 +16,7 @@
* 🔨 优化 sftp 上传文件重复处理可配置 `app.sftp`
* 🔨 优化 用户状态调整交互逻辑
* 🔨 优化 角色状态调整交互逻辑
* 🔨 优化 命令执行添加内置参数
* 🔨 删除 用户锁定状态
[如何升级](/update/v1.0.5.md)

View File

@@ -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 |

View File

@@ -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";

View File

@@ -35,6 +35,4 @@ public interface Const extends com.orion.lang.constant.Const, FieldConst, CnCons
String SYSTEM_USERNAME = "system";
String ERROR_LOG = "error.log";
}

View File

@@ -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";
}

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -29,7 +29,7 @@ public class ExecCommandDTO {
private Integer timeout;
@Schema(description = "是否使用脚本执行")
private Integer scriptExec;
private Boolean scriptExec;
@Schema(description = "主机")
private List<ExecCommandHostDTO> hosts;

View File

@@ -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;
}

View File

@@ -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 = "执行失败";
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
/**
* 更新配置
*

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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) {
// 查询原配置

View File

@@ -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'

View File

@@ -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>

View File

@@ -42,7 +42,11 @@ const columns = [
dataIndex: 'recentLog',
slotName: 'recentLog',
align: 'left',
width: 184,
headerCellStyle: {
display: 'flex',
justifyContent: 'center'
},
width: 200,
}, {
title: '修改时间',
dataIndex: 'updateTime',