feat(web): add /api/node-info endpoint for on-demand node detail queries
- Expose GET /api/node-info?address=<ipv6> endpoint in KLALBWebServer - Return local deviceName and deviceDescription immediately for self node - Perform asynchronous full node-info query (NODE_INFO_FULL_REQ) via KLALBRoutingProtocolAPIClient for remote nodes with 3s timeout fallback
This commit is contained in:
+1
-1
Submodule dashboard updated: f38d640ac4...ad94d2c274
@@ -19,8 +19,10 @@ import org.kne.cloud.network.ipv6.IPv6NetworkLink;
|
||||
import org.kne.cloud.network.ipv6.RouteItem;
|
||||
import org.kne.cloud.network.klalb.*;
|
||||
import org.kne.cloud.network.monitor.LinkStatus;
|
||||
import org.kne.cloud.network.srv6.KLALBNodeInformation;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.NeighborInfo;
|
||||
import org.kne.cloud.network.srv6.RouterInfo;
|
||||
import org.kne.cloud.network.srv6.SRv6Router;
|
||||
@@ -68,6 +70,7 @@ public class KLALBWebServer {
|
||||
server.createContext("/api/links/reconnect", this::handleReconnectAll);
|
||||
server.createContext("/api/routes", this::handleRoutes);
|
||||
server.createContext("/api/nodes", this::handleNodes);
|
||||
server.createContext("/api/node-info", this::handleNodeInfo);
|
||||
server.createContext("/api/interfaces", this::handleInterfaces);
|
||||
server.createContext("/api/config", this::handleConfig);
|
||||
|
||||
@@ -453,6 +456,119 @@ public class KLALBWebServer {
|
||||
sendJsonResponse(exchange, 200, routesArray);
|
||||
}
|
||||
|
||||
private KLALBRoutingProtocolAPIClient nodeInfoClient;
|
||||
|
||||
private synchronized KLALBRoutingProtocolAPIClient getNodeInfoClient(KLALBController kc) {
|
||||
if (nodeInfoClient == null) {
|
||||
nodeInfoClient = new KLALBRoutingProtocolAPIClient(kc.getIpv6Router().getKlalbRouteProtol());
|
||||
}
|
||||
return nodeInfoClient;
|
||||
}
|
||||
|
||||
private void handleNodeInfo(HttpExchange exchange) throws IOException {
|
||||
if (handleCorsPreflight(exchange)) return;
|
||||
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||
sendError(exchange, 405, "Method not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> params = parseQuery(exchange.getRequestURI().getRawQuery());
|
||||
String addressStr = params.get("address");
|
||||
if (addressStr == null || addressStr.trim().isEmpty()) {
|
||||
sendError(exchange, 400, "address parameter is required");
|
||||
return;
|
||||
}
|
||||
|
||||
KLALBController kc = proxySystem.getKlalbController();
|
||||
if (kc == null || kc.getIpv6Router() == null || kc.getIpv6Router().getKlalbRouteProtol() == null) {
|
||||
sendError(exchange, 503, "Controller not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
IPv6Address target;
|
||||
try {
|
||||
target = new IPv6Address((Inet6Address) InetAddress.getByName(addressStr.trim()));
|
||||
} catch (Exception e) {
|
||||
sendError(exchange, 400, "Invalid IPv6 address: " + addressStr);
|
||||
return;
|
||||
}
|
||||
|
||||
SRv6Router router = kc.getIpv6Router();
|
||||
boolean isSelf = router.getLocator().getAddress().equals(target);
|
||||
|
||||
JsonObject resp = new JsonObject();
|
||||
resp.addProperty("address", target.toString());
|
||||
|
||||
if (isSelf) {
|
||||
// 本机:描述直接取本地控制器配置
|
||||
KLALBControllerConfigItem cfg = proxySystem.getControllerConfig();
|
||||
resp.addProperty("isSelf", true);
|
||||
String dname = router.getDeviceName() != null ? router.getDeviceName()
|
||||
: (cfg != null && cfg.getDeviceName() != null ? cfg.getDeviceName() : "");
|
||||
resp.addProperty("deviceName", dname);
|
||||
resp.addProperty("deviceDescription",
|
||||
cfg != null && cfg.getDeviceDescription() != null ? cfg.getDeviceDescription() : "");
|
||||
JsonArray lines = new JsonArray();
|
||||
for (MultiProtocolSocketAddress mpsa : kc.getExternalEndpoints()) {
|
||||
lines.add(new JsonPrimitive(mpsa.toString()));
|
||||
}
|
||||
resp.add("openLines", lines);
|
||||
sendJsonResponse(exchange, 200, resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待)
|
||||
resp.addProperty("isSelf", false);
|
||||
CompletableFuture<KLALBNodeInformation> future = new CompletableFuture<>();
|
||||
try {
|
||||
getNodeInfoClient(kc).requestNodeInfoFull(
|
||||
new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||
info -> future.complete(info));
|
||||
|
||||
KLALBNodeInformation info;
|
||||
try {
|
||||
info = future.get(3, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException te) {
|
||||
info = null;
|
||||
}
|
||||
|
||||
String dname = info != null && info.getDeviceName() != null && !info.getDeviceName().isEmpty()
|
||||
? info.getDeviceName()
|
||||
: router.getKlalbRouteProtol().getDeviceName(target);
|
||||
resp.addProperty("deviceName", dname != null ? dname : "");
|
||||
resp.addProperty("deviceDescription",
|
||||
info != null && info.getDeviceDescription() != null ? info.getDeviceDescription() : "");
|
||||
resp.addProperty("reachable", info != null);
|
||||
JsonArray lines = new JsonArray();
|
||||
if (info != null && info.getOpenLines() != null) {
|
||||
for (MultiProtocolSocketAddress mpsa : info.getOpenLines()) {
|
||||
lines.add(new JsonPrimitive(mpsa.toString()));
|
||||
}
|
||||
}
|
||||
resp.add("openLines", lines);
|
||||
} catch (Exception e) {
|
||||
resp.addProperty("deviceName", "");
|
||||
resp.addProperty("deviceDescription", "");
|
||||
resp.addProperty("reachable", false);
|
||||
resp.add("openLines", new JsonArray());
|
||||
}
|
||||
sendJsonResponse(exchange, 200, resp);
|
||||
}
|
||||
|
||||
private Map<String, String> parseQuery(String rawQuery) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if (rawQuery == null || rawQuery.isEmpty()) return params;
|
||||
for (String pair : rawQuery.split("&")) {
|
||||
int idx = pair.indexOf('=');
|
||||
if (idx <= 0) continue;
|
||||
try {
|
||||
params.put(URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8),
|
||||
URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8));
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private void handleNodes(HttpExchange exchange) throws IOException {
|
||||
if (handleCorsPreflight(exchange)) return;
|
||||
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||
|
||||
Reference in New Issue
Block a user