🔨 批量执行日志.
This commit is contained in:
@@ -41,6 +41,8 @@ public interface FieldConst {
|
||||
|
||||
String TARGET = "target";
|
||||
|
||||
String CHARSET = "charset";
|
||||
|
||||
String TOKEN = "token";
|
||||
|
||||
String PATH = "path";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.orion.ops.module.asset.config;
|
||||
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.handler.ExecLogTailHandler;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.ExecLogTailHandler;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.TerminalMessageDispatcher;
|
||||
import com.orion.ops.module.asset.handler.host.transfer.TransferMessageDispatcher;
|
||||
import com.orion.ops.module.asset.interceptor.ExecLogTailInterceptor;
|
||||
|
||||
@@ -28,7 +28,7 @@ Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
|
||||
{
|
||||
"execId": 1
|
||||
"execId": 56
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,11 +52,13 @@ public interface HostConfigDAO extends IMapper<HostConfigDO> {
|
||||
* 通过 hostId 批量查询主机配置
|
||||
*
|
||||
* @param hostIdList hostIdList
|
||||
* @param type type
|
||||
* @return rows
|
||||
*/
|
||||
default List<HostConfigDO> getHostConfigByHostIdList(List<Long> hostIdList) {
|
||||
default List<HostConfigDO> getHostConfigByHostIdList(List<Long> hostIdList, String type) {
|
||||
// 条件
|
||||
LambdaQueryWrapper<HostConfigDO> wrapper = this.lambda()
|
||||
LambdaQueryWrapper<HostConfigDO> wrapper = this.wrapper()
|
||||
.eq(HostConfigDO::getType, type)
|
||||
.in(HostConfigDO::getHostId, hostIdList);
|
||||
// 查询
|
||||
return this.of(wrapper).list();
|
||||
|
||||
@@ -75,4 +75,16 @@ public interface AssetThreadPools {
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量执行日志查看线程池
|
||||
*/
|
||||
ThreadPoolExecutor EXEC_LOG = ExecutorBuilder.create()
|
||||
.namedThreadFactory("exec-log-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -31,4 +31,7 @@ public class ExecHostLogTailDTO implements Serializable {
|
||||
@Schema(description = "文件路径")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "输出编码")
|
||||
private String charset;
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,6 @@ public class ExecLogTailRequest {
|
||||
private Long execId;
|
||||
|
||||
@Schema(description = "执行主机id")
|
||||
private List<Long> execHostIdList;
|
||||
private List<Long> hostExecIdList;
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ public class HostConfigVO {
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "version")
|
||||
private Integer version;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.orion.ops.module.asset.dao.ExecHostLogDAO;
|
||||
import com.orion.ops.module.asset.entity.domain.ExecHostLogDO;
|
||||
import com.orion.ops.module.asset.enums.ExecHostStatusEnum;
|
||||
import com.orion.ops.module.asset.handler.host.exec.command.dto.ExecCommandHostDTO;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.manager.ExecLogManager;
|
||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||
import com.orion.spring.SpringHolder;
|
||||
import lombok.Getter;
|
||||
@@ -37,6 +38,8 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
|
||||
private final FileClient fileClient = SpringHolder.getBean("logsFileClient");
|
||||
|
||||
private final ExecLogManager execLogManager = SpringHolder.getBean(ExecLogManager.class);
|
||||
|
||||
private final HostTerminalService hostTerminalService = SpringHolder.getBean(HostTerminalService.class);
|
||||
|
||||
private final ExecHostLogDAO execHostLogDAO = SpringHolder.getBean(ExecHostLogDAO.class);
|
||||
@@ -177,7 +180,9 @@ public class ExecCommandHandler implements IExecCommandHandler {
|
||||
Streams.close(executor);
|
||||
Streams.close(sessionStore);
|
||||
Streams.close(logOutputStream);
|
||||
// TODO 关闭日志
|
||||
// TODO TEST 异步关闭日志
|
||||
execLogManager.asyncCloseTailFile(execHostCommand.getLogPath());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,7 +133,6 @@ public class ExecTaskHandler implements IExecTaskHandler {
|
||||
log.info("ExecTaskHandler-close id: {}", execCommand.getLogId());
|
||||
Streams.close(timeoutChecker);
|
||||
this.handlers.forEach(Streams::close);
|
||||
// TODO 关闭日志
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log;
|
||||
|
||||
import com.orion.ops.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.ops.framework.common.file.FileClient;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||
import com.orion.ops.module.asset.entity.dto.ExecHostLogTailDTO;
|
||||
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.manager.ExecLogManager;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.tracker.ExecLogTracker;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 执行日志查看处理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 18:38
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ExecLogTailHandler extends AbstractWebSocketHandler {
|
||||
|
||||
@Resource
|
||||
private FileClient logsFileClient;
|
||||
|
||||
@Resource
|
||||
private ExecLogManager execLogManager;
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
String id = session.getId();
|
||||
log.info("ExecLogTailHandler-afterConnectionEstablished id: {}", id);
|
||||
// 获取参数
|
||||
ExecLogTailDTO info = WebSockets.getAttr(session, ExtraFieldConst.INFO);
|
||||
// 打开会话
|
||||
for (ExecHostLogTailDTO host : info.getHosts()) {
|
||||
String trackerId = this.getTrackerId(id, info, host);
|
||||
String absolutePath = logsFileClient.getAbsolutePath(host.getPath());
|
||||
// 追踪器
|
||||
ExecLogTracker tracker = new ExecLogTracker(trackerId, absolutePath, session, host);
|
||||
// 执行
|
||||
AssetThreadPools.EXEC_LOG.execute(tracker);
|
||||
// 添加追踪器
|
||||
execLogManager.addTracker(tracker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) {
|
||||
log.error("ExecLogTailHandler-handleTransportError id: {}", session.getId(), exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
String id = session.getId();
|
||||
log.info("ExecLogTailHandler-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason());
|
||||
// 关闭会话
|
||||
ExecLogTailDTO info = WebSockets.getAttr(session, ExtraFieldConst.INFO);
|
||||
// 移除追踪器 TODO TEST
|
||||
for (ExecHostLogTailDTO host : info.getHosts()) {
|
||||
execLogManager.removeTracker(this.getTrackerId(id, info, host));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO ws://127.0.0.1:9200/orion/keep-alive/exec/log/ive0btemHxmEY0HyTm5
|
||||
// todo 首页元数据加载
|
||||
// todo 批量执行的 warn
|
||||
|
||||
/**
|
||||
* 获取追踪器 id
|
||||
*
|
||||
* @param id id
|
||||
* @param info info
|
||||
* @param host host
|
||||
* @return trackerId
|
||||
*/
|
||||
private String getTrackerId(String id, ExecLogTailDTO info, ExecHostLogTailDTO host) {
|
||||
return id + "_" + info.getId() + "_" + host.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log.constant;
|
||||
|
||||
/**
|
||||
* 日志常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 23:15
|
||||
*/
|
||||
public interface LogConst {
|
||||
|
||||
String SEPARATOR = "|";
|
||||
|
||||
int TRACKER_OFFSET_LINE = 200;
|
||||
|
||||
int TRACKER_DELAY_MS = 200;
|
||||
|
||||
int TRACKER_WAIT_TIMES = 10;
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log.handler;
|
||||
|
||||
import com.orion.ops.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.entity.dto.ExecLogTailDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
|
||||
|
||||
/**
|
||||
* 执行日志查看处理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 18:38
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ExecLogTailHandler extends AbstractWebSocketHandler {
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) {
|
||||
log.info("ExecLogTailHandler-afterConnectionEstablished id: {}", session.getId());
|
||||
// 获取参数
|
||||
ExecLogTailDTO info = WebSockets.getAttr(session, ExtraFieldConst.INFO);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) {
|
||||
log.error("ExecLogTailHandler-handleTransportError id: {}", session.getId(), exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
String id = session.getId();
|
||||
log.info("ExecLogTailHandler-afterConnectionClosed id: {}, code: {}, reason: {}", id, status.getCode(), status.getReason());
|
||||
// 关闭会话
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log.manager;
|
||||
|
||||
import com.orion.lang.utils.Threads;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.tracker.IExecLogTracker;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 执行日志管理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 23:36
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ExecLogManager {
|
||||
|
||||
private final ConcurrentHashMap<String, IExecLogTracker> execTrackers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 添加执行日志追踪器
|
||||
*
|
||||
* @param tracker tracker
|
||||
*/
|
||||
public void addTracker(IExecLogTracker tracker) {
|
||||
execTrackers.put(tracker.getTrackerId(), tracker);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志追踪器
|
||||
*
|
||||
* @param trackerId trackerId
|
||||
* @return tracker
|
||||
*/
|
||||
public IExecLogTracker getTracker(String trackerId) {
|
||||
return execTrackers.get(trackerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除日志追踪器
|
||||
*
|
||||
* @param trackerId trackerId
|
||||
*/
|
||||
public void removeTracker(String trackerId) {
|
||||
IExecLogTracker tracker = execTrackers.remove(trackerId);
|
||||
if (tracker != null) {
|
||||
tracker.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步关闭进行中的追踪器
|
||||
*
|
||||
* @param path path
|
||||
*/
|
||||
public void asyncCloseTailFile(String path) {
|
||||
Threads.start(() -> {
|
||||
try {
|
||||
// 获取当前路径的全部追踪器
|
||||
List<IExecLogTracker> trackers = execTrackers.values()
|
||||
.stream()
|
||||
.filter(s -> s.getPath().equals(path))
|
||||
.collect(Collectors.toList());
|
||||
Threads.sleep(Const.MS_S_1);
|
||||
trackers.forEach(IExecLogTracker::setLastModify);
|
||||
Threads.sleep(Const.MS_S_5);
|
||||
trackers.forEach(IExecLogTracker::close);
|
||||
} catch (Exception e) {
|
||||
log.error("ExecLogManager.asyncCloseTailFile error path: {}", path, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log.tracker;
|
||||
|
||||
import com.orion.ext.tail.Tracker;
|
||||
import com.orion.ext.tail.delay.DelayTrackerListener;
|
||||
import com.orion.ext.tail.mode.FileNotFoundMode;
|
||||
import com.orion.ext.tail.mode.FileOffsetMode;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.entity.dto.ExecHostLogTailDTO;
|
||||
import com.orion.ops.module.asset.handler.host.exec.log.constant.LogConst;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* log tracker 实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 23:36
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExecLogTracker implements IExecLogTracker {
|
||||
|
||||
private final WebSocketSession session;
|
||||
|
||||
private final ExecHostLogTailDTO config;
|
||||
|
||||
@Getter
|
||||
private final String trackerId;
|
||||
|
||||
@Getter
|
||||
private final String absolutePath;
|
||||
|
||||
private DelayTrackerListener tracker;
|
||||
|
||||
private volatile boolean close;
|
||||
|
||||
public ExecLogTracker(String trackerId,
|
||||
String absolutePath,
|
||||
WebSocketSession session,
|
||||
ExecHostLogTailDTO config) {
|
||||
this.trackerId = trackerId;
|
||||
this.absolutePath = absolutePath;
|
||||
this.session = session;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.tracker = new DelayTrackerListener(absolutePath, this);
|
||||
tracker.charset(config.getCharset());
|
||||
tracker.delayMillis(LogConst.TRACKER_DELAY_MS);
|
||||
tracker.offset(FileOffsetMode.LINE, LogConst.TRACKER_OFFSET_LINE);
|
||||
tracker.notFoundMode(FileNotFoundMode.WAIT_COUNT, LogConst.TRACKER_WAIT_TIMES);
|
||||
// 开始监听文件
|
||||
tracker.run();
|
||||
// 监听完成回调
|
||||
// TODO test
|
||||
this.close = true;
|
||||
} catch (Exception e) {
|
||||
log.error("exec log tracker error path: {}", absolutePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModify() {
|
||||
tracker.setFileLastModifyTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return config.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(byte[] bytes, int len, Tracker tracker) {
|
||||
WebSockets.sendText(session, config.getId() + LogConst.SEPARATOR + new String(bytes, 0, len));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// TODO test
|
||||
log.info("ExecLogTracker.close path: {}", absolutePath);
|
||||
if (close) {
|
||||
return;
|
||||
}
|
||||
this.close = true;
|
||||
if (tracker != null) {
|
||||
tracker.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.orion.ops.module.asset.handler.host.exec.log.tracker;
|
||||
|
||||
import com.orion.ext.tail.handler.DataHandler;
|
||||
import com.orion.lang.able.SafeCloseable;
|
||||
|
||||
/**
|
||||
* log tracker 定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/3/18 23:00
|
||||
*/
|
||||
public interface IExecLogTracker extends Runnable, DataHandler, SafeCloseable {
|
||||
|
||||
/**
|
||||
* 设置最后修改时间
|
||||
*/
|
||||
void setLastModify();
|
||||
|
||||
/**
|
||||
* 获取 id
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
String getTrackerId();
|
||||
|
||||
/**
|
||||
* 获取路径
|
||||
*
|
||||
* @return path
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* 获取绝对路径
|
||||
*
|
||||
* @return 绝对路径
|
||||
*/
|
||||
String getAbsolutePath();
|
||||
|
||||
}
|
||||
@@ -4,14 +4,11 @@ import com.orion.lang.utils.io.Streams;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.net.host.ssh.shell.ShellExecutor;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.enums.BooleanBit;
|
||||
import com.orion.ops.framework.websocket.core.utils.WebSockets;
|
||||
import com.orion.ops.module.asset.define.AssetThreadPools;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.SshOutputResponse;
|
||||
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCloseResponse;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
@@ -22,7 +22,7 @@ public interface HostConfigService {
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param type type
|
||||
* @return 配置
|
||||
* @return config
|
||||
*/
|
||||
HostConfigVO getHostConfig(Long hostId, String type);
|
||||
|
||||
@@ -31,7 +31,7 @@ public interface HostConfigService {
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @param type type
|
||||
* @return 配置
|
||||
* @return config
|
||||
*/
|
||||
<T extends GenericsDataModel> T getHostConfig(Long hostId, HostConfigTypeEnum type);
|
||||
|
||||
@@ -39,10 +39,19 @@ public interface HostConfigService {
|
||||
* 获取配置
|
||||
*
|
||||
* @param hostId hostId
|
||||
* @return 配置
|
||||
* @return config
|
||||
*/
|
||||
List<HostConfigVO> getHostConfigList(Long hostId);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param hostIdList hostIdList
|
||||
* @param type type
|
||||
* @return config
|
||||
*/
|
||||
List<HostConfigVO> getHostConfigList(List<Long> hostIdList, String type);
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -16,6 +17,7 @@ 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;
|
||||
@@ -35,6 +37,7 @@ 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.ExecCommandHostVO;
|
||||
import com.orion.ops.module.asset.entity.vo.ExecCommandVO;
|
||||
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;
|
||||
@@ -47,6 +50,7 @@ import com.orion.ops.module.asset.handler.host.exec.command.handler.IExecTaskHan
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -55,10 +59,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -92,6 +94,9 @@ public class ExecServiceImpl implements ExecService {
|
||||
@Resource
|
||||
private AssetAuthorizedDataService assetAuthorizedDataService;
|
||||
|
||||
@Resource
|
||||
private HostConfigService hostConfigService;
|
||||
|
||||
@Resource
|
||||
private ExecTaskManager execTaskManager;
|
||||
|
||||
@@ -167,6 +172,7 @@ public class ExecServiceImpl implements ExecService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ExecCommandVO reExecCommand(Long logId) {
|
||||
log.info("ExecService.reExecCommand start logId: {}", logId);
|
||||
// 获取执行记录
|
||||
@@ -288,24 +294,31 @@ public class ExecServiceImpl implements ExecService {
|
||||
@Override
|
||||
public String getExecLogTailToken(ExecLogTailRequest request) {
|
||||
Long execId = request.getExecId();
|
||||
List<Long> execHostIdList = request.getExecHostIdList();
|
||||
log.info("ExecService.getExecLogTailToken start execId: {}, execHostIdList: {}", execId, execHostIdList);
|
||||
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 (execHostIdList == null) {
|
||||
if (hostExecIdList == null) {
|
||||
hostLogs = execHostLogDAO.selectByLogId(execId);
|
||||
} else {
|
||||
hostLogs = execHostLogDAO.of()
|
||||
.createWrapper()
|
||||
.eq(ExecHostLogDO::getLogId, execId)
|
||||
.in(ExecHostLogDO::getId, execHostIdList)
|
||||
.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);
|
||||
@@ -318,12 +331,17 @@ public class ExecServiceImpl implements ExecService {
|
||||
.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: {}, execHostIdList: {}", token, execId, execHostIdList);
|
||||
log.info("ExecService.getExecLogTailToken finish token: {}, execId: {}, hostExecIdList: {}", token, execId, hostExecIdList);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends GenericsDataModel> T getHostConfig(Long hostId, HostConfigTypeEnum type) {
|
||||
// 查询配置
|
||||
HostConfigDO config = hostConfigDAO.getHostConfigByHostId(hostId, type.getType());
|
||||
@@ -76,20 +75,19 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
public List<HostConfigVO> getHostConfigList(Long hostId) {
|
||||
// 查询
|
||||
List<HostConfigDO> configs = hostConfigDAO.getHostConfigByHostId(hostId);
|
||||
return configs.stream()
|
||||
.map(this::convertHostConfig)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostConfigVO> getHostConfigList(List<Long> hostIdList, String type) {
|
||||
// 查询
|
||||
List<HostConfigDO> configs = hostConfigDAO.getHostConfigByHostIdList(hostIdList, type);
|
||||
// 返回
|
||||
return configs.stream()
|
||||
.map(s -> {
|
||||
// 获取配置
|
||||
HostConfigTypeEnum type = HostConfigTypeEnum.of(s.getType());
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
// 转为视图
|
||||
HostConfigVO vo = HostConfigConvert.MAPPER.to(s);
|
||||
Map<String, Object> config = type.getStrategyBean().toView(s.getConfig());
|
||||
vo.setConfig(config);
|
||||
return vo;
|
||||
})
|
||||
.map(this::convertHostConfig)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@@ -219,4 +217,23 @@ public class HostConfigServiceImpl implements HostConfigService {
|
||||
return insert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转化配置
|
||||
*
|
||||
* @param row row
|
||||
* @return config
|
||||
*/
|
||||
private HostConfigVO convertHostConfig(HostConfigDO row) {
|
||||
// 获取配置
|
||||
HostConfigTypeEnum type = HostConfigTypeEnum.of(row.getType());
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
// 转为视图
|
||||
HostConfigVO vo = HostConfigConvert.MAPPER.to(row);
|
||||
Map<String, Object> config = type.getStrategyBean().toView(row.getConfig());
|
||||
vo.setConfig(config);
|
||||
return vo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface HostConfigRequest {
|
||||
*/
|
||||
export interface HostConfigQueryResponse {
|
||||
id: number;
|
||||
hostId: number;
|
||||
type: string;
|
||||
version: number;
|
||||
status: number;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<div v-for="item in hosts"
|
||||
:key="item.id"
|
||||
class="exec-host-item"
|
||||
:class="[ current === item.hostId ? 'exec-host-item-selected' : '' ]"
|
||||
@click="emits('selected', item.hostId)">
|
||||
:class="[ current === item.id ? 'exec-host-item-selected' : '' ]"
|
||||
@click="emits('selected', item.id)">
|
||||
<!-- 主机名称 -->
|
||||
<div class="exec-host-item-name">
|
||||
<span class="host-name">{{ item.hostName }}</span>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="log-panel-container">
|
||||
<!-- 执行主机 -->
|
||||
<log-panel-host class="host-container"
|
||||
:current="currentHostId"
|
||||
:current="currentHostExecId"
|
||||
:hosts="command.hosts"
|
||||
@selected="selectedHost"
|
||||
@back="back" />
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
const emits = defineEmits(['back']);
|
||||
|
||||
const currentHostId = ref(1);
|
||||
const currentHostExecId = ref(1);
|
||||
const command = ref<ExecCommandResponse>({
|
||||
id: 50,
|
||||
hosts: [{
|
||||
@@ -65,7 +65,7 @@
|
||||
// 打开
|
||||
const open = (record: ExecCommandResponse) => {
|
||||
command.value = record;
|
||||
currentHostId.value = record.hosts[0].hostId;
|
||||
currentHostExecId.value = record.hosts[0].id;
|
||||
// 打开日志
|
||||
openLog();
|
||||
};
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
// 选中主机
|
||||
const selectedHost = (hostId: number) => {
|
||||
currentHostId.value = hostId;
|
||||
currentHostExecId.value = hostId;
|
||||
};
|
||||
|
||||
// 返回
|
||||
|
||||
Reference in New Issue
Block a user