🔨 批量执行日志.

This commit is contained in:
lijiahangmax
2024-03-19 01:24:15 +08:00
parent 2aaf3ee907
commit f8ea51eb8c
23 changed files with 437 additions and 90 deletions

View File

@@ -41,6 +41,8 @@ public interface FieldConst {
String TARGET = "target";
String CHARSET = "charset";
String TOKEN = "token";
String PATH = "path";

View File

@@ -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;

View File

@@ -28,7 +28,7 @@ Content-Type: application/json
Authorization: {{token}}
{
"execId": 1
"execId": 56
}

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -31,4 +31,7 @@ public class ExecHostLogTailDTO implements Serializable {
@Schema(description = "文件路径")
private String path;
@Schema(description = "输出编码")
private String charset;
}

View File

@@ -28,6 +28,6 @@ public class ExecLogTailRequest {
private Long execId;
@Schema(description = "执行主机id")
private List<Long> execHostIdList;
private List<Long> hostExecIdList;
}

View File

@@ -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;

View File

@@ -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());
}
/**

View File

@@ -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 关闭日志
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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());
// 关闭会话
}
}

View File

@@ -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);
}
});
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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);
/**
* 更新配置
*

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -16,6 +16,7 @@ export interface HostConfigRequest {
*/
export interface HostConfigQueryResponse {
id: number;
hostId: number;
type: string;
version: number;
status: number;

View File

@@ -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>

View File

@@ -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;
};
// 返回