🔨 执行命令.
This commit is contained in:
@@ -106,6 +106,10 @@ public class DataQuery<T> {
|
|||||||
return then;
|
return then;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataQuery<T> limit(IPageRequest page) {
|
||||||
|
return this.last(Pager.of(page).getSql());
|
||||||
|
}
|
||||||
|
|
||||||
public DataQuery<T> limit(int limit) {
|
public DataQuery<T> limit(int limit) {
|
||||||
return this.last(Const.LIMIT + Const.SPACE + limit);
|
return this.last(Const.LIMIT + Const.SPACE + limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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.common.validator.group.Page;
|
||||||
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
|
||||||
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
|
||||||
|
import com.orion.ops.framework.security.core.utils.SecurityUtils;
|
||||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||||
import com.orion.ops.module.asset.define.operator.ExecOperatorType;
|
import com.orion.ops.module.asset.define.operator.ExecOperatorType;
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecLogQueryRequest;
|
import com.orion.ops.module.asset.entity.request.exec.ExecLogQueryRequest;
|
||||||
@@ -73,6 +74,16 @@ public class ExecLogController {
|
|||||||
return execLogService.getExecLogStatus(idList);
|
return execLogService.getExecLogStatus(idList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
|
@GetMapping("/history")
|
||||||
|
@Operation(summary = "查询执行历史")
|
||||||
|
@PreAuthorize("@ss.hasAnyPermission('asset:exec-log:query', 'asset:exec:exec-command')")
|
||||||
|
public List<ExecLogVO> getExecLogHistory(@Validated(Page.class) ExecLogQueryRequest request) {
|
||||||
|
request.setSource(ExecSourceEnum.BATCH.name());
|
||||||
|
request.setUserId(SecurityUtils.getLoginUserId());
|
||||||
|
return execLogService.getExecHistory(request);
|
||||||
|
}
|
||||||
|
|
||||||
@OperatorLog(ExecOperatorType.DELETE_LOG)
|
@OperatorLog(ExecOperatorType.DELETE_LOG)
|
||||||
@DeleteMapping("/delete")
|
@DeleteMapping("/delete")
|
||||||
@Operation(summary = "删除执行日志")
|
@Operation(summary = "删除执行日志")
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行模板 api
|
* 执行模板 api
|
||||||
@@ -67,14 +66,6 @@ public class ExecTemplateController {
|
|||||||
return execTemplateService.getExecTemplateById(id);
|
return execTemplateService.getExecTemplateById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@IgnoreLog(IgnoreLogMode.RET)
|
|
||||||
@GetMapping("/list")
|
|
||||||
@Operation(summary = "查询全部执行模板")
|
|
||||||
@PreAuthorize("@ss.hasPermission('asset:exec-template:query')")
|
|
||||||
public List<ExecTemplateVO> getExecTemplateList() {
|
|
||||||
return execTemplateService.getExecTemplateListByCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
@IgnoreLog(IgnoreLogMode.RET)
|
@IgnoreLog(IgnoreLogMode.RET)
|
||||||
@PostMapping("/query")
|
@PostMapping("/query")
|
||||||
@Operation(summary = "分页查询执行模板")
|
@Operation(summary = "分页查询执行模板")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.orion.ops.module.asset.convert;
|
package com.orion.ops.module.asset.convert;
|
||||||
|
|
||||||
import com.orion.ops.module.asset.entity.domain.ExecTemplateDO;
|
import com.orion.ops.module.asset.entity.domain.ExecTemplateDO;
|
||||||
import com.orion.ops.module.asset.entity.dto.ExecTemplateCacheDTO;
|
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateCreateRequest;
|
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateCreateRequest;
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateUpdateRequest;
|
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.entity.vo.ExecTemplateVO;
|
||||||
@@ -26,8 +25,4 @@ public interface ExecTemplateConvert {
|
|||||||
|
|
||||||
ExecTemplateVO to(ExecTemplateDO domain);
|
ExecTemplateVO to(ExecTemplateDO domain);
|
||||||
|
|
||||||
ExecTemplateVO to(ExecTemplateCacheDTO cache);
|
|
||||||
|
|
||||||
ExecTemplateCacheDTO toCache(ExecTemplateDO domain);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.orion.ops.module.asset.define.cache;
|
|
||||||
|
|
||||||
import com.orion.lang.define.cache.key.CacheKeyBuilder;
|
|
||||||
import com.orion.lang.define.cache.key.CacheKeyDefine;
|
|
||||||
import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
|
|
||||||
import com.orion.ops.module.asset.entity.dto.ExecTemplateCacheDTO;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行模板缓存 key
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.1
|
|
||||||
* @since 2024-3-7 18:08
|
|
||||||
*/
|
|
||||||
public interface ExecTemplateCacheKeyDefine {
|
|
||||||
|
|
||||||
CacheKeyDefine EXEC_TEMPLATE = new CacheKeyBuilder()
|
|
||||||
.key("exec:template:list")
|
|
||||||
.desc("执行模板列表")
|
|
||||||
.type(ExecTemplateCacheDTO.class)
|
|
||||||
.struct(RedisCacheStruct.HASH)
|
|
||||||
.timeout(1, TimeUnit.DAYS)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行参数 schema 对象
|
||||||
|
*
|
||||||
|
* @author Jiahang Li
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2024/3/15 14:50
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(name = "ExecParameterSchemaDTO", description = "命令执行参数 schema 对象")
|
||||||
|
public class ExecParameterSchemaDTO {
|
||||||
|
|
||||||
|
@Schema(description = "参数名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "参数描述")
|
||||||
|
private String desc;
|
||||||
|
|
||||||
|
@Schema(description = "默认值")
|
||||||
|
private Object defaultValue;
|
||||||
|
|
||||||
|
@Schema(description = "值")
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.orion.ops.module.asset.entity.dto;
|
|
||||||
|
|
||||||
import com.orion.lang.define.cache.key.model.LongCacheIdModel;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行模板 缓存对象
|
|
||||||
*
|
|
||||||
* @author Jiahang Li
|
|
||||||
* @version 1.0.1
|
|
||||||
* @since 2024-3-7 18:08
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Builder
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Schema(name = "ExecTemplateCacheDTO", description = "执行模板 缓存对象")
|
|
||||||
public class ExecTemplateCacheDTO implements LongCacheIdModel, Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Schema(description = "id")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "名称")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Schema(description = "命令")
|
|
||||||
private String command;
|
|
||||||
|
|
||||||
@Schema(description = "超时时间秒 0不超时")
|
|
||||||
private Integer timeout;
|
|
||||||
|
|
||||||
@Schema(description = "参数")
|
|
||||||
private String parameter;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -34,10 +34,6 @@ public class ExecCommandRequest {
|
|||||||
@Schema(description = "执行命令")
|
@Schema(description = "执行命令")
|
||||||
private String command;
|
private String command;
|
||||||
|
|
||||||
@NotBlank
|
|
||||||
@Schema(description = "执行参数")
|
|
||||||
private String parameter;
|
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Schema(description = "参数 schema")
|
@Schema(description = "参数 schema")
|
||||||
private String parameterSchema;
|
private String parameterSchema;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量执行日志 视图响应对象
|
* 批量执行日志 视图响应对象
|
||||||
@@ -55,4 +56,7 @@ public class ExecLogVO implements Serializable {
|
|||||||
@Schema(description = "执行完成时间")
|
@Schema(description = "执行完成时间")
|
||||||
private Date finishTime;
|
private Date finishTime;
|
||||||
|
|
||||||
|
@Schema(description = "执行主机id")
|
||||||
|
private List<Long> hostIdList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ public interface ExecLogService {
|
|||||||
*/
|
*/
|
||||||
DataGrid<ExecLogVO> getExecLogPage(ExecLogQueryRequest request);
|
DataGrid<ExecLogVO> getExecLogPage(ExecLogQueryRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取执行历史
|
||||||
|
*
|
||||||
|
* @param request request
|
||||||
|
* @return history
|
||||||
|
*/
|
||||||
|
List<ExecLogVO> getExecHistory(ExecLogQueryRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取执行日志状态
|
* 获取执行日志状态
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ 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.request.exec.ExecTemplateUpdateRequest;
|
||||||
import com.orion.ops.module.asset.entity.vo.ExecTemplateVO;
|
import com.orion.ops.module.asset.entity.vo.ExecTemplateVO;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行模板 服务类
|
* 执行模板 服务类
|
||||||
*
|
*
|
||||||
@@ -41,13 +39,6 @@ public interface ExecTemplateService {
|
|||||||
*/
|
*/
|
||||||
ExecTemplateVO getExecTemplateById(Long id);
|
ExecTemplateVO getExecTemplateById(Long id);
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过缓存查询执行模板
|
|
||||||
*
|
|
||||||
* @return rows
|
|
||||||
*/
|
|
||||||
List<ExecTemplateVO> getExecTemplateListByCache();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询执行模板
|
* 分页查询执行模板
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -64,6 +65,41 @@ public class ExecLogServiceImpl implements ExecLogService {
|
|||||||
.dataGrid(ExecLogConvert.MAPPER::to);
|
.dataGrid(ExecLogConvert.MAPPER::to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ExecLogVO> getExecHistory(ExecLogQueryRequest request) {
|
||||||
|
// 查询执行记录
|
||||||
|
List<ExecLogVO> logs = execLogDAO.of()
|
||||||
|
.createWrapper()
|
||||||
|
.eq(ExecLogDO::getSource, request.getSource())
|
||||||
|
.eq(ExecLogDO::getUserId, request.getUserId())
|
||||||
|
.groupBy(ExecLogDO::getDescription)
|
||||||
|
.orderByDesc(ExecLogDO::getId)
|
||||||
|
.then()
|
||||||
|
.limit(request)
|
||||||
|
.list(ExecLogConvert.MAPPER::to);
|
||||||
|
if (logs.isEmpty()) {
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
List<Long> logIdList = logs.stream()
|
||||||
|
.map(ExecLogVO::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
// 设置执行主机id
|
||||||
|
Map<Long, List<Long>> hostIdRel = execHostLogDAO.of()
|
||||||
|
.createWrapper()
|
||||||
|
.in(ExecHostLogDO::getLogId, logIdList)
|
||||||
|
.then()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
ExecHostLogDO::getLogId,
|
||||||
|
Collectors.mapping(
|
||||||
|
ExecHostLogDO::getHostId,
|
||||||
|
Collectors.toList()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
logs.forEach(s -> s.setHostIdList(hostIdRel.get(s.getId())));
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExecLogStatusVO getExecLogStatus(List<Long> idList) {
|
public ExecLogStatusVO getExecLogStatus(List<Long> idList) {
|
||||||
// 查询执行状态
|
// 查询执行状态
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.orion.ops.module.asset.service.impl;
|
package com.orion.ops.module.asset.service.impl;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.orion.lang.function.Functions;
|
||||||
import com.orion.lang.id.UUIds;
|
import com.orion.lang.id.UUIds;
|
||||||
import com.orion.lang.utils.Strings;
|
import com.orion.lang.utils.Strings;
|
||||||
|
import com.orion.lang.utils.collect.Lists;
|
||||||
import com.orion.lang.utils.collect.Maps;
|
import com.orion.lang.utils.collect.Maps;
|
||||||
import com.orion.lang.utils.json.matcher.NoMatchStrategy;
|
import com.orion.lang.utils.json.matcher.NoMatchStrategy;
|
||||||
import com.orion.lang.utils.json.matcher.ReplacementFormatter;
|
import com.orion.lang.utils.json.matcher.ReplacementFormatter;
|
||||||
@@ -23,6 +24,7 @@ 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.ExecHostLogDO;
|
||||||
import com.orion.ops.module.asset.entity.domain.ExecLogDO;
|
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.domain.HostDO;
|
||||||
|
import com.orion.ops.module.asset.entity.dto.ExecParameterSchemaDTO;
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecCommandRequest;
|
import com.orion.ops.module.asset.entity.request.exec.ExecCommandRequest;
|
||||||
import com.orion.ops.module.asset.entity.vo.ExecCommandHostVO;
|
import com.orion.ops.module.asset.entity.vo.ExecCommandHostVO;
|
||||||
import com.orion.ops.module.asset.entity.vo.ExecCommandVO;
|
import com.orion.ops.module.asset.entity.vo.ExecCommandVO;
|
||||||
@@ -109,7 +111,7 @@ public class ExecServiceImpl implements ExecService {
|
|||||||
execLogDAO.insert(execLog);
|
execLogDAO.insert(execLog);
|
||||||
Long execId = execLog.getId();
|
Long execId = execLog.getId();
|
||||||
// 获取内置参数
|
// 获取内置参数
|
||||||
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(user, execId, request.getParameter());
|
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(user, execId, request.getParameterSchema());
|
||||||
// 设置主机日志
|
// 设置主机日志
|
||||||
List<ExecHostLogDO> execHostLogs = hosts.stream()
|
List<ExecHostLogDO> execHostLogs = hosts.stream()
|
||||||
.map(s -> {
|
.map(s -> {
|
||||||
@@ -159,7 +161,6 @@ public class ExecServiceImpl implements ExecService {
|
|||||||
.description(execLog.getDescription())
|
.description(execLog.getDescription())
|
||||||
.timeout(execLog.getTimeout())
|
.timeout(execLog.getTimeout())
|
||||||
.command(execLog.getCommand())
|
.command(execLog.getCommand())
|
||||||
.parameter(hostLogs.get(0).getParameter())
|
|
||||||
.parameterSchema(execLog.getParameterSchema())
|
.parameterSchema(execLog.getParameterSchema())
|
||||||
.hostIdList(hostIdList)
|
.hostIdList(hostIdList)
|
||||||
.build();
|
.build();
|
||||||
@@ -298,19 +299,37 @@ public class ExecServiceImpl implements ExecService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取基础内置参数
|
* 提取参数
|
||||||
*
|
*
|
||||||
* @param user user
|
* @param parameterSchema parameterSchema
|
||||||
* @param execId execId
|
|
||||||
* @return params
|
* @return params
|
||||||
*/
|
*/
|
||||||
private Map<String, Object> getBaseBuiltinsParams(LoginUser user, Long execId, String inputParam) {
|
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
|
||||||
|
* @return params
|
||||||
|
*/
|
||||||
|
private Map<String, Object> getBaseBuiltinsParams(LoginUser user, Long execId, String parameterSchema) {
|
||||||
String uuid = UUIds.random();
|
String uuid = UUIds.random();
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
// 输入参数
|
// 输入参数
|
||||||
JSONObject inputParams = JSON.parseObject(inputParam);
|
Map<String, Object> params = this.extraSchemaParams(parameterSchema);
|
||||||
// 内置参数
|
// 添加内置参数
|
||||||
Map<String, Object> params = Maps.newMap(inputParams);
|
|
||||||
params.put("userId", user.getId());
|
params.put("userId", user.getId());
|
||||||
params.put("username", user.getId());
|
params.put("username", user.getId());
|
||||||
params.put("execId", execId);
|
params.put("execId", execId);
|
||||||
|
|||||||
@@ -5,13 +5,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.orion.lang.define.wrapper.DataGrid;
|
import com.orion.lang.define.wrapper.DataGrid;
|
||||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||||
import com.orion.ops.framework.common.utils.Valid;
|
import com.orion.ops.framework.common.utils.Valid;
|
||||||
import com.orion.ops.framework.redis.core.utils.RedisMaps;
|
|
||||||
import com.orion.ops.framework.redis.core.utils.barrier.CacheBarriers;
|
|
||||||
import com.orion.ops.module.asset.convert.ExecTemplateConvert;
|
import com.orion.ops.module.asset.convert.ExecTemplateConvert;
|
||||||
import com.orion.ops.module.asset.dao.ExecTemplateDAO;
|
import com.orion.ops.module.asset.dao.ExecTemplateDAO;
|
||||||
import com.orion.ops.module.asset.define.cache.ExecTemplateCacheKeyDefine;
|
|
||||||
import com.orion.ops.module.asset.entity.domain.ExecTemplateDO;
|
import com.orion.ops.module.asset.entity.domain.ExecTemplateDO;
|
||||||
import com.orion.ops.module.asset.entity.dto.ExecTemplateCacheDTO;
|
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateCreateRequest;
|
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.ExecTemplateQueryRequest;
|
||||||
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateUpdateRequest;
|
import com.orion.ops.module.asset.entity.request.exec.ExecTemplateUpdateRequest;
|
||||||
@@ -21,9 +17,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行模板 服务实现类
|
* 执行模板 服务实现类
|
||||||
@@ -50,8 +43,6 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
|||||||
int effect = execTemplateDAO.insert(record);
|
int effect = execTemplateDAO.insert(record);
|
||||||
Long id = record.getId();
|
Long id = record.getId();
|
||||||
log.info("ExecTemplateService-createExecTemplate id: {}, effect: {}", id, effect);
|
log.info("ExecTemplateService-createExecTemplate id: {}, effect: {}", id, effect);
|
||||||
// 删除缓存
|
|
||||||
RedisMaps.delete(ExecTemplateCacheKeyDefine.EXEC_TEMPLATE);
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +60,6 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
|||||||
// 更新
|
// 更新
|
||||||
int effect = execTemplateDAO.updateById(updateRecord);
|
int effect = execTemplateDAO.updateById(updateRecord);
|
||||||
log.info("ExecTemplateService-updateExecTemplateById effect: {}", effect);
|
log.info("ExecTemplateService-updateExecTemplateById effect: {}", effect);
|
||||||
// 删除缓存
|
|
||||||
RedisMaps.delete(ExecTemplateCacheKeyDefine.EXEC_TEMPLATE);
|
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,27 +72,6 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
|||||||
return ExecTemplateConvert.MAPPER.to(record);
|
return ExecTemplateConvert.MAPPER.to(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ExecTemplateVO> getExecTemplateListByCache() {
|
|
||||||
// 查询缓存
|
|
||||||
List<ExecTemplateCacheDTO> list = RedisMaps.valuesJson(ExecTemplateCacheKeyDefine.EXEC_TEMPLATE);
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
// 查询数据库
|
|
||||||
list = execTemplateDAO.of().list(ExecTemplateConvert.MAPPER::toCache);
|
|
||||||
// 设置屏障 防止穿透
|
|
||||||
CacheBarriers.checkBarrier(list, ExecTemplateCacheDTO::new);
|
|
||||||
// 设置缓存
|
|
||||||
RedisMaps.putAllJson(ExecTemplateCacheKeyDefine.EXEC_TEMPLATE, s -> s.getId().toString(), list);
|
|
||||||
}
|
|
||||||
// 删除屏障
|
|
||||||
CacheBarriers.removeBarrier(list);
|
|
||||||
// 转换
|
|
||||||
return list.stream()
|
|
||||||
.map(ExecTemplateConvert.MAPPER::to)
|
|
||||||
.sorted(Comparator.comparing(ExecTemplateVO::getId).reversed())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataGrid<ExecTemplateVO> getExecTemplatePage(ExecTemplateQueryRequest request) {
|
public DataGrid<ExecTemplateVO> getExecTemplatePage(ExecTemplateQueryRequest request) {
|
||||||
// 条件
|
// 条件
|
||||||
@@ -123,8 +91,6 @@ public class ExecTemplateServiceImpl implements ExecTemplateService {
|
|||||||
// 删除
|
// 删除
|
||||||
int effect = execTemplateDAO.deleteById(id);
|
int effect = execTemplateDAO.deleteById(id);
|
||||||
log.info("ExecTemplateService-deleteExecTemplateById id: {}, effect: {}", id, effect);
|
log.info("ExecTemplateService-deleteExecTemplateById id: {}, effect: {}", id, effect);
|
||||||
// 删除缓存
|
|
||||||
RedisMaps.delete(ExecTemplateCacheKeyDefine.EXEC_TEMPLATE, id);
|
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface ExecLogQueryResponse extends TableData, ExecLogQueryExtraRespon
|
|||||||
status: string;
|
status: string;
|
||||||
startTime: number;
|
startTime: number;
|
||||||
finishTime: number;
|
finishTime: number;
|
||||||
|
hostIdList: Array<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +89,13 @@ export function getExecLogStatus(idList: Array<number>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询历史执行记录
|
||||||
|
*/
|
||||||
|
export function getExecLogHistory(limit: number) {
|
||||||
|
return axios.get<Array<ExecLogQueryResponse>>('/asset/exec-log/history', { params: { page: 1, limit } });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除执行记录
|
* 删除执行记录
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -64,13 +64,6 @@ export function getExecTemplate(id: number) {
|
|||||||
return axios.get<ExecTemplateQueryResponse>('/asset/exec-template/get', { params: { id } });
|
return axios.get<ExecTemplateQueryResponse>('/asset/exec-template/get', { params: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询全部执行模板
|
|
||||||
*/
|
|
||||||
export function getExecTemplateList() {
|
|
||||||
return axios.get<Array<ExecTemplateQueryResponse>>('/asset/exec-template/list');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询执行模板
|
* 分页查询执行模板
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export interface ExecCommandRequest {
|
|||||||
description?: string;
|
description?: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
command?: string;
|
command?: string;
|
||||||
parameter?: string;
|
|
||||||
parameterSchema?: string;
|
parameterSchema?: string;
|
||||||
hostIdList?: number[];
|
hostIdList?: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
165
orion-ops-ui/src/components/exec/template/modal/index.vue
Normal file
165
orion-ops-ui/src/components/exec/template/modal/index.vue
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal v-model:visible="visible"
|
||||||
|
title-align="start"
|
||||||
|
title="执行模板"
|
||||||
|
width="86%"
|
||||||
|
:top="80"
|
||||||
|
:body-style="{padding: '0 8px'}"
|
||||||
|
:align-center="false"
|
||||||
|
:draggable="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
:footer="false"
|
||||||
|
@close="handleClose">
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<a-card class="general-card table-search-card"
|
||||||
|
style="margin-bottom: 0;">
|
||||||
|
<query-header :model="formModel"
|
||||||
|
label-align="left"
|
||||||
|
@submit="fetchTableData"
|
||||||
|
@reset="fetchTableData"
|
||||||
|
@keyup.enter="() => fetchTableData()">
|
||||||
|
<!-- id -->
|
||||||
|
<a-form-item field="id" label="id">
|
||||||
|
<a-input-number v-model="formModel.id"
|
||||||
|
placeholder="请输入id"
|
||||||
|
allow-clear
|
||||||
|
hide-button />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 模板名称 -->
|
||||||
|
<a-form-item field="name" label="模板名称">
|
||||||
|
<a-input v-model="formModel.name"
|
||||||
|
placeholder="请输入模板名称"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 模板命令 -->
|
||||||
|
<a-form-item field="command" label="模板命令">
|
||||||
|
<a-input v-model="formModel.command"
|
||||||
|
placeholder="请输入模板命令"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</query-header>
|
||||||
|
</a-card>
|
||||||
|
<!-- 表格 -->
|
||||||
|
<a-card class="general-card table-card">
|
||||||
|
<!-- table -->
|
||||||
|
<a-table row-key="id"
|
||||||
|
ref="tableRef"
|
||||||
|
:loading="loading"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableRenderData"
|
||||||
|
:pagination="pagination"
|
||||||
|
:scroll="{ x: '100%', y: '60vh' }"
|
||||||
|
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||||
|
@page-size-change="(size) => fetchTableData(1, size)"
|
||||||
|
:bordered="false">
|
||||||
|
<!-- 模板名称 -->
|
||||||
|
<template #name="{ record }">
|
||||||
|
<span class="span-blue">{{ record.name }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 模板命令 -->
|
||||||
|
<template #command="{ record }">
|
||||||
|
<span class="copy-left" @click="copy(record.command, '已复制')">
|
||||||
|
<icon-copy />
|
||||||
|
</span>
|
||||||
|
<span :title="record.command">{{ record.command }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<template #handle="{ record }">
|
||||||
|
<div class="table-handle-wrapper">
|
||||||
|
<!-- 选择 -->
|
||||||
|
<a-button type="text"
|
||||||
|
size="mini"
|
||||||
|
@click="selectedTemplate(record)">
|
||||||
|
选择
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'execTemplateModal'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ExecTemplateQueryRequest, ExecTemplateQueryResponse } from '@/api/exec/exec-template';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { usePagination } from '@/types/table';
|
||||||
|
import useVisible from '@/hooks/visible';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import useCopy from '@/hooks/copy';
|
||||||
|
import columns from './table.columns';
|
||||||
|
import { getExecTemplatePage } from '@/api/exec/exec-template';
|
||||||
|
|
||||||
|
const emits = defineEmits(['selected']);
|
||||||
|
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
const { copy } = useCopy();
|
||||||
|
const pagination = usePagination();
|
||||||
|
|
||||||
|
const tableRenderData = ref<ExecTemplateQueryResponse[]>([]);
|
||||||
|
const formModel = reactive<ExecTemplateQueryRequest>({
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
command: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
const open = () => {
|
||||||
|
setVisible(true);
|
||||||
|
// 加载数据
|
||||||
|
if (!tableRenderData.value.length) {
|
||||||
|
fetchTableData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
|
||||||
|
// 选择模板
|
||||||
|
const selectedTemplate = (record: ExecTemplateQueryResponse) => {
|
||||||
|
emits('selected', record);
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const doFetchTableData = async (request: ExecTemplateQueryRequest) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data } = await getExecTemplatePage(request);
|
||||||
|
tableRenderData.value = data.rows;
|
||||||
|
pagination.total = data.total;
|
||||||
|
pagination.current = request.page;
|
||||||
|
pagination.pageSize = request.limit;
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换页码
|
||||||
|
const fetchTableData = (page = 1, limit = pagination.pageSize, form = formModel) => {
|
||||||
|
doFetchTableData({ page, limit, ...form });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭回调
|
||||||
|
const handleClose = () => {
|
||||||
|
handleClear();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const handleClear = () => {
|
||||||
|
setLoading(false);
|
||||||
|
setVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import { dateFormat } from '@/utils';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
dataIndex: 'id',
|
||||||
|
slotName: 'id',
|
||||||
|
width: 70,
|
||||||
|
align: 'left',
|
||||||
|
fixed: 'left',
|
||||||
|
}, {
|
||||||
|
title: '模板名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
slotName: 'name',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
}, {
|
||||||
|
title: '模板命令',
|
||||||
|
dataIndex: 'command',
|
||||||
|
slotName: 'command',
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
}, {
|
||||||
|
title: '修改时间',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
slotName: 'updateTime',
|
||||||
|
align: 'center',
|
||||||
|
width: 180,
|
||||||
|
render: ({ record }) => {
|
||||||
|
return dateFormat(new Date(record.updateTime));
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'handle',
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
] as TableColumnData[];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
@@ -76,7 +76,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
|
||||||
import type { HistoryValueQueryRequest, HistoryValueQueryResponse } from '@/api/meta/history-value';
|
import type { HistoryValueQueryRequest, HistoryValueQueryResponse } from '@/api/meta/history-value';
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
@@ -84,54 +83,9 @@
|
|||||||
import { getHistoryValuePage } from '@/api/meta/history-value';
|
import { getHistoryValuePage } from '@/api/meta/history-value';
|
||||||
import { usePagination } from '@/types/table';
|
import { usePagination } from '@/types/table';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
import { dateFormat } from '@/utils';
|
import columns from './table.columns';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'id',
|
|
||||||
dataIndex: 'id',
|
|
||||||
slotName: 'id',
|
|
||||||
width: 70,
|
|
||||||
align: 'left',
|
|
||||||
fixed: 'left',
|
|
||||||
}, {
|
|
||||||
title: '修改前',
|
|
||||||
dataIndex: 'beforeValue',
|
|
||||||
slotName: 'beforeValue',
|
|
||||||
align: 'left',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
}, {
|
|
||||||
title: '修改后',
|
|
||||||
dataIndex: 'afterValue',
|
|
||||||
slotName: 'afterValue',
|
|
||||||
align: 'left',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
}, {
|
|
||||||
title: '修改时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
slotName: 'createTime',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
render: ({ record }) => {
|
|
||||||
return dateFormat(new Date(record.createTime));
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
title: '修改人',
|
|
||||||
dataIndex: 'creator',
|
|
||||||
slotName: 'creator',
|
|
||||||
width: 80,
|
|
||||||
}, {
|
|
||||||
title: '操作',
|
|
||||||
slotName: 'handle',
|
|
||||||
width: 80,
|
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
|
||||||
},
|
|
||||||
] as TableColumnData[];
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
rollback: Function
|
rollback: Function
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||||
|
import { dateFormat } from '@/utils';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
dataIndex: 'id',
|
||||||
|
slotName: 'id',
|
||||||
|
width: 70,
|
||||||
|
align: 'left',
|
||||||
|
fixed: 'left',
|
||||||
|
}, {
|
||||||
|
title: '修改前',
|
||||||
|
dataIndex: 'beforeValue',
|
||||||
|
slotName: 'beforeValue',
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
}, {
|
||||||
|
title: '修改后',
|
||||||
|
dataIndex: 'afterValue',
|
||||||
|
slotName: 'afterValue',
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
}, {
|
||||||
|
title: '修改时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
slotName: 'createTime',
|
||||||
|
align: 'center',
|
||||||
|
width: 180,
|
||||||
|
render: ({ record }) => {
|
||||||
|
return dateFormat(new Date(record.createTime));
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
title: '修改人',
|
||||||
|
dataIndex: 'creator',
|
||||||
|
slotName: 'creator',
|
||||||
|
width: 80,
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
slotName: 'handle',
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
] as TableColumnData[];
|
||||||
|
|
||||||
|
export default columns;
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
// 模板参数
|
// 模板参数
|
||||||
export interface TemplateParam {
|
export interface TemplateParam {
|
||||||
name?: string;
|
name?: string;
|
||||||
default?: string;
|
|
||||||
desc?: string;
|
desc?: string;
|
||||||
|
defaultValue?: any;
|
||||||
|
value?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内置参数
|
// 内置参数
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
his
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'execHistory'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 命令执行 -->
|
||||||
|
<a-spin class="exec-container" :loading="loading">
|
||||||
|
<!-- 执行参数 -->
|
||||||
|
<div class="exec-form-container">
|
||||||
|
<!-- 表头 -->
|
||||||
|
<div class="exec-form-header">
|
||||||
|
<h3>执行参数</h3>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<a-button-group size="small">
|
||||||
|
<a-button @click="reset">重置</a-button>
|
||||||
|
<a-button type="primary" @click="exec">执行</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
</div>
|
||||||
|
<!-- 命令表单 -->
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
:rules="formRules">
|
||||||
|
<!-- 执行主机 -->
|
||||||
|
<a-form-item field="hostIdList"
|
||||||
|
label="执行主机"
|
||||||
|
label-col-flex="72px">
|
||||||
|
<div class="selected-host">
|
||||||
|
<!-- 已选择数量 -->
|
||||||
|
<span class="usn" v-if="formModel.hostIdList?.length">
|
||||||
|
已选择<span class="selected-host-count span-blue">{{ formModel.hostIdList?.length }}</span>台主机
|
||||||
|
</span>
|
||||||
|
<span class="usn pointer span-blue" @click="openSelectHost">
|
||||||
|
{{ formModel.hostIdList?.length ? '重新选择' : '选择主机' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 执行描述 -->
|
||||||
|
<a-form-item field="description"
|
||||||
|
label="执行描述"
|
||||||
|
label-col-flex="72px">
|
||||||
|
<a-input v-model="formModel.description"
|
||||||
|
placeholder="请输入执行描述"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 超时时间 -->
|
||||||
|
<a-form-item field="timeout"
|
||||||
|
label="超时时间"
|
||||||
|
label-col-flex="72px">
|
||||||
|
<a-input-number v-model="formModel.timeout"
|
||||||
|
placeholder="为0则不超时"
|
||||||
|
:min="0"
|
||||||
|
:max="100000"
|
||||||
|
hide-button>
|
||||||
|
<template #suffix>
|
||||||
|
秒
|
||||||
|
</template>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<!-- 命令参数 -->
|
||||||
|
<a-divider v-if="parameterSchema.length"
|
||||||
|
orientation="center"
|
||||||
|
style="margin: 12px 0 26px 0;">
|
||||||
|
命令参数
|
||||||
|
</a-divider>
|
||||||
|
<!-- 参数表单 -->
|
||||||
|
<a-form v-if="parameterSchema.length"
|
||||||
|
:model="parameterFormModel"
|
||||||
|
ref="parameterFormRef"
|
||||||
|
label-align="right">
|
||||||
|
<a-form-item v-for="item in parameterSchema"
|
||||||
|
:key="item.name"
|
||||||
|
:field="item.name as string"
|
||||||
|
:label="item.name"
|
||||||
|
label-col-flex="72px"
|
||||||
|
required>
|
||||||
|
<a-input v-model="parameterFormModel[item.name as string]"
|
||||||
|
:placeholder="item.desc"
|
||||||
|
allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<!-- 执行命令 -->
|
||||||
|
<div class="exec-command-container">
|
||||||
|
<!-- 表头 -->
|
||||||
|
<div class="exec-form-header">
|
||||||
|
<h3>执行命令</h3>
|
||||||
|
<span class="span-blue usn pointer" @click="openTemplate">从模板中选择</span>
|
||||||
|
</div>
|
||||||
|
<!-- 命令编辑器 -->
|
||||||
|
<div class="command-editor-wrapper">
|
||||||
|
<exec-editor v-model="formModel.command"
|
||||||
|
theme="vs-dark"
|
||||||
|
:parameter="parameterSchema" />
|
||||||
|
</div>
|
||||||
|
<!-- 命名提示信息 -->
|
||||||
|
<div v-pre class="command-editor-help">
|
||||||
|
使用 @{{ xxx }} 来替换参数, 输入_可以获取全部变量
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 执行历史 -->
|
||||||
|
<div class="exec-history-container">
|
||||||
|
<div v-if="!historyLogs.length" class="flex-center mt16">
|
||||||
|
<a-empty description="无执行记录" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="exec-history-rows">
|
||||||
|
<div v-for="record in historyLogs"
|
||||||
|
:key="record.id"
|
||||||
|
class="exec-history">
|
||||||
|
<!-- 机器数量 -->
|
||||||
|
<span class="exec-history-count">
|
||||||
|
{{ record.hostIdList?.length || 0 }}
|
||||||
|
</span>
|
||||||
|
<!-- 执行描述 -->
|
||||||
|
<span class="exec-history-desc">
|
||||||
|
{{ record.description }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 主机模态框 -->
|
||||||
|
<authorized-host-modal ref="hostModal"
|
||||||
|
@selected="setSelectedHost" />
|
||||||
|
<!-- 命令模板模态框 -->
|
||||||
|
<exec-template-modal ref="templateModal"
|
||||||
|
@selected="setWithTemplate" />
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'execPanel'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ExecCommandRequest } from '@/api/exec/exec';
|
||||||
|
import type { TemplateParam } from '@/components/view/exec-editor/const';
|
||||||
|
import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template';
|
||||||
|
import type { ExecLogQueryResponse } from '@/api/exec/exec-log';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import formRules from '../types/form.rules';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import { batchExecCommand } from '@/api/exec/exec';
|
||||||
|
import { historyCount } from '../types/const';
|
||||||
|
import { getExecLogHistory } from '@/api/exec/exec-log';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import ExecEditor from '@/components/view/exec-editor/index.vue';
|
||||||
|
import AuthorizedHostModal from '@/components/asset/host/authorized-host-modal/index.vue';
|
||||||
|
import ExecTemplateModal from '@/components/exec/template/modal/index.vue';
|
||||||
|
|
||||||
|
const defaultForm = (): ExecCommandRequest => {
|
||||||
|
return {
|
||||||
|
timeout: 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
const hostModal = ref<any>();
|
||||||
|
const templateModal = ref<any>();
|
||||||
|
const formRef = ref<any>();
|
||||||
|
const parameterFormRef = ref<any>();
|
||||||
|
const formModel = ref<ExecCommandRequest>({ ...defaultForm() });
|
||||||
|
const parameterFormModel = ref<Record<string, any>>({});
|
||||||
|
const parameterSchema = ref<Array<TemplateParam>>([]);
|
||||||
|
const historyLogs = ref<Array<ExecLogQueryResponse>>([]);
|
||||||
|
|
||||||
|
// 加载执行记录
|
||||||
|
const fetchExecHistory = async () => {
|
||||||
|
const { data } = await getExecLogHistory(historyCount);
|
||||||
|
historyLogs.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开选择主机
|
||||||
|
const openSelectHost = () => {
|
||||||
|
hostModal.value.open(formModel.value.hostIdList);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开模板
|
||||||
|
const openTemplate = () => {
|
||||||
|
templateModal.value.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置选中主机
|
||||||
|
const setSelectedHost = (hosts: Array<number>) => {
|
||||||
|
formModel.value.hostIdList = hosts;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从执行模板设置
|
||||||
|
const setWithTemplate = (record: ExecTemplateQueryResponse) => {
|
||||||
|
formModel.value = {
|
||||||
|
...formModel.value,
|
||||||
|
command: record.command,
|
||||||
|
description: record.name,
|
||||||
|
timeout: record.timeout,
|
||||||
|
};
|
||||||
|
parameterSchema.value = record.parameter ? JSON.parse(record.parameter) : [];
|
||||||
|
parameterFormModel.value = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从执行日志设置
|
||||||
|
const setWithExecLog = () => {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行
|
||||||
|
const exec = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 验证参数
|
||||||
|
let error = await formRef.value.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
error = await parameterFormRef.value?.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!formModel.value.command) {
|
||||||
|
Message.error('请输入命令');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 设置 schema
|
||||||
|
for (let ps of parameterSchema.value) {
|
||||||
|
ps.value = parameterFormModel.value[ps.name as string];
|
||||||
|
}
|
||||||
|
// 执行命令
|
||||||
|
const { data } = await batchExecCommand({
|
||||||
|
...formModel.value,
|
||||||
|
parameterSchema: JSON.stringify(parameterSchema.value),
|
||||||
|
});
|
||||||
|
// TODO log history
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formModel.value = Object.assign({}, { ...defaultForm() });
|
||||||
|
parameterFormModel.value = {};
|
||||||
|
parameterSchema.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 加载执行记录
|
||||||
|
onMounted(fetchExecHistory);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@form-width: 420px;
|
||||||
|
@history-width: 320px;
|
||||||
|
@command-gap: @form-width + @history-width + 32px;
|
||||||
|
|
||||||
|
.exec-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.exec-form-container {
|
||||||
|
width: @form-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-command-container {
|
||||||
|
width: calc(100% - @command-gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-history-container {
|
||||||
|
width: @history-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-form-container, .exec-command-container, .exec-history-container {
|
||||||
|
background: var(--color-bg-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-form-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: 28px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-form-container {
|
||||||
|
.selected-host {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-command-container {
|
||||||
|
background: red;
|
||||||
|
|
||||||
|
.command-editor-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 66px);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-editor-help {
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 8px;
|
||||||
|
height: 18px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-history-container {
|
||||||
|
|
||||||
|
.exec-history-rows {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exec-history {
|
||||||
|
padding: 6px 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--color-fill-2);
|
||||||
|
transition: all .2s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-fill-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-bg-2);
|
||||||
|
background: rgb(var(--arcoblue-6));
|
||||||
|
}
|
||||||
|
|
||||||
|
&-desc {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-container">
|
<div class="layout-container full">
|
||||||
|
<!-- 执行面板 -->
|
||||||
|
<exec-panel />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import ExecPanel from './components/exec-panel.vue';
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
2
orion-ops-ui/src/views/exec/exec-command/types/const.ts
Normal file
2
orion-ops-ui/src/views/exec/exec-command/types/const.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// 执行
|
||||||
|
export const historyCount = 20;
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
parameterSchema.value = JSON.parse(record.parameter);
|
parameterSchema.value = JSON.parse(record.parameter);
|
||||||
const params = {} as any;
|
const params = {} as any;
|
||||||
for (let param of parameterSchema.value) {
|
for (let param of parameterSchema.value) {
|
||||||
params[param.name as keyof any] = param.default;
|
params[param.name as keyof any] = param.defaultValue;
|
||||||
}
|
}
|
||||||
parameterFormModel.value = params;
|
parameterFormModel.value = params;
|
||||||
|
|
||||||
@@ -167,10 +167,13 @@
|
|||||||
if (error) {
|
if (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 设置 schema
|
||||||
|
for (let ps of parameterSchema.value) {
|
||||||
|
ps.value = parameterFormModel.value[ps.name as string];
|
||||||
|
}
|
||||||
// 执行命令
|
// 执行命令
|
||||||
await batchExecCommand({
|
await batchExecCommand({
|
||||||
...formModel.value,
|
...formModel.value,
|
||||||
parameter: JSON.stringify(parameterFormModel.value),
|
|
||||||
parameterSchema: JSON.stringify(parameterSchema.value),
|
parameterSchema: JSON.stringify(parameterSchema.value),
|
||||||
});
|
});
|
||||||
Message.success('已开始执行');
|
Message.success('已开始执行');
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
placeholder="参数名称 (必填)"
|
placeholder="参数名称 (必填)"
|
||||||
allow-clear />
|
allow-clear />
|
||||||
<a-input class="parameter-item-default"
|
<a-input class="parameter-item-default"
|
||||||
v-model="item.default"
|
v-model="item.defaultValue"
|
||||||
placeholder="默认值 (非必填)"
|
placeholder="默认值 (非必填)"
|
||||||
allow-clear />
|
allow-clear />
|
||||||
<a-input class="parameter-item-description"
|
<a-input class="parameter-item-description"
|
||||||
|
|||||||
Reference in New Issue
Block a user