🔨 批量执行.
This commit is contained in:
@@ -87,4 +87,6 @@ public interface ErrorMessage {
|
||||
|
||||
String ILLEGAL_STATUS = "当前状态不支持此操作";
|
||||
|
||||
String CHECK_AUTHORIZED_HOST = "请选择已授权的主机";
|
||||
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ orion:
|
||||
local:
|
||||
primary: true
|
||||
enabled: true
|
||||
timestamp-prefix: true
|
||||
timestamp-prefix: false
|
||||
storage-path: ${user.home}
|
||||
base-path: /orion/storage/orion-ops-pro
|
||||
security:
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
|
||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.ops.module.asset.define.operator.ExecOperatorType;
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecVO;
|
||||
import com.orion.ops.module.asset.service.ExecService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -40,8 +41,8 @@ public class ExecController {
|
||||
@PostMapping("/start")
|
||||
@Operation(summary = "批量执行")
|
||||
@PreAuthorize("@ss.hasPermission('asset:exec:start')")
|
||||
public void startExecCommand(@RequestBody ExecRequest request) {
|
||||
execService.startExecCommand(request);
|
||||
public ExecVO startExecCommand(@RequestBody ExecRequest request) {
|
||||
return execService.startExecCommand(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.orion.ops.module.asset.entity.dto;
|
||||
|
||||
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 15:46
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "ExecStartDTO", description = "批量执行启动对象")
|
||||
public class ExecStartDTO {
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description = "主机")
|
||||
private List<ExecStartHostDTO> hosts;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.orion.ops.module.asset.entity.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 批量执行启动主机对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/11 15:46
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "ExecStartHostDTO", description = "批量执行启动主机对象")
|
||||
public class ExecStartHostDTO {
|
||||
|
||||
@Schema(description = "hostLogId")
|
||||
private Long hostLogId;
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "日志文件路径")
|
||||
private String logPath;
|
||||
|
||||
@Schema(description = "执行命令")
|
||||
private String command;
|
||||
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 执行模板 缓存对象
|
||||
@@ -41,16 +40,4 @@ public class ExecTemplateCacheDTO implements LongCacheIdModel, Serializable {
|
||||
@Schema(description = "参数")
|
||||
private String parameter;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@Schema(description = "修改时间")
|
||||
private Date updateTime;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "修改人")
|
||||
private String updater;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.ops.framework.common.entity.PageRequest;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
@@ -23,16 +24,19 @@ import java.util.List;
|
||||
@Schema(name = "ExecRequest", description = "批量执行 请求对象")
|
||||
public class ExecRequest extends PageRequest {
|
||||
|
||||
@Schema(description = "执行模板id")
|
||||
private Long templateId;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 128)
|
||||
@Schema(description = "执行描述")
|
||||
private String desc;
|
||||
|
||||
@Schema(description = "执行模板id")
|
||||
private Long templateId;
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "执行命令")
|
||||
private String command;
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "执行参数")
|
||||
private String parameter;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.orion.ops.module.asset.entity.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 批量执行 视图响应对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/11 14:57
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "ExecVO", description = "批量执行 视图响应对象")
|
||||
public class ExecVO implements Serializable {
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "主机 id 映射 host:id")
|
||||
private Map<String, Long> hostIdRel;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.orion.ops.module.asset.enums;
|
||||
|
||||
/**
|
||||
* 批量执行主机状态
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/11 17:08
|
||||
*/
|
||||
public enum ExecHostStatusEnum {
|
||||
|
||||
/**
|
||||
* 等待中
|
||||
*/
|
||||
WAITING,
|
||||
|
||||
/**
|
||||
* 执行中
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* 执行完成
|
||||
*/
|
||||
COMPLETED,
|
||||
|
||||
/**
|
||||
* 执行失败
|
||||
*/
|
||||
FAILED,
|
||||
|
||||
/**
|
||||
* 中断执行
|
||||
*/
|
||||
INTERRUPTED,
|
||||
|
||||
;
|
||||
|
||||
public static ExecHostStatusEnum of(String status) {
|
||||
if (status == null) {
|
||||
return null;
|
||||
}
|
||||
for (ExecHostStatusEnum value : values()) {
|
||||
if (value.name().equals(status)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.orion.ops.module.asset.enums;
|
||||
|
||||
/**
|
||||
* 批量执行来源
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/11 16:00
|
||||
*/
|
||||
public enum ExecSourceEnum {
|
||||
|
||||
/**
|
||||
* 批量执行
|
||||
*/
|
||||
BATCH,
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.orion.ops.module.asset.enums;
|
||||
|
||||
/**
|
||||
* 批量执行状态
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/11 17:08
|
||||
*/
|
||||
public enum ExecStatusEnum {
|
||||
|
||||
/**
|
||||
* 等待中
|
||||
*/
|
||||
WAITING,
|
||||
|
||||
/**
|
||||
* 执行中
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* 执行完成
|
||||
*/
|
||||
COMPLETED,
|
||||
|
||||
/**
|
||||
* 执行失败
|
||||
*/
|
||||
FAILED,
|
||||
|
||||
;
|
||||
|
||||
public static ExecStatusEnum of(String status) {
|
||||
if (status == null) {
|
||||
return null;
|
||||
}
|
||||
for (ExecStatusEnum value : values()) {
|
||||
if (value.name().equals(status)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.orion.ops.module.asset.entity.request.asset.AssetAuthorizedDataQueryR
|
||||
import com.orion.ops.module.asset.entity.vo.AuthorizedHostWrapperVO;
|
||||
import com.orion.ops.module.asset.entity.vo.HostIdentityVO;
|
||||
import com.orion.ops.module.asset.entity.vo.HostKeyVO;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
import com.orion.ops.module.infra.enums.DataPermissionTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
@@ -27,7 +28,16 @@ public interface AssetAuthorizedDataService {
|
||||
List<Long> getAuthorizedDataRelId(DataPermissionTypeEnum type, AssetAuthorizedDataQueryRequest request);
|
||||
|
||||
/**
|
||||
* 查询用户已授权的主机主机
|
||||
* 查询用户已授权的主机
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
* @return hostId
|
||||
*/
|
||||
List<Long> getUserAuthorizedHostId(Long userId, HostConfigTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询用户已授权的主机
|
||||
*
|
||||
* @param userId userId
|
||||
* @param type type
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.ops.module.asset.service;
|
||||
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecVO;
|
||||
|
||||
/**
|
||||
* 批量执行服务
|
||||
@@ -15,7 +16,8 @@ public interface ExecService {
|
||||
* 批量执行
|
||||
*
|
||||
* @param request request
|
||||
* @return result
|
||||
*/
|
||||
void startExecCommand(ExecRequest request);
|
||||
ExecVO startExecCommand(ExecRequest request);
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.module.asset.convert.HostGroupConvert;
|
||||
import com.orion.ops.module.asset.entity.request.asset.AssetAuthorizedDataQueryRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.*;
|
||||
import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
|
||||
import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.extra.model.HostColorExtraModel;
|
||||
import com.orion.ops.module.asset.service.*;
|
||||
@@ -90,6 +91,20 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getUserAuthorizedHostId(Long userId, HostConfigTypeEnum type) {
|
||||
final boolean allData = systemUserApi.isAdminUser(userId);
|
||||
if (allData) {
|
||||
// 管理员查询所有
|
||||
return this.getEnabledConfigHostId(true, Maps.empty(), type.name());
|
||||
} else {
|
||||
// 其他用户 查询授权的数据
|
||||
Map<Long, Set<Long>> dataGroupRel = dataGroupRelApi.getGroupRelList(DataGroupTypeEnum.HOST);
|
||||
// 查询配置启用的主机
|
||||
return this.getEnabledConfigHostId(false, dataGroupRel, type.name());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizedHostWrapperVO getUserAuthorizedHost(Long userId, String type) {
|
||||
if (systemUserApi.isAdminUser(userId)) {
|
||||
|
||||
@@ -1,12 +1,42 @@
|
||||
package com.orion.ops.module.asset.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.orion.lang.id.UUIds;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.lang.utils.json.matcher.NoMatchStrategy;
|
||||
import com.orion.lang.utils.json.matcher.ReplacementFormatter;
|
||||
import com.orion.lang.utils.json.matcher.ReplacementFormatters;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.framework.common.utils.Valid;
|
||||
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.ops.module.asset.dao.ExecHostLogDAO;
|
||||
import com.orion.ops.module.asset.dao.ExecLogDAO;
|
||||
import com.orion.ops.module.asset.dao.HostDAO;
|
||||
import com.orion.ops.module.asset.entity.domain.ExecHostLogDO;
|
||||
import com.orion.ops.module.asset.entity.domain.ExecLogDO;
|
||||
import com.orion.ops.module.asset.entity.domain.HostDO;
|
||||
import com.orion.ops.module.asset.entity.request.exec.ExecRequest;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecVO;
|
||||
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.service.AssetAuthorizedDataService;
|
||||
import com.orion.ops.module.asset.service.ExecService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 批量执行服务实现
|
||||
@@ -19,12 +49,127 @@ import javax.annotation.Resource;
|
||||
@Service
|
||||
public class ExecServiceImpl implements ExecService {
|
||||
|
||||
private static final ReplacementFormatter FORMATTER = ReplacementFormatters.create("@{{ ", " }}")
|
||||
.noMatchStrategy(NoMatchStrategy.EMPTY);
|
||||
|
||||
@Resource
|
||||
private ExecLogDAO execLogDAO;
|
||||
|
||||
@Override
|
||||
public void startExecCommand(ExecRequest request) {
|
||||
@Resource
|
||||
private ExecHostLogDAO execHostLogDAO;
|
||||
|
||||
@Resource
|
||||
private HostDAO hostDAO;
|
||||
|
||||
@Resource
|
||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ExecVO startExecCommand(ExecRequest request) {
|
||||
log.info("ExecService.startExecCommand start params: {}", JSON.toJSONString(request));
|
||||
LoginUser user = Objects.requireNonNull(SecurityUtils.getLoginUser());
|
||||
Long userId = user.getId();
|
||||
String command = request.getCommand();
|
||||
List<Long> hostIdList = request.getHostIdList();
|
||||
// 检查主机权限
|
||||
List<Long> authorizedHostIdList = assetAuthorizedDataService.getUserAuthorizedHostId(userId, HostConfigTypeEnum.SSH);
|
||||
hostIdList.removeIf(s -> !authorizedHostIdList.contains(s));
|
||||
Valid.notEmpty(hostIdList, ErrorMessage.CHECK_AUTHORIZED_HOST);
|
||||
List<HostDO> hosts = hostDAO.selectBatchIds(hostIdList);
|
||||
// 插入日志
|
||||
ExecLogDO execLog = ExecLogDO.builder()
|
||||
.userId(userId)
|
||||
.source(ExecSourceEnum.BATCH.name())
|
||||
.desc(request.getDesc())
|
||||
.command(command)
|
||||
.status(ExecStatusEnum.COMPLETED.name())
|
||||
.build();
|
||||
execLogDAO.insert(execLog);
|
||||
Long execId = execLog.getId();
|
||||
// 获取内置参数
|
||||
Map<String, Object> builtinsParams = getBaseBuiltinsParams(user, execId, request.getParameter());
|
||||
// 设置主机日志
|
||||
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())
|
||||
.status(ExecHostStatusEnum.WAITING.name())
|
||||
.command(FORMATTER.format(command, parameter))
|
||||
.parameter(parameter)
|
||||
.logPath(this.buildLogPath(execId, s.getId()))
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
execHostLogDAO.insertBatch(execHostLogs);
|
||||
// TODO 开始执行
|
||||
|
||||
|
||||
// 返回
|
||||
Map<String, Long> hostIdRel = execHostLogs.stream()
|
||||
.collect(Collectors.toMap(s -> String.valueOf(s.getHostId()), ExecHostLogDO::getId));
|
||||
return ExecVO.builder()
|
||||
.id(execId)
|
||||
.hostIdRel(hostIdRel)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建日志路径
|
||||
*
|
||||
* @param logId logId
|
||||
* @param hostId hostId
|
||||
* @return logPath
|
||||
*/
|
||||
private String buildLogPath(Long logId, Long hostId) {
|
||||
return "/logs/exec/" + logId + "/" + hostId + ".log";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础内置参数
|
||||
*
|
||||
* @param user user
|
||||
* @param execId execId
|
||||
* @return params
|
||||
*/
|
||||
private Map<String, Object> getBaseBuiltinsParams(LoginUser user, Long execId, String inputParam) {
|
||||
String uuid = UUIds.random();
|
||||
Date date = new Date();
|
||||
// 输入参数
|
||||
JSONObject inputParams = JSON.parseObject(inputParam);
|
||||
// 内置参数
|
||||
Map<String, Object> params = Maps.newMap(inputParams);
|
||||
params.put("userId", user.getId());
|
||||
params.put("username", user.getId());
|
||||
params.put("execId", execId);
|
||||
params.put("uuid", uuid);
|
||||
params.put("uuidShort", uuid.replace("-", Strings.EMPTY));
|
||||
params.put("timestampMillis", date.getTime());
|
||||
params.put("timestamp", date.getTime() / Dates.SECOND_STAMP);
|
||||
params.put("date", Dates.format(date, Dates.YMD));
|
||||
params.put("datetime", Dates.format(date, Dates.YMD_HMS));
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机参数
|
||||
*
|
||||
* @param baseParams baseParams
|
||||
* @param host host
|
||||
* @return params
|
||||
*/
|
||||
private Map<String, Object> getHostParams(Map<String, Object> baseParams, HostDO host) {
|
||||
String uuid = UUIds.random();
|
||||
Map<String, Object> params = Maps.newMap(baseParams);
|
||||
params.put("hostId", host.getId());
|
||||
params.put("hostName", host.getName());
|
||||
params.put("hostCode", host.getCode());
|
||||
params.put("hostAddress", host.getAddress());
|
||||
params.put("hostUuid", uuid);
|
||||
params.put("hostUuidShort", uuid.replace("-", Strings.EMPTY));
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public enum DataPermissionTypeEnum {
|
||||
/**
|
||||
* 主机分组
|
||||
*/
|
||||
HOST_GROUP(true, "主机"),
|
||||
HOST_GROUP(true, "主机分组"),
|
||||
|
||||
/**
|
||||
* 主机秘钥
|
||||
|
||||
Reference in New Issue
Block a user