🎨 修改执行模块代码结构.

This commit is contained in:
lijiahang
2024-04-10 19:24:59 +08:00
parent 31af860d7b
commit 3249508808
22 changed files with 877 additions and 576 deletions

View File

@@ -1,5 +1,5 @@
### 批量执行命令
POST {{baseUrl}}/asset/exec/exec-command
POST {{baseUrl}}/asset/exec-command/exec
Content-Type: application/json
Authorization: {{token}}
@@ -11,9 +11,17 @@ Authorization: {{token}}
"hostIdList": [1]
}
### 批量执行命令
POST {{baseUrl}}/asset/exec-command/re-exec
Content-Type: application/json
Authorization: {{token}}
{
"logId": 1
}
### 中断执行命令
POST {{baseUrl}}/asset/exec/interrupt-command
POST {{baseUrl}}/asset/exec-command/interrupt
Content-Type: application/json
Authorization: {{token}}
@@ -22,19 +30,4 @@ Authorization: {{token}}
}
### 查看执行日志
POST {{baseUrl}}/asset/exec/tail-log
Content-Type: application/json
Authorization: {{token}}
{
"execId": 56
}
### 下载执行日志文件
GET {{baseUrl}}/asset/exec/download-log?id=83
Authorization: {{token}}
###

View File

@@ -0,0 +1,85 @@
package com.orion.ops.module.asset.controller;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.biz.operator.log.core.enums.ReturnType;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.asset.define.operator.ExecCommandOperatorType;
import com.orion.ops.module.asset.entity.request.exec.ExecCommandRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecInterruptRequest;
import com.orion.ops.module.asset.entity.request.exec.ReExecCommandRequest;
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
import com.orion.ops.module.asset.enums.ExecSourceEnum;
import com.orion.ops.module.asset.service.ExecCommandService;
import com.orion.ops.module.asset.service.ExecLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 批量执行
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/11 11:44
*/
@Tag(name = "asset - 批量执行服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/exec-command")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class ExecCommandController {
private static final String SOURCE = ExecSourceEnum.BATCH.name();
@Resource
private ExecCommandService execCommandService;
@Resource
private ExecLogService execLogService;
@OperatorLog(value = ExecCommandOperatorType.EXEC, ret = ReturnType.IGNORE)
@PostMapping("/exec")
@Operation(summary = "批量执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec-command:exec')")
public ExecLogVO execCommand(@Validated @RequestBody ExecCommandRequest request) {
return execCommandService.execCommand(request);
}
@OperatorLog(value = ExecCommandOperatorType.EXEC, ret = ReturnType.IGNORE)
@PostMapping("/re-exec")
@Operation(summary = "重新执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec-command:exec')")
public ExecLogVO reExecCommand(@Validated @RequestBody ReExecCommandRequest request) {
return execCommandService.reExecCommand(request.getLogId());
}
@OperatorLog(ExecCommandOperatorType.INTERRUPT_EXEC)
@PutMapping("/interrupt")
@Operation(summary = "中断执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec-command:interrupt')")
public HttpWrapper<?> interruptExecCommand(@RequestBody ExecInterruptRequest request) {
Long logId = Valid.notNull(request.getLogId());
execLogService.interruptExec(logId, SOURCE);
return HttpWrapper.ok();
}
@OperatorLog(ExecCommandOperatorType.INTERRUPT_HOST)
@PutMapping("/interrupt-host")
@Operation(summary = "中断执行主机命令")
@PreAuthorize("@ss.hasPermission('asset:exec-command:interrupt')")
public HttpWrapper<?> interruptHostExecCommand(@RequestBody ExecInterruptRequest request) {
Long hostLogId = Valid.notNull(request.getHostLogId());
execLogService.interruptHostExec(hostLogId, SOURCE);
return HttpWrapper.ok();
}
}

View File

@@ -0,0 +1,48 @@
### 查询批量执行日志
GET {{baseUrl}}/asset/exec-command-log/get?id=1
Authorization: {{token}}
### 分页查询批量执行日志
POST {{baseUrl}}/asset/exec-command-log/query
Content-Type: application/json
Authorization: {{token}}
{
"page": 1,
"limit": 10,
"id": "",
"userId": "",
"username": "",
"description": "",
"command": "",
"status": ""
}
### 删除批量执行日志
DELETE {{baseUrl}}/asset/exec-command-log/delete?id=1
Authorization: {{token}}
### 批量删除批量执行日志
DELETE {{baseUrl}}/asset/exec-command-log/batch-delete?idList=1,2,3
Authorization: {{token}}
### 查看执行日志
POST {{baseUrl}}/asset/exec-command-log/tail
Content-Type: application/json
Authorization: {{token}}
{
"execId": 56
}
### 下载执行日志文件
GET {{baseUrl}}/asset/exec-command-log/download?id=83
Authorization: {{token}}
###

View File

@@ -7,8 +7,9 @@ import com.orion.ops.framework.log.core.annotation.IgnoreLog;
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.module.asset.define.operator.ExecOperatorType;
import com.orion.ops.module.asset.define.operator.ExecCommandLogOperatorType;
import com.orion.ops.module.asset.entity.request.exec.ExecLogQueryRequest;
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;
@@ -24,6 +25,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -38,9 +40,11 @@ import java.util.List;
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/exec-log")
@RequestMapping("/asset/exec-command-log")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class ExecLogController {
public class ExecCommandLogController {
private static final String SOURCE = ExecSourceEnum.BATCH.name();
@Resource
private ExecLogService execLogService;
@@ -51,25 +55,25 @@ public class ExecLogController {
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query")
@Operation(summary = "分页查询执行日志")
@PreAuthorize("@ss.hasPermission('asset:exec-log:query')")
public DataGrid<ExecLogVO> getExecLogPage(@Validated(Page.class) @RequestBody ExecLogQueryRequest request) {
request.setSource(ExecSourceEnum.BATCH.name());
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:query')")
public DataGrid<ExecLogVO> getExecCommandLogPage(@Validated(Page.class) @RequestBody ExecLogQueryRequest request) {
request.setSource(SOURCE);
return execLogService.getExecLogPage(request);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/get")
@Operation(summary = "查询执行日志")
@PreAuthorize("@ss.hasPermission('asset:exec-log:query')")
public ExecLogVO getExecLog(@RequestParam("id") Long id) {
return execLogService.getExecLog(id, ExecSourceEnum.BATCH.name());
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:query')")
public ExecLogVO getExecCommandLog(@RequestParam("id") Long id) {
return execLogService.getExecLog(id, SOURCE);
}
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/host-list")
@Operation(summary = "查询全部执行主机日志")
@PreAuthorize("@ss.hasPermission('asset:exec-log:query')")
public List<ExecHostLogVO> getExecHostLogList(@RequestParam("logId") Long logId) {
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:query')")
public List<ExecHostLogVO> getExecCommandHostLogList(@RequestParam("logId") Long logId) {
return execHostLogService.getExecHostLogList(logId);
}
@@ -77,64 +81,80 @@ public class ExecLogController {
@GetMapping("/status")
@Operation(summary = "查询命令执行状态")
@Parameter(name = "idList", description = "idList", required = true)
@PreAuthorize("@ss.hasPermission('asset:exec-log:query')")
public ExecLogStatusVO getExecLogStatus(@RequestParam("idList") List<Long> idList) {
return execLogService.getExecLogStatus(idList);
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:query')")
public ExecLogStatusVO getExecCommandLogStatus(@RequestParam("idList") List<Long> idList) {
return execLogService.getExecLogStatus(idList, SOURCE);
}
@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());
@PreAuthorize("@ss.hasAnyPermission('asset:exec-command-log:query', 'asset:exec-command:exec')")
public List<ExecLogVO> getExecCommandLogHistory(@Validated(Page.class) ExecLogQueryRequest request) {
request.setSource(SOURCE);
request.setUserId(SecurityUtils.getLoginUserId());
return execLogService.getExecHistory(request);
}
@OperatorLog(ExecOperatorType.DELETE_LOG)
@OperatorLog(ExecCommandLogOperatorType.DELETE)
@DeleteMapping("/delete")
@Operation(summary = "删除执行日志")
@Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('asset:exec-log:delete')")
public Integer deleteExecLog(@RequestParam("id") Long id) {
return execLogService.deleteExecLogById(id);
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:delete')")
public Integer deleteExecCommandLog(@RequestParam("id") Long id) {
return execLogService.deleteExecLogById(id, SOURCE);
}
@OperatorLog(ExecOperatorType.DELETE_LOG)
@OperatorLog(ExecCommandLogOperatorType.DELETE)
@DeleteMapping("/batch-delete")
@Operation(summary = "删除执行日志")
@Parameter(name = "idList", description = "idList", required = true)
@PreAuthorize("@ss.hasPermission('asset:exec-log:delete')")
public Integer batchDeleteExecLog(@RequestParam("idList") List<Long> idList) {
return execLogService.deleteExecLogByIdList(idList);
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:delete')")
public Integer batchDeleteExecCommandLog(@RequestParam("idList") List<Long> idList) {
return execLogService.deleteExecLogByIdList(idList, SOURCE);
}
@OperatorLog(ExecOperatorType.DELETE_HOST_LOG)
@OperatorLog(ExecCommandLogOperatorType.DELETE_HOST)
@DeleteMapping("/delete-host")
@Operation(summary = "删除执行主机日志")
@Parameter(name = "id", description = "id", required = true)
@PreAuthorize("@ss.hasPermission('asset:exec-log:delete')")
public Integer deleteExecHostLog(@RequestParam("id") Long id) {
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:delete')")
public Integer deleteExecCommandHostLog(@RequestParam("id") Long id) {
return execHostLogService.deleteExecHostLogById(id);
}
@PostMapping("/query-count")
@Operation(summary = "查询执行日志数量")
@PreAuthorize("@ss.hasPermission('asset:exec-log:management:clear')")
public Long getExecLogCount(@RequestBody ExecLogQueryRequest request) {
request.setSource(ExecSourceEnum.BATCH.name());
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:management:clear')")
public Long getExecCommandLogCount(@RequestBody ExecLogQueryRequest request) {
request.setSource(SOURCE);
return execLogService.queryExecLogCount(request);
}
@OperatorLog(ExecOperatorType.CLEAR_LOG)
@OperatorLog(ExecCommandLogOperatorType.CLEAR)
@PostMapping("/clear")
@Operation(summary = "清空执行日志")
@PreAuthorize("@ss.hasPermission('asset:exec-log:management:clear')")
public Integer clearExecLog(@RequestBody ExecLogQueryRequest request) {
request.setSource(ExecSourceEnum.BATCH.name());
@PreAuthorize("@ss.hasPermission('asset:exec-command-log:management:clear')")
public Integer clearExecCommandLog(@RequestBody ExecLogQueryRequest request) {
request.setSource(SOURCE);
return execLogService.clearExecLog(request);
}
@PostMapping("/tail")
@Operation(summary = "查看执行日志")
@PreAuthorize("@ss.hasAnyPermission('asset:exec-command-log:query', 'asset:exec-command:exec')")
public String getExecCommandLogTailToken(@Validated @RequestBody ExecLogTailRequest request) {
request.setSource(SOURCE);
return execLogService.getExecLogTailToken(request);
}
@OperatorLog(ExecCommandLogOperatorType.DOWNLOAD)
@GetMapping("/download")
@Operation(summary = "下载执行日志")
@PreAuthorize("@ss.hasAnyPermission('asset:exec-command-log:query', 'asset:exec-command:exec')")
public void downloadExecCommandLogFile(@RequestParam("id") Long id, HttpServletResponse response) {
execLogService.downloadLogFile(id, SOURCE, response);
}
}

View File

@@ -1,95 +0,0 @@
package com.orion.ops.module.asset.controller;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.biz.operator.log.core.enums.ReturnType;
import com.orion.ops.framework.common.utils.Valid;
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.ExecCommandRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecInterruptRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecLogTailRequest;
import com.orion.ops.module.asset.entity.request.exec.ReExecCommandRequest;
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
import com.orion.ops.module.asset.service.ExecService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
/**
* 批量执行
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/11 11:44
*/
@Tag(name = "asset - 批量执行服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/asset/exec")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class ExecController {
@Resource
private ExecService execService;
@OperatorLog(value = ExecOperatorType.EXEC_COMMAND, ret = ReturnType.IGNORE)
@PostMapping("/exec-command")
@Operation(summary = "批量执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec:exec-command')")
public ExecLogVO execCommand(@Validated @RequestBody ExecCommandRequest request) {
return execService.execCommand(request);
}
@OperatorLog(value = ExecOperatorType.EXEC_COMMAND, ret = ReturnType.IGNORE)
@PostMapping("/re-exec-command")
@Operation(summary = "重新执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec:exec-command')")
public ExecLogVO reExecCommand(@Validated @RequestBody ReExecCommandRequest request) {
return execService.reExecCommand(request.getLogId());
}
@OperatorLog(ExecOperatorType.INTERRUPT_EXEC)
@PutMapping("/interrupt")
@Operation(summary = "中断执行命令")
@PreAuthorize("@ss.hasPermission('asset:exec:interrupt-exec')")
public HttpWrapper<?> interruptExec(@RequestBody ExecInterruptRequest request) {
Long logId = Valid.notNull(request.getLogId());
execService.interruptExec(logId);
return HttpWrapper.ok();
}
@OperatorLog(ExecOperatorType.INTERRUPT_HOST)
@PutMapping("/interrupt-host")
@Operation(summary = "中断执行主机命令")
@PreAuthorize("@ss.hasPermission('asset:exec:interrupt-exec')")
public HttpWrapper<?> interruptHostExec(@RequestBody ExecInterruptRequest request) {
Long hostLogId = Valid.notNull(request.getHostLogId());
execService.interruptHostExec(hostLogId);
return HttpWrapper.ok();
}
@PostMapping("/tail-log")
@Operation(summary = "查看执行日志")
@PreAuthorize("@ss.hasAnyPermission('asset:exec:exec-command', 'asset:exec-log:query')")
public String getExecLogTailToken(@Validated @RequestBody ExecLogTailRequest request) {
return execService.getExecLogTailToken(request);
}
@OperatorLog(ExecOperatorType.DOWNLOAD_HOST_LOG)
@GetMapping("/download-log")
@Operation(summary = "下载执行日志")
@PreAuthorize("@ss.hasAnyPermission('asset:exec:exec-command', 'asset:exec-log:query')")
public void downloadExecLogFile(@RequestParam("id") Long id, HttpServletResponse response) {
execService.downloadLogFile(id, response);
}
}

View File

@@ -1,34 +0,0 @@
### 查询批量执行日志
GET {{baseUrl}}/asset/exec-log/get?id=1
Authorization: {{token}}
### 分页查询批量执行日志
POST {{baseUrl}}/asset/exec-log/query
Content-Type: application/json
Authorization: {{token}}
{
"page": 1,
"limit": 10,
"id": "",
"userId": "",
"username": "",
"source": "",
"sourceId": "",
"description": "",
"command": "",
"status": ""
}
### 删除批量执行日志
DELETE {{baseUrl}}/asset/exec-log/delete?id=1
Authorization: {{token}}
### 批量删除批量执行日志
DELETE {{baseUrl}}/asset/exec-log/batch-delete?idList=1,2,3
Authorization: {{token}}
###

View File

@@ -67,6 +67,7 @@ public class HostConnectLogController {
@PostMapping("/query-count")
@Operation(summary = "查询主机连接日志数量")
@PreAuthorize("@ss.hasPermission('asset:host-connect-log:management:clear')")
public Long getHostConnectLogCount(@RequestBody HostConnectLogQueryRequest request) {
return hostConnectLogService.getHostConnectLogCount(request);
}

View File

@@ -17,6 +17,22 @@ import java.util.List;
@Mapper
public interface ExecLogDAO extends IMapper<ExecLogDO> {
/**
* 通过 id 和 source 查询
*
* @param id id
* @param source source
* @return log
*/
default ExecLogDO selectByIdSource(Long id, String source) {
return this.of()
.createWrapper()
.eq(ExecLogDO::getId, id)
.eq(ExecLogDO::getSource, source)
.then()
.getOne();
}
/**
* 获取执行历史
*

View File

@@ -0,0 +1,38 @@
package com.orion.ops.module.asset.define.operator;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.H;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.L;
/**
* 批量执行 操作记录类型
*
* @author Jiahang Li
* @version 1.0.1
* @since 2024-3-11 11:31
*/
@Module("asset:exec-command-log")
public class ExecCommandLogOperatorType extends InitializingOperatorTypes {
public static final String DELETE = "exec-command-log:delete";
public static final String DELETE_HOST = "exec-command-log:delete-host";
public static final String CLEAR = "exec-command-log:clear";
public static final String DOWNLOAD = "exec-command-log:download";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(H, DELETE, "删除执行记录 <sb>${count}</sb> 条"),
new OperatorType(H, CLEAR, "清理执行记录 <sb>${count}</sb> 条"),
new OperatorType(H, DELETE_HOST, "删除主机执行记录 <sb>${logId}</sb> <sb>${hostName}</sb>"),
new OperatorType(L, DOWNLOAD, "下载主机执行日志 <sb>${logId}</sb> <sb>${hostName}</sb>"),
};
}
}

View File

@@ -0,0 +1,34 @@
package com.orion.ops.module.asset.define.operator;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.M;
/**
* 批量执行 操作记录类型
*
* @author Jiahang Li
* @version 1.0.1
* @since 2024-3-11 11:31
*/
@Module("asset:exec-command-log")
public class ExecCommandOperatorType extends InitializingOperatorTypes {
public static final String EXEC = "exec-command:exec";
public static final String INTERRUPT_EXEC = "exec-command:interrupt-exec";
public static final String INTERRUPT_HOST = "exec-command:interrupt-host";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(M, EXEC, "执行主机命令"),
new OperatorType(M, INTERRUPT_EXEC, "中断执行命令"),
new OperatorType(M, INTERRUPT_HOST, "中断主机执行命令 <sb>${logId}</sb> <sb>${hostName}</sb>"),
};
}
}

View File

@@ -1,46 +0,0 @@
package com.orion.ops.module.asset.define.operator;
import com.orion.ops.framework.biz.operator.log.core.annotation.Module;
import com.orion.ops.framework.biz.operator.log.core.factory.InitializingOperatorTypes;
import com.orion.ops.framework.biz.operator.log.core.model.OperatorType;
import static com.orion.ops.framework.biz.operator.log.core.enums.OperatorRiskLevel.*;
/**
* 批量执行 操作记录类型
*
* @author Jiahang Li
* @version 1.0.1
* @since 2024-3-11 11:31
*/
@Module("asset:exec")
public class ExecOperatorType extends InitializingOperatorTypes {
public static final String EXEC_COMMAND = "exec:exec-command";
public static final String INTERRUPT_EXEC = "exec:interrupt-exec";
public static final String INTERRUPT_HOST = "exec:interrupt-host";
public static final String DELETE_LOG = "exec:delete-log";
public static final String CLEAR_LOG = "exec:clear-log";
public static final String DELETE_HOST_LOG = "exec:delete-host-log";
public static final String DOWNLOAD_HOST_LOG = "exec:download-host-log";
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(M, EXEC_COMMAND, "执行主机命令"),
new OperatorType(M, INTERRUPT_EXEC, "中断执行命令"),
new OperatorType(M, INTERRUPT_HOST, "中断主机执行命令 <sb>${logId}</sb> <sb>${hostName}</sb>"),
new OperatorType(H, DELETE_LOG, "删除执行记录 <sb>${count}</sb> 条"),
new OperatorType(H, CLEAR_LOG, "清理执行记录 <sb>${count}</sb> 条"),
new OperatorType(H, DELETE_HOST_LOG, "删除主机执行记录 <sb>${logId}</sb> <sb>${hostName}</sb>"),
new OperatorType(L, DOWNLOAD_HOST_LOG, "下载主机执行日志 <sb>${logId}</sb> <sb>${hostName}</sb>"),
};
}
}

View File

@@ -23,6 +23,9 @@ import java.util.List;
@Schema(name = "ExecLogTailRequest", description = "执行日志查看 请求对象")
public class ExecLogTailRequest {
@Schema(description = "执行来源")
private String source;
@NotNull
@Schema(description = "执行id")
private Long execId;

View File

@@ -3,7 +3,7 @@ package com.orion.ops.module.asset.interceptor;
import com.orion.lang.utils.Urls;
import com.orion.ops.framework.common.constant.ExtraFieldConst;
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
import com.orion.ops.module.asset.service.ExecService;
import com.orion.ops.module.asset.service.ExecLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
@@ -26,7 +26,7 @@ import java.util.Map;
public class ExecLogTailInterceptor implements HandshakeInterceptor {
@Resource
private ExecService execService;
private ExecLogService execLogService;
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
@@ -34,7 +34,7 @@ public class ExecLogTailInterceptor implements HandshakeInterceptor {
String token = Urls.getUrlSource(request.getURI().getPath());
log.info("ExecLogTailInterceptor-beforeHandshake start token: {}", token);
// 获取日志数据
ExecLogTailDTO info = execService.getExecLogTailInfo(token);
ExecLogTailDTO info = execLogService.getExecLogTailInfo(token);
if (info == null) {
log.error("ExecLogTailInterceptor-beforeHandshake absent token: {}", token);
return false;

View File

@@ -0,0 +1,40 @@
package com.orion.ops.module.asset.service;
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.ExecLogVO;
/**
* 批量执行服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/11 12:02
*/
public interface ExecCommandService {
/**
* 批量执行命令
*
* @param request request
* @return result
*/
ExecLogVO execCommand(ExecCommandRequest request);
/**
* 批量执行命令
*
* @param request request
* @return result
*/
ExecLogVO execCommandWithSource(ExecCommandExecRequest request);
/**
* 重新执行命令
*
* @param logId logId
* @return result
*/
ExecLogVO reExecCommand(Long logId);
}

View File

@@ -1,10 +1,13 @@
package com.orion.ops.module.asset.service;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
import com.orion.ops.module.asset.entity.request.exec.ExecLogQueryRequest;
import com.orion.ops.module.asset.entity.request.exec.ExecLogTailRequest;
import com.orion.ops.module.asset.entity.vo.ExecLogStatusVO;
import com.orion.ops.module.asset.entity.vo.ExecLogVO;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
@@ -45,9 +48,28 @@ public interface ExecLogService {
* 获取执行日志状态
*
* @param idList idList
* @param source source
* @return status
*/
ExecLogStatusVO getExecLogStatus(List<Long> idList);
ExecLogStatusVO getExecLogStatus(List<Long> idList, String source);
/**
* 删除执行日志
*
* @param id id
* @param source source
* @return effect
*/
Integer deleteExecLogById(Long id, String source);
/**
* 批量删除批量执行日志
*
* @param idList idList
* @param source source
* @return effect
*/
Integer deleteExecLogByIdList(List<Long> idList, String source);
/**
* 查询批量执行日志数量
@@ -57,22 +79,6 @@ public interface ExecLogService {
*/
Long queryExecLogCount(ExecLogQueryRequest request);
/**
* 删除执行日志
*
* @param id id
* @return effect
*/
Integer deleteExecLogById(Long id);
/**
* 批量删除批量执行日志
*
* @param idList idList
* @return effect
*/
Integer deleteExecLogByIdList(List<Long> idList);
/**
* 清理执行日志
*
@@ -81,4 +87,45 @@ public interface ExecLogService {
*/
Integer clearExecLog(ExecLogQueryRequest request);
/**
* 中断命令执行
*
* @param logId logId
* @param source source
*/
void interruptExec(Long logId, String source);
/**
* 中断命令执行
*
* @param hostLogId hostLogId
* @param source source
*/
void interruptHostExec(Long hostLogId, String source);
/**
* 查看执行日志
*
* @param request request
* @return token
*/
String getExecLogTailToken(ExecLogTailRequest request);
/**
* 获取查看执行日志参数
*
* @param token token
* @return log
*/
ExecLogTailDTO getExecLogTailInfo(String token);
/**
* 下载执行日志文件
*
* @param id id
* @param source source
* @param response response
*/
void downloadLogFile(Long id, String source, HttpServletResponse response);
}

View File

@@ -1,82 +0,0 @@
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;
import javax.servlet.http.HttpServletResponse;
/**
* 批量执行服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/3/11 12:02
*/
public interface ExecService {
/**
* 批量执行命令
*
* @param request request
* @return result
*/
ExecLogVO execCommand(ExecCommandRequest request);
/**
* 批量执行命令
*
* @param request request
* @return result
*/
ExecLogVO execCommandWithSource(ExecCommandExecRequest request);
/**
* 重新执行命令
*
* @param logId logId
* @return result
*/
ExecLogVO reExecCommand(Long logId);
/**
* 中断命令执行
*
* @param logId logId
*/
void interruptExec(Long logId);
/**
* 中断命令执行
*
* @param hostLogId hostLogId
*/
void interruptHostExec(Long hostLogId);
/**
* 查看执行日志
*
* @param request request
* @return token
*/
String getExecLogTailToken(ExecLogTailRequest request);
/**
* 获取查看执行日志参数
*
* @param token token
* @return log
*/
ExecLogTailDTO getExecLogTailInfo(String token);
/**
* 下载执行日志文件
*
* @param id id
* @param response response
*/
void downloadLogFile(Long id, HttpServletResponse response);
}

View File

@@ -1,15 +1,11 @@
package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.function.Functions;
import com.orion.lang.id.UUIds;
import com.orion.lang.utils.Objects1;
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.io.Files1;
import com.orion.lang.utils.json.matcher.NoMatchStrategy;
import com.orion.lang.utils.json.matcher.ReplacementFormatter;
import com.orion.lang.utils.json.matcher.ReplacementFormatters;
@@ -17,11 +13,9 @@ 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.FieldConst;
import com.orion.ops.framework.common.file.FileClient;
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;
@@ -29,19 +23,14 @@ import com.orion.ops.module.asset.convert.ExecLogConvert;
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.define.cache.ExecCacheKeyDefine;
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.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;
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.ExecSourceEnum;
import com.orion.ops.module.asset.enums.ExecStatusEnum;
@@ -49,22 +38,17 @@ import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
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.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;
import com.orion.ops.module.asset.service.AssetAuthorizedDataService;
import com.orion.ops.module.asset.service.ExecService;
import com.orion.ops.module.asset.service.HostConfigService;
import com.orion.web.servlet.web.Servlets;
import com.orion.ops.module.asset.service.ExecCommandService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -76,7 +60,7 @@ import java.util.stream.Collectors;
*/
@Slf4j
@Service
public class ExecServiceImpl implements ExecService {
public class ExecCommandServiceImpl implements ExecCommandService {
private static final ReplacementFormatter FORMATTER = ReplacementFormatters.create("@{{ ", " }}")
.noMatchStrategy(NoMatchStrategy.KEEP);
@@ -98,12 +82,6 @@ public class ExecServiceImpl implements ExecService {
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@Resource
private HostConfigService hostConfigService;
@Resource
private ExecTaskManager execTaskManager;
@Override
@Transactional(rollbackFor = Exception.class)
public ExecLogVO execCommand(ExecCommandRequest request) {
@@ -186,7 +164,7 @@ public class ExecServiceImpl implements ExecService {
public ExecLogVO reExecCommand(Long logId) {
log.info("ExecService.reExecCommand start logId: {}", logId);
// 获取执行记录
ExecLogDO execLog = execLogDAO.selectById(logId);
ExecLogDO execLog = execLogDAO.selectByIdSource(logId, ExecSourceEnum.BATCH.name());
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
// 获取执行主机
List<ExecHostLogDO> hostLogs = execHostLogDAO.selectByLogId(logId);
@@ -205,200 +183,6 @@ public class ExecServiceImpl implements ExecService {
return this.execCommand(request);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void interruptExec(Long logId) {
log.info("ExecService.interruptExec start logId: {}", logId);
// 获取执行记录
ExecLogDO execLog = execLogDAO.selectById(logId);
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
// 检查状态
if (!ExecStatusEnum.of(execLog.getStatus()).isCloseable()) {
return;
}
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecService.interruptExec interrupted logId: {}", logId);
// 中断
task.interrupted();
} else {
log.info("ExecService.interruptExec updateStatus start logId: {}", logId);
// 不存在则直接修改状态
ExecLogDO updateExec = new ExecLogDO();
updateExec.setId(logId);
updateExec.setStatus(ExecStatusEnum.COMPLETED.name());
updateExec.setFinishTime(new Date());
int effect = execLogDAO.updateById(updateExec);
// 更新主机状态
ExecHostLogDO updateHost = new ExecHostLogDO();
updateHost.setStatus(ExecHostStatusEnum.INTERRUPTED.name());
updateHost.setFinishTime(new Date());
LambdaQueryWrapper<ExecHostLogDO> updateHostWrapper = execHostLogDAO.lambda()
.eq(ExecHostLogDO::getLogId, logId)
.in(ExecHostLogDO::getStatus, ExecHostStatusEnum.CLOSEABLE_STATUS);
effect += execHostLogDAO.update(updateHost, updateHostWrapper);
log.info("ExecService.interruptExec updateStatus finish logId: {}, effect: {}", logId, effect);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void interruptHostExec(Long hostLogId) {
log.info("ExecService.interruptHostExec start hostLogId: {}", hostLogId);
// 获取执行记录
ExecHostLogDO hostLog = execHostLogDAO.selectById(hostLogId);
Valid.notNull(hostLog, ErrorMessage.DATA_ABSENT);
Long logId = hostLog.getLogId();
// 添加日志参数
OperatorLogs.add(OperatorLogs.LOG_ID, logId);
OperatorLogs.add(OperatorLogs.HOST_NAME, hostLog.getHostName());
// 检查状态
if (!ExecHostStatusEnum.of(hostLog.getStatus()).isCloseable()) {
return;
}
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecService.interruptHostExec interrupted logId: {}, hostLogId: {}", logId, hostLogId);
IExecCommandHandler handler = task.getHandlers()
.stream()
.filter(s -> s.getHostId().equals(hostLog.getHostId()))
.findFirst()
.orElse(null);
// 中断
if (handler != null) {
handler.interrupted();
}
} else {
log.info("ExecService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId);
// 不存在则直接修改状态
ExecHostLogDO updateHost = new ExecHostLogDO();
updateHost.setId(hostLogId);
updateHost.setStatus(ExecHostStatusEnum.INTERRUPTED.name());
updateHost.setFinishTime(new Date());
int effect = execHostLogDAO.updateById(updateHost);
// 查询执行状态
ExecLogDO execLog = execLogDAO.selectById(logId);
if (ExecStatusEnum.of(execLog.getStatus()).isCloseable()) {
// 状态可修改则需要检查其他主机任务是否已经完成
Long closeableCount = execHostLogDAO.of()
.createWrapper()
.eq(ExecHostLogDO::getLogId, logId)
.in(ExecHostLogDO::getStatus, ExecHostStatusEnum.CLOSEABLE_STATUS)
.then()
.count();
if (closeableCount == 0) {
// 修改任务状态
ExecLogDO updateExec = new ExecLogDO();
updateExec.setId(logId);
updateExec.setStatus(ExecStatusEnum.COMPLETED.name());
updateExec.setFinishTime(new Date());
effect += execLogDAO.updateById(updateExec);
}
}
log.info("ExecService.interruptHostExec updateStatus finish logId: {}, hostLogId: {}, effect: {}", logId, hostLogId, effect);
}
}
@Override
public String getExecLogTailToken(ExecLogTailRequest request) {
Long execId = request.getExecId();
List<Long> hostExecIdList = request.getHostExecIdList();
log.info("ExecService.getExecLogTailToken start execId: {}, hostExecIdList: {}", execId, hostExecIdList);
// 查询执行日志
ExecLogDO execLog = execLogDAO.selectById(execId);
Valid.notNull(execLog, ErrorMessage.LOG_ABSENT);
// 查询主机日志
List<ExecHostLogDO> hostLogs;
if (hostExecIdList == null) {
hostLogs = execHostLogDAO.selectByLogId(execId);
} else {
hostLogs = execHostLogDAO.of()
.createWrapper()
.eq(ExecHostLogDO::getLogId, execId)
.in(ExecHostLogDO::getId, hostExecIdList)
.then()
.list();
}
Valid.notEmpty(hostLogs, ErrorMessage.LOG_ABSENT);
// 获取编码集
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()));
// 生成缓存
String token = UUIds.random19();
String cacheKey = ExecCacheKeyDefine.EXEC_TAIL.format(token);
ExecLogTailDTO cache = ExecLogTailDTO.builder()
.token(token)
.id(execId)
.userId(SecurityUtils.getLoginUserId())
.hosts(hostLogs.stream()
.map(s -> ExecHostLogTailDTO.builder()
.id(s.getId())
.hostId(s.getHostId())
.path(s.getLogPath())
.charset(Optional.ofNullable(configMap.get(s.getHostId()))
.map(HostConfigVO::getConfig)
.map(c -> c.get(FieldConst.CHARSET))
.map(Objects1::toString)
.orElse(Const.UTF_8))
.build())
.collect(Collectors.toList()))
.build();
// 设置缓存
RedisStrings.setJson(cacheKey, ExecCacheKeyDefine.EXEC_TAIL, cache);
log.info("ExecService.getExecLogTailToken finish token: {}, execId: {}, hostExecIdList: {}", token, execId, hostExecIdList);
return token;
}
@Override
public ExecLogTailDTO getExecLogTailInfo(String token) {
String cacheKey = ExecCacheKeyDefine.EXEC_TAIL.format(token);
// 获取缓存
ExecLogTailDTO tail = RedisStrings.getJson(cacheKey, ExecCacheKeyDefine.EXEC_TAIL);
if (tail != null) {
// 删除缓存
RedisStrings.delete(cacheKey);
}
return tail;
}
@Override
public void downloadLogFile(Long id, HttpServletResponse response) {
log.info("ExecService.downloadLogFile id: {}", id);
try {
// 获取主机执行日志
ExecHostLogDO hostLog = execHostLogDAO.selectById(id);
Valid.notNull(hostLog, ErrorMessage.LOG_ABSENT);
String logPath = hostLog.getLogPath();
Valid.notNull(logPath, ErrorMessage.LOG_ABSENT);
// 设置日志参数
OperatorLogs.add(OperatorLogs.LOG_ID, hostLog.getLogId());
OperatorLogs.add(OperatorLogs.HOST_ID, hostLog.getHostId());
OperatorLogs.add(OperatorLogs.HOST_NAME, hostLog.getHostName());
// 获取日志
InputStream in = logsFileClient.getContentInputStream(logPath);
// 返回
Servlets.transfer(response, in, Files1.getFileName(logPath));
} catch (Exception e) {
log.error("ExecService.downloadLogFile error id: {}", id, e);
String errorMessage = ErrorMessage.FILE_READ_ERROR;
if (e instanceof InvalidArgumentException) {
errorMessage = e.getMessage();
}
// 响应错误信息
try {
Servlets.transfer(response, Strings.bytes(errorMessage), Const.ERROR_LOG);
} catch (Exception ex) {
log.error("ExecService.downloadLogFile transfer-error id: {}", id, ex);
}
}
}
/**
* 开始执行命令
*

View File

@@ -26,7 +26,7 @@ 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 com.orion.ops.module.asset.service.ExecCommandService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -62,7 +62,7 @@ public class ExecJobServiceImpl implements ExecJobService {
private ExecJobHostService execJobHostService;
@Resource
private ExecService execService;
private ExecCommandService execCommandService;
@Resource
private AssetAuthorizedDataService assetAuthorizedDataService;
@@ -271,7 +271,7 @@ public class ExecJobServiceImpl implements ExecJobService {
.parameterSchema(job.getParameterSchema())
.hostIdList(hostIdList)
.build();
ExecLogVO execResult = execService.execCommandWithSource(exec);
ExecLogVO execResult = execCommandService.execCommandWithSource(exec);
// 更新最近执行的任务id
ExecJobDO updateRecent = new ExecJobDO();
updateRecent.setId(id);

View File

@@ -3,33 +3,55 @@ package com.orion.ops.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.id.UUIds;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.Objects1;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.collect.Lists;
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.file.FileClient;
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.ExecHostLogConvert;
import com.orion.ops.module.asset.convert.ExecLogConvert;
import com.orion.ops.module.asset.dao.ExecHostLogDAO;
import com.orion.ops.module.asset.dao.ExecLogDAO;
import com.orion.ops.module.asset.define.cache.ExecCacheKeyDefine;
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.dto.ExecHostLogTailDTO;
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
import com.orion.ops.module.asset.entity.request.exec.ExecLogQueryRequest;
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.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;
import com.orion.ops.module.asset.service.ExecHostLogService;
import com.orion.ops.module.asset.service.ExecLogService;
import com.orion.ops.module.asset.service.HostConfigService;
import com.orion.web.servlet.web.Servlets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -55,6 +77,12 @@ public class ExecLogServiceImpl implements ExecLogService {
@Resource
private ExecTaskManager execTaskManager;
@Resource
private HostConfigService hostConfigService;
@Resource
private FileClient logsFileClient;
@Override
public DataGrid<ExecLogVO> getExecLogPage(ExecLogQueryRequest request) {
// 条件
@@ -68,12 +96,7 @@ public class ExecLogServiceImpl implements ExecLogService {
@Override
public ExecLogVO getExecLog(Long id, String source) {
// 查询执行日志
ExecLogDO row = execLogDAO.of()
.createValidateWrapper()
.eq(ExecLogDO::getId, id)
.eq(ExecLogDO::getSource, source)
.then()
.getOne();
ExecLogDO row = execLogDAO.selectByIdSource(id, source);
Valid.notNull(row, ErrorMessage.LOG_ABSENT);
// 查询执行主机
List<ExecHostLogDO> hosts = execHostLogDAO.selectByLogId(id);
@@ -113,7 +136,7 @@ public class ExecLogServiceImpl implements ExecLogService {
}
@Override
public ExecLogStatusVO getExecLogStatus(List<Long> idList) {
public ExecLogStatusVO getExecLogStatus(List<Long> idList, String source) {
// 查询执行状态
List<ExecLogVO> logList = execLogDAO.of()
.createWrapper()
@@ -122,6 +145,7 @@ public class ExecLogServiceImpl implements ExecLogService {
ExecLogDO::getStartTime,
ExecLogDO::getFinishTime)
.in(ExecLogDO::getId, idList)
.eq(ExecLogDO::getSource, source)
.then()
.list(ExecLogConvert.MAPPER::to);
// 查询主机状态
@@ -144,17 +168,12 @@ public class ExecLogServiceImpl implements ExecLogService {
.build();
}
@Override
public Long queryExecLogCount(ExecLogQueryRequest request) {
return execLogDAO.selectCount(this.buildQueryWrapper(request));
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deleteExecLogById(Long id) {
public Integer deleteExecLogById(Long id, String source) {
log.info("ExecLogService-deleteExecLogById id: {}", id);
// 检查数据是否存在
ExecLogDO record = execLogDAO.selectById(id);
ExecLogDO record = execLogDAO.selectByIdSource(id, source);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 中断命令执行
this.interruptedTask(Lists.singleton(id));
@@ -170,8 +189,16 @@ public class ExecLogServiceImpl implements ExecLogService {
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deleteExecLogByIdList(List<Long> idList) {
public Integer deleteExecLogByIdList(List<Long> idList, String source) {
log.info("ExecLogService-deleteExecLogByIdList idList: {}", idList);
int count = execLogDAO.of()
.createWrapper()
.in(ExecLogDO::getId, idList)
.eq(ExecLogDO::getSource, source)
.then()
.count()
.intValue();
Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED);
// 中断命令执行
this.interruptedTask(idList);
// 删除执行日志
@@ -184,6 +211,11 @@ public class ExecLogServiceImpl implements ExecLogService {
return effect;
}
@Override
public Long queryExecLogCount(ExecLogQueryRequest request) {
return execLogDAO.selectCount(this.buildQueryWrapper(request));
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer clearExecLog(ExecLogQueryRequest request) {
@@ -210,6 +242,204 @@ public class ExecLogServiceImpl implements ExecLogService {
return effect;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void interruptExec(Long logId, String source) {
log.info("ExecLogService.interruptExec start logId: {}, source: {}", logId, source);
// 获取执行记录
ExecLogDO execLog = execLogDAO.selectByIdSource(logId, source);
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
// 检查状态
if (!ExecStatusEnum.of(execLog.getStatus()).isCloseable()) {
return;
}
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecLogService.interruptExec interrupted logId: {}", logId);
// 中断
task.interrupted();
} else {
log.info("ExecLogService.interruptExec updateStatus start logId: {}", logId);
// 不存在则直接修改状态
ExecLogDO updateExec = new ExecLogDO();
updateExec.setId(logId);
updateExec.setStatus(ExecStatusEnum.COMPLETED.name());
updateExec.setFinishTime(new Date());
int effect = execLogDAO.updateById(updateExec);
// 更新主机状态
ExecHostLogDO updateHost = new ExecHostLogDO();
updateHost.setStatus(ExecHostStatusEnum.INTERRUPTED.name());
updateHost.setFinishTime(new Date());
LambdaQueryWrapper<ExecHostLogDO> updateHostWrapper = execHostLogDAO.lambda()
.eq(ExecHostLogDO::getLogId, logId)
.in(ExecHostLogDO::getStatus, ExecHostStatusEnum.CLOSEABLE_STATUS);
effect += execHostLogDAO.update(updateHost, updateHostWrapper);
log.info("ExecLogService.interruptExec updateStatus finish logId: {}, effect: {}", logId, effect);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void interruptHostExec(Long hostLogId, String source) {
log.info("ExecLogService.interruptHostExec start hostLogId: {}, source: {}", hostLogId, source);
// 获取执行记录
ExecHostLogDO hostLog = execHostLogDAO.selectById(hostLogId);
Valid.notNull(hostLog, ErrorMessage.DATA_ABSENT);
Long logId = hostLog.getLogId();
ExecLogDO execLog = execLogDAO.selectByIdSource(logId, source);
Valid.notNull(execLog, ErrorMessage.DATA_ABSENT);
// 添加日志参数
OperatorLogs.add(OperatorLogs.LOG_ID, logId);
OperatorLogs.add(OperatorLogs.HOST_NAME, hostLog.getHostName());
// 检查状态
if (!ExecHostStatusEnum.of(hostLog.getStatus()).isCloseable()) {
return;
}
// 中断执行
IExecTaskHandler task = execTaskManager.getTask(logId);
if (task != null) {
log.info("ExecLogService.interruptHostExec interrupted logId: {}, hostLogId: {}", logId, hostLogId);
IExecCommandHandler handler = task.getHandlers()
.stream()
.filter(s -> s.getHostId().equals(hostLog.getHostId()))
.findFirst()
.orElse(null);
// 中断
if (handler != null) {
handler.interrupted();
}
} else {
log.info("ExecLogService.interruptHostExec updateStatus start logId: {}, hostLogId: {}", logId, hostLogId);
// 不存在则直接修改状态
ExecHostLogDO updateHost = new ExecHostLogDO();
updateHost.setId(hostLogId);
updateHost.setStatus(ExecHostStatusEnum.INTERRUPTED.name());
updateHost.setFinishTime(new Date());
int effect = execHostLogDAO.updateById(updateHost);
// 检查是否可关闭
if (ExecStatusEnum.of(execLog.getStatus()).isCloseable()) {
// 状态可修改则需要检查其他主机任务是否已经完成
Long closeableCount = execHostLogDAO.of()
.createWrapper()
.eq(ExecHostLogDO::getLogId, logId)
.in(ExecHostLogDO::getStatus, ExecHostStatusEnum.CLOSEABLE_STATUS)
.then()
.count();
if (closeableCount == 0) {
// 修改任务状态
ExecLogDO updateExec = new ExecLogDO();
updateExec.setId(logId);
updateExec.setStatus(ExecStatusEnum.COMPLETED.name());
updateExec.setFinishTime(new Date());
effect += execLogDAO.updateById(updateExec);
}
}
log.info("ExecLogService.interruptHostExec updateStatus finish logId: {}, hostLogId: {}, effect: {}", logId, hostLogId, effect);
}
}
@Override
public String getExecLogTailToken(ExecLogTailRequest request) {
String source = request.getSource();
Long execId = request.getExecId();
List<Long> hostExecIdList = request.getHostExecIdList();
log.info("ExecLogService.getExecLogTailToken start execId: {}, hostExecIdList: {}", execId, hostExecIdList);
// 查询执行日志
ExecLogDO execLog = execLogDAO.selectByIdSource(execId, source);
Valid.notNull(execLog, ErrorMessage.LOG_ABSENT);
// 查询主机日志
List<ExecHostLogDO> hostLogs;
if (hostExecIdList == null) {
hostLogs = execHostLogDAO.selectByLogId(execId);
} else {
hostLogs = execHostLogDAO.of()
.createWrapper()
.eq(ExecHostLogDO::getLogId, execId)
.in(ExecHostLogDO::getId, hostExecIdList)
.then()
.list();
}
Valid.notEmpty(hostLogs, ErrorMessage.LOG_ABSENT);
// 获取编码集
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()));
// 生成缓存
String token = UUIds.random19();
String cacheKey = ExecCacheKeyDefine.EXEC_TAIL.format(token);
ExecLogTailDTO cache = ExecLogTailDTO.builder()
.token(token)
.id(execId)
.userId(SecurityUtils.getLoginUserId())
.hosts(hostLogs.stream()
.map(s -> ExecHostLogTailDTO.builder()
.id(s.getId())
.hostId(s.getHostId())
.path(s.getLogPath())
.charset(Optional.ofNullable(configMap.get(s.getHostId()))
.map(HostConfigVO::getConfig)
.map(c -> c.get(FieldConst.CHARSET))
.map(Objects1::toString)
.orElse(Const.UTF_8))
.build())
.collect(Collectors.toList()))
.build();
// 设置缓存
RedisStrings.setJson(cacheKey, ExecCacheKeyDefine.EXEC_TAIL, cache);
log.info("ExecLogService.getExecLogTailToken finish token: {}, execId: {}, hostExecIdList: {}", token, execId, hostExecIdList);
return token;
}
@Override
public ExecLogTailDTO getExecLogTailInfo(String token) {
String cacheKey = ExecCacheKeyDefine.EXEC_TAIL.format(token);
// 获取缓存
ExecLogTailDTO tail = RedisStrings.getJson(cacheKey, ExecCacheKeyDefine.EXEC_TAIL);
if (tail != null) {
// 删除缓存
RedisStrings.delete(cacheKey);
}
return tail;
}
@Override
public void downloadLogFile(Long id, String source, HttpServletResponse response) {
log.info("ExecLogService.downloadLogFile id: {}, source: {}", id, source);
try {
// 获取主机执行日志
ExecHostLogDO hostLog = execHostLogDAO.selectById(id);
Valid.notNull(hostLog, ErrorMessage.LOG_ABSENT);
String logPath = hostLog.getLogPath();
Valid.notNull(logPath, ErrorMessage.LOG_ABSENT);
ExecLogDO execLog = execLogDAO.selectByIdSource(hostLog.getLogId(), source);
Valid.notNull(execLog, ErrorMessage.LOG_ABSENT);
// 设置日志参数
OperatorLogs.add(OperatorLogs.LOG_ID, hostLog.getLogId());
OperatorLogs.add(OperatorLogs.HOST_ID, hostLog.getHostId());
OperatorLogs.add(OperatorLogs.HOST_NAME, hostLog.getHostName());
// 获取日志
InputStream in = logsFileClient.getContentInputStream(logPath);
// 返回
Servlets.transfer(response, in, Files1.getFileName(logPath));
} catch (Exception e) {
log.error("ExecLogService.downloadLogFile error id: {}", id, e);
String errorMessage = ErrorMessage.FILE_READ_ERROR;
if (e instanceof InvalidArgumentException) {
errorMessage = e.getMessage();
}
// 响应错误信息
try {
Servlets.transfer(response, Strings.bytes(errorMessage), Const.ERROR_LOG);
} catch (Exception ex) {
log.error("ExecLogService.downloadLogFile transfer-error id: {}", id, ex);
}
}
}
/**
* 构建查询 wrapper
*

View File

@@ -0,0 +1,169 @@
import type { DataGrid, Pagination } from '@/types/global';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import axios from 'axios';
import qs from 'query-string';
/**
* 执行记录查询请求
*/
export interface ExecCommandLogQueryRequest extends Pagination {
id?: number;
userId?: number;
description?: string;
command?: string;
status?: string;
startTimeRange?: string[];
}
/**
* 执行记录查询响应
*/
export interface ExecCommandLogQueryResponse extends TableData, ExecCommandLogQueryExtraResponse {
id: number;
userId: number;
username: string;
description: string;
command: string;
parameterSchema: string;
timeout: number;
status: string;
startTime: number;
finishTime: number;
hostIdList: Array<number>;
hosts: Array<ExecCommandHostLogQueryResponse>;
}
/**
* 执行记录查询响应 拓展
*/
export interface ExecCommandLogQueryExtraResponse {
hosts: Array<ExecCommandHostLogQueryResponse>;
}
/**
* 主机执行记录查询响应
*/
export interface ExecCommandHostLogQueryResponse extends TableData {
id: number;
logId: number;
hostId: number;
hostName: string;
hostAddress: string;
status: string;
command: string;
parameter: string;
exitStatus: number;
errorMessage: string;
startTime: number;
finishTime: number;
}
/**
* 执行状态查询响应
*/
export interface ExecCommandLogStatusResponse {
logList: Array<ExecCommandLogQueryResponse>;
hostList: Array<ExecCommandHostLogQueryResponse>;
}
/**
* 中断命令请求
*/
export interface ExecCommandLogTailRequest {
execId?: number;
hostExecIdList?: Array<number>;
}
/**
* 分页查询执行记录
*/
export function getExecCommandLogPage(request: ExecCommandLogQueryRequest) {
return axios.post<DataGrid<ExecCommandLogQueryResponse>>('/asset/exec-command-log/query', request);
}
/**
* 查询执行记录
*/
export function getExecCommandLog(id: number) {
return axios.get<ExecCommandLogQueryResponse>('/asset/exec-command-log/get', { params: { id } });
}
/**
* 查询主机执行记录
*/
export function getExecCommandHostLogList(logId: number) {
return axios.get<Array<ExecCommandHostLogQueryResponse>>('/asset/exec-command-log/host-list', { params: { logId } });
}
/**
* 查询命令执行状态
*/
export function getExecCommandLogStatus(idList: Array<number>) {
return axios.get<ExecCommandLogStatusResponse>('/asset/exec-command-log/status', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* 查询历史执行记录
*/
export function getExecCommandLogHistory(limit: number) {
return axios.get<Array<ExecCommandLogQueryResponse>>('/asset/exec-command-log/history', { params: { page: 1, limit } });
}
/**
* 删除执行记录
*/
export function deleteExecCommandLog(id: number) {
return axios.delete('/asset/exec-command-log/delete', { params: { id } });
}
/**
* 批量删除执行记录
*/
export function batchDeleteExecCommandLog(idList: Array<number>) {
return axios.delete('/asset/exec-command-log/batch-delete', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* 删除主机执行记录
*/
export function deleteExecCommandHostLog(id: number) {
return axios.delete('/asset/exec-command-log/delete-host', { params: { id } });
}
/**
* 查询操作日志数量
*/
export function getExecCommandLogCount(request: ExecCommandLogQueryRequest) {
return axios.post<number>('/asset/exec-command-log/query-count', request);
}
/**
* 清空操作日志
*/
export function clearExecCommandLog(request: ExecCommandLogQueryRequest) {
return axios.post<number>('/asset/exec-command-log/clear', request);
}
/**
* 查看执行日志
*/
export function getExecCommandLogTailToken(request: ExecCommandLogTailRequest) {
return axios.post<string>('/asset/exec-command-log/tail', request);
}
/**
* 下载执行日志文件
*/
export function downloadExecCommandLogFile(id: number) {
return axios.get('/asset/exec-command-log/download', { unwrap: true, params: { id } });
}

View File

@@ -0,0 +1,50 @@
import type { ExecLogQueryResponse } from './exec-log';
import axios from 'axios';
/**
* 执行命令请求
*/
export interface ExecCommandRequest {
logId?: number;
description?: string;
timeout?: number;
command?: string;
parameterSchema?: string;
hostIdList?: Array<number>;
}
/**
* 中断命令请求
*/
export interface ExecCommandInterruptRequest {
logId?: number;
hostLogId?: number;
}
/**
* 批量执行命令
*/
export function batchExecCommand(request: ExecCommandRequest) {
return axios.post<ExecLogQueryResponse>('/asset/exec-command/exec', request);
}
/**
* 重新执行命令
*/
export function reExecCommand(request: ExecCommandRequest) {
return axios.post<ExecLogQueryResponse>('/asset/exec-command/re-exec', request);
}
/**
* 中断执行命令
*/
export function interruptExecCommand(request: ExecCommandInterruptRequest) {
return axios.put('/asset/exec-command/interrupt', request);
}
/**
* 中断执行主机命令
*/
export function interruptHostExecCommand(request: ExecCommandInterruptRequest) {
return axios.put('/asset/exec-command/interrupt-host', request);
}

View File

@@ -13,9 +13,9 @@ const EXEC: AppRouteRecordRaw[] = [
component: () => import('@/views/exec/exec-command/index.vue'),
},
{
name: 'execLog',
path: '/exec-log',
component: () => import('@/views/exec/exec-log/index.vue'),
name: 'execCommandLog',
path: '/exec-command-log',
component: () => import('@/views/exec/exec-command-log/index.vue'),
},
{
name: 'execTemplate',