🔨 定时执行.

This commit is contained in:
lijiahang
2024-04-09 18:41:07 +08:00
parent 1771f05b65
commit 0934703509
11 changed files with 249 additions and 79 deletions

View File

@@ -1,16 +1,14 @@
package com.orion.ops.module.asset.controller;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.common.validator.group.Page;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.asset.define.operator.ExecJobOperatorType;
import com.orion.ops.module.asset.entity.request.exec.ExecJobCreateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobQueryRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateStatusRequest;
import com.orion.ops.module.asset.entity.request.exec.*;
import com.orion.ops.module.asset.entity.vo.ExecJobVO;
import com.orion.ops.module.asset.service.ExecJobService;
import io.swagger.v3.oas.annotations.Operation;
@@ -92,5 +90,14 @@ public class ExecJobController {
return execJobService.deleteExecJobById(id);
}
@OperatorLog(ExecJobOperatorType.TRIGGER)
@PostMapping("/trigger")
@Operation(summary = "手动触发计划任务")
@PreAuthorize("@ss.hasPermission('asset:exec-job:trigger')")
public HttpWrapper<?> triggerExecJob(@Validated @RequestBody ExecJobTriggerRequest request) {
execJobService.triggerExecJob(request);
return HttpWrapper.ok();
}
}

View File

@@ -0,0 +1,22 @@
package com.orion.ops.module.asset.convert;
import com.orion.ops.module.asset.entity.request.exec.ExecCommandExecRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecCommandRequest;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 批量执行 内部对象转换器
*
* @author Jiahang Li
* @version 1.0.3
* @since 2024-3-28 12:03
*/
@Mapper
public interface ExecConvert {
ExecConvert MAPPER = Mappers.getMapper(ExecConvert.class);
ExecCommandExecRequest to(ExecCommandRequest request);
}

View File

@@ -22,7 +22,7 @@ public class ExecJobOperatorType extends InitializingOperatorTypes {
public static final String UPDATE_STATUS = "exec-job:update-status";
public static final String EXEC = "exec-job:exec";
public static final String TRIGGER = "exec-job:trigger";
public static final String DELETE = "exec-job:delete";
@@ -32,7 +32,7 @@ public class ExecJobOperatorType extends InitializingOperatorTypes {
new OperatorType(L, CREATE, "创建计划任务 <sb>${name}</sb>"),
new OperatorType(M, UPDATE, "更新计划任务 <sb>${before}</sb>"),
new OperatorType(M, UPDATE_STATUS, "<sb>${statusName}</sb>计划任务 <sb>${name}</sb>"),
new OperatorType(M, EXEC, "手动执行计划任务 <sb>${name}</sb>"),
new OperatorType(M, TRIGGER, "手动触发计划任务 <sb>${name}</sb>"),
new OperatorType(H, DELETE, "删除计划任务 <sb>${name}</sb>"),
};
}

View File

@@ -0,0 +1,55 @@
package com.orion.ops.module.asset.entity.request.exec;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 批量执行命令 请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/11 11:46
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecCommandExecRequest", description = "批量执行命令 请求对象")
public class ExecCommandExecRequest {
@Schema(description = "执行用户id")
private Long userId;
@Schema(description = "执行用户名")
private String username;
@Schema(description = "执行来源")
private String source;
@Schema(description = "来源id")
private Long sourceId;
@Schema(description = "执行序列")
private Integer execSeq;
@Schema(description = "执行描述")
private String description;
@Schema(description = "超时时间")
private Integer timeout;
@Schema(description = "执行命令")
private String command;
@Schema(description = "参数 schema")
private String parameterSchema;
@Schema(description = "执行主机")
private List<Long> hostIdList;
}

View File

@@ -0,0 +1,32 @@
package com.orion.ops.module.asset.entity.request.exec;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 计划执行任务 手动触发请求对象
*
* @author Jiahang Li
* @version 1.0.3
* @since 2024-3-28 12:03
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "ExecJobCreateRequest", description = "计划执行任务 手动触发请求对象")
public class ExecJobTriggerRequest implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Schema(description = "id")
private Long id;
}

View File

@@ -88,7 +88,6 @@ public class ExecTaskHandler implements IExecTaskHandler {
private void runHostCommand(List<ExecCommandHostDTO> hosts) throws Exception {
// 超时检查
if (execCommand.getTimeout() != 0) {
// TODO test
this.timeoutChecker = TimeoutCheckers.create();
AssetThreadPools.TIMEOUT_CHECK.execute(this.timeoutChecker);
}

View File

@@ -0,0 +1,28 @@
package com.orion.ops.module.asset.handler.host.exec.job;
import com.orion.ops.framework.common.constant.FieldConst;
import com.orion.ops.module.asset.service.ExecJobService;
import com.orion.spring.SpringHolder;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
/**
* 计划执行命令任务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/9 18:33
*/
@Slf4j
public class ExecCommandJob implements Job {
private static final ExecJobService execJobService = SpringHolder.getBean(ExecJobService.class);
@Override
public void execute(JobExecutionContext context) {
long jobId = context.getMergedJobDataMap().getLong(FieldConst.KEY);
// TODO
}
}

View File

@@ -1,10 +1,7 @@
package com.orion.ops.module.asset.service;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.module.asset.entity.request.exec.ExecJobCreateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobQueryRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateStatusRequest;
import com.orion.ops.module.asset.entity.request.exec.*;
import com.orion.ops.module.asset.entity.vo.ExecJobVO;
/**
@@ -72,4 +69,11 @@ public interface ExecJobService {
*/
Integer deleteExecJobById(Long id);
/**
* 手动触发任务
*
* @param request request
*/
void triggerExecJob(ExecJobTriggerRequest request);
}

View File

@@ -1,6 +1,7 @@
package com.orion.ops.module.asset.service;
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
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.request.exec.ExecLogTailRequest;
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
@@ -25,12 +26,20 @@ public interface ExecService {
ExecLogVO execCommand(ExecCommandRequest request);
/**
* 重新执行命令
* 批量执行命令
*
* @param id id
* @param request request
* @return result
*/
ExecLogVO reExecCommand(Long id);
ExecLogVO execCommandWithSource(ExecCommandExecRequest request);
/**
* 重新执行命令
*
* @param logId logId
* @return result
*/
ExecLogVO reExecCommand(Long logId);
/**
* 中断命令执行

View File

@@ -5,11 +5,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Booleans;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.time.Dates;
import com.orion.lang.utils.time.cron.Cron;
import com.orion.ops.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.constant.FieldConst;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.job.core.utils.QuartzUtils;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
@@ -18,19 +16,16 @@ import com.orion.ops.module.asset.dao.ExecJobDAO;
import com.orion.ops.module.asset.dao.ExecLogDAO;
import com.orion.ops.module.asset.entity.domain.ExecJobDO;
import com.orion.ops.module.asset.entity.domain.ExecLogDO;
import com.orion.ops.module.asset.entity.request.exec.ExecJobCreateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobQueryRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecJobUpdateStatusRequest;
import com.orion.ops.module.asset.entity.request.exec.*;
import com.orion.ops.module.asset.entity.vo.ExecJobVO;
import com.orion.ops.module.asset.enums.ExecJobStatusEnum;
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
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.ExecJobHostService;
import com.orion.ops.module.asset.service.ExecJobService;
import com.orion.ops.module.asset.service.ExecService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -55,19 +50,25 @@ public class ExecJobServiceImpl implements ExecJobService {
// TODO 测试 SSH 禁用后是什么样子的
// TODO 操作日志 菜单
// TODO 执行日志抽象
// TODO 任务分组
// TODO 手动执行 测试 quartz
// 内置参数 params.put("source", request.getSource());
// params.put("sourceId", request.getSourceId());
// params.put("seq", request.getExecSeq());
private static final String QUARTZ_TYPE = "Exec";
@Resource
private ExecJobDAO execJobDAO;
@Resource
private ExecLogDAO execLogDAO;
@Resource
private ExecJobHostService execJobHostService;
@Resource
private ExecLogDAO execLogDAO;
private ExecService execService;
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@@ -228,6 +229,12 @@ public class ExecJobServiceImpl implements ExecJobService {
return effect;
}
@Override
public void triggerExecJob(ExecJobTriggerRequest request) {
}
/**
* 检查对象是否存在
*
@@ -273,10 +280,9 @@ public class ExecJobServiceImpl implements ExecJobService {
if (delete) {
QuartzUtils.deleteJob(QUARTZ_TYPE, id);
}
// FIXME
// 启动 quartz job
if (add) {
QuartzUtils.addJob(QUARTZ_TYPE, id, record.getExpression(), record.getName(), TestJob.class);
QuartzUtils.addJob(QUARTZ_TYPE, id, record.getExpression(), record.getName(), ExecCommandJob.class);
}
}
@@ -293,15 +299,4 @@ public class ExecJobServiceImpl implements ExecJobService {
}
}
// FIXME
static class TestJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("----------------------");
System.out.println(Dates.current());
System.out.println(context.getMergedJobDataMap().getLong(FieldConst.KEY));
}
}
}

View File

@@ -23,6 +23,7 @@ import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisStrings;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.convert.ExecConvert;
import com.orion.ops.module.asset.convert.ExecHostLogConvert;
import com.orion.ops.module.asset.convert.ExecLogConvert;
import com.orion.ops.module.asset.dao.ExecHostLogDAO;
@@ -35,6 +36,7 @@ import com.orion.ops.module.asset.entity.domain.HostDO;
import com.orion.ops.module.asset.entity.dto.ExecHostLogTailDTO;
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
import com.orion.ops.module.asset.entity.dto.ExecParameterSchemaDTO;
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.request.exec.ExecLogTailRequest;
import com.orion.ops.module.asset.entity.vo.ExecHostLogVO;
@@ -115,12 +117,27 @@ public class ExecServiceImpl implements ExecService {
hostIdList.removeIf(s -> !authorizedHostIdList.contains(s));
log.info("ExecService.startExecCommand host hostList: {}", hostIdList);
Valid.notEmpty(hostIdList, ErrorMessage.CHECK_AUTHORIZED_HOST);
// 执行命令
ExecCommandExecRequest execRequest = ExecConvert.MAPPER.to(request);
execRequest.setUserId(userId);
execRequest.setUsername(user.getUsername());
execRequest.setSource(ExecSourceEnum.BATCH.name());
return this.execCommandWithSource(execRequest);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ExecLogVO execCommandWithSource(ExecCommandExecRequest request) {
String command = request.getCommand();
List<Long> hostIdList = request.getHostIdList();
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
// 插入日志
ExecLogDO execLog = ExecLogDO.builder()
.userId(userId)
.username(user.getUsername())
.source(ExecSourceEnum.BATCH.name())
.userId(request.getUserId())
.username(request.getUsername())
.source(request.getSource())
.sourceId(request.getSourceId())
.execSeq(request.getExecSeq())
.description(Strings.ifBlank(request.getDescription(), () -> {
if (command.length() < DESC_OMIT + 3) {
return command;
@@ -136,7 +153,7 @@ public class ExecServiceImpl implements ExecService {
execLogDAO.insert(execLog);
Long execId = execLog.getId();
// 获取内置参数
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(user, execId, request.getParameterSchema());
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(execId, request);
// 设置主机日志
List<ExecHostLogDO> execHostLogs = hosts.stream()
.map(s -> {
@@ -154,7 +171,7 @@ public class ExecServiceImpl implements ExecService {
}).collect(Collectors.toList());
execHostLogDAO.insertBatch(execHostLogs);
// 操作日志
OperatorLogs.add(OperatorLogs.ID, execId);
OperatorLogs.add(OperatorLogs.LOG_ID, execId);
// 开始执行
this.startExec(execLog, execHostLogs);
// 返回
@@ -405,52 +422,24 @@ public class ExecServiceImpl implements ExecService {
ExecTaskExecutors.start(exec);
}
/**
* 构建日志路径
*
* @param logId logId
* @param hostId hostId
* @return logPath
*/
private String buildLogPath(Long logId, Long hostId) {
String logFile = "/exec/" + logId + "/" + logId + "_" + hostId + ".log";
return logsFileClient.getReturnPath(logFile);
}
/**
* 提取参数
*
* @param parameterSchema parameterSchema
* @return params
*/
private Map<String, Object> extraSchemaParams(String parameterSchema) {
List<ExecParameterSchemaDTO> schemaList = JSON.parseArray(parameterSchema, ExecParameterSchemaDTO.class);
if (Lists.isEmpty(schemaList)) {
return Maps.newMap();
}
// 解析参数
return schemaList.stream()
.collect(Collectors.toMap(ExecParameterSchemaDTO::getName,
ExecParameterSchemaDTO::getValue,
Functions.right()));
}
/**
* 获取基础内置参数
*
* @param user user
* @param execId execId
* @param parameterSchema parameterSchema
* @param execId execId
* @param request request
* @return params
*/
private Map<String, Object> getBaseBuiltinsParams(LoginUser user, Long execId, String parameterSchema) {
private Map<String, Object> getBaseBuiltinsParams(Long execId, ExecCommandExecRequest request) {
String uuid = UUIds.random();
Date date = new Date();
// 输入参数
Map<String, Object> params = this.extraSchemaParams(parameterSchema);
Map<String, Object> params = this.extraSchemaParams(request.getParameterSchema());
// 添加内置参数
params.put("userId", user.getId());
params.put("username", user.getId());
params.put("userId", request.getUserId());
params.put("username", request.getUsername());
params.put("source", request.getSource());
params.put("sourceId", request.getSourceId());
params.put("seq", request.getExecSeq());
params.put("execId", execId);
params.put("uuid", uuid);
params.put("uuidShort", uuid.replace("-", Strings.EMPTY));
@@ -480,4 +469,34 @@ public class ExecServiceImpl implements ExecService {
return params;
}
/**
* 提取参数
*
* @param parameterSchema parameterSchema
* @return params
*/
private Map<String, Object> extraSchemaParams(String parameterSchema) {
List<ExecParameterSchemaDTO> schemaList = JSON.parseArray(parameterSchema, ExecParameterSchemaDTO.class);
if (Lists.isEmpty(schemaList)) {
return Maps.newMap();
}
// 解析参数
return schemaList.stream()
.collect(Collectors.toMap(ExecParameterSchemaDTO::getName,
ExecParameterSchemaDTO::getValue,
Functions.right()));
}
/**
* 构建日志路径
*
* @param logId logId
* @param hostId hostId
* @return logPath
*/
private String buildLogPath(Long logId, Long hostId) {
String logFile = "/exec/" + logId + "/" + logId + "_" + hostId + ".log";
return logsFileClient.getReturnPath(logFile);
}
}