forked from KNEMC/KLALB
feat(srv6)!: split node information queries
Replace the combined node-info protocol with independent profile, external-endpoint, and extra-route requests. Aggregate consumers use NodeInfoQueryCoordinator and validate response source addresses. BREAKING CHANGE: nodeinfotinyreq/resp and nodeinfofullreq/resp are removed. Peers must use the profile, external-endpoint, and extra-route request pairs.
This commit is contained in:
@@ -53,10 +53,11 @@ import org.kne.cloud.network.ntp.NTPContext;
|
||||
import org.kne.cloud.network.ntp.NTPv4Packet;
|
||||
import org.kne.cloud.network.ntp.NTPv4Protocol;
|
||||
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
|
||||
import org.kne.cloud.network.srv6.SRv6Router;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.SRv6Router;
|
||||
import org.kne.cloud.network.srv6.SRv6RouterListener;
|
||||
import org.kne.cloud.network.tcp.UDPPacket;
|
||||
import org.kne.cloud.network.tcp.UDPProtocolRegister;
|
||||
@@ -389,9 +390,11 @@ public class KLALBController {
|
||||
try {
|
||||
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
|
||||
KLALBRoutingProtocol.DEFAULT_PORT);
|
||||
apiClient.requestNodeInfoFull(iaddr, (v) -> {
|
||||
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
|
||||
for (MultiProtocolSocketAddress msa : v.getOpenLines()) {
|
||||
apiClient.requestExternalEndpoints(iaddr, (v) -> {
|
||||
if (v == null || v.getStatus() != NodeInfoQueryStatus.OK) return;
|
||||
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
|
||||
if (v.getExternalEndpoints() == null) return;
|
||||
for (MultiProtocolSocketAddress msa : v.getExternalEndpoints()) {
|
||||
// System.out.print(msa);
|
||||
addRemoteLines(msa);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
package org.kne.cloud.network.klalb;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.srv6.ExternalEndpointsResult;
|
||||
import org.kne.cloud.network.srv6.ExtraRoutesResult;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
|
||||
/** Coordinates the three independent sections of a remote node-info query. */
|
||||
public final class NodeInfoQueryCoordinator {
|
||||
private static final long DEADLINE_MILLIS = 3000L;
|
||||
private static final ExecutorService REQUEST_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
|
||||
private static final ScheduledExecutorService DEADLINE_EXECUTOR = Executors
|
||||
.newSingleThreadScheduledExecutor(r -> {
|
||||
Thread thread = new Thread(r, "KLALB-node-info-deadline");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
private final KLALBRoutingProtocolAPIClient client;
|
||||
|
||||
public NodeInfoQueryCoordinator(KLALBRoutingProtocolAPIClient client) {
|
||||
this.client = Objects.requireNonNull(client, "client");
|
||||
}
|
||||
|
||||
/** Starts all section requests and completes at the first all-sections response or the shared deadline. */
|
||||
public CompletableFuture<Result> query(SocketAddress address) {
|
||||
Objects.requireNonNull(address, "address");
|
||||
Pending pending = new Pending();
|
||||
pending.result.whenComplete((ignored, error) -> {
|
||||
if (pending.result.isCancelled()) pending.cancel();
|
||||
});
|
||||
pending.deadline = DEADLINE_EXECUTOR.schedule(pending::timeout, DEADLINE_MILLIS, TimeUnit.MILLISECONDS);
|
||||
|
||||
issue(pending, () -> client.requestNodeProfile(address, pending::profile), pending::profileFailed);
|
||||
issue(pending, () -> client.requestExternalEndpoints(address, pending::externalEndpoints),
|
||||
pending::externalEndpointsFailed);
|
||||
issue(pending, () -> client.requestExtraRoutes(address, pending::extraRoutes), pending::extraRoutesFailed);
|
||||
return pending.result;
|
||||
}
|
||||
|
||||
private void issue(Pending pending, ThrowingRequest request, Runnable failure) {
|
||||
REQUEST_EXECUTOR.execute(() -> pending.issue(request, failure));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingRequest {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
public static final class Section<T> {
|
||||
private final boolean received;
|
||||
private final Optional<T> value;
|
||||
private final Optional<NodeInfoQueryStatus> status;
|
||||
|
||||
private Section(boolean received, T value, NodeInfoQueryStatus status) {
|
||||
this.received = received;
|
||||
this.value = Optional.ofNullable(value);
|
||||
this.status = Optional.ofNullable(status);
|
||||
}
|
||||
|
||||
public boolean isReceived() {
|
||||
return received;
|
||||
}
|
||||
|
||||
public Optional<T> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Optional<NodeInfoQueryStatus> getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
private static <T> Section<T> missing() {
|
||||
return new Section<T>(false, null, null);
|
||||
}
|
||||
|
||||
private static <T> Section<T> received(T value, NodeInfoQueryStatus status) {
|
||||
return new Section<T>(true, value, status);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
private final Section<NodeProfile> profile;
|
||||
private final Section<List<MultiProtocolSocketAddress>> externalEndpoints;
|
||||
private final Section<List<String>> extraRoutes;
|
||||
|
||||
private Result(Section<NodeProfile> profile,
|
||||
Section<List<MultiProtocolSocketAddress>> externalEndpoints,
|
||||
Section<List<String>> extraRoutes) {
|
||||
this.profile = profile;
|
||||
this.externalEndpoints = externalEndpoints;
|
||||
this.extraRoutes = extraRoutes;
|
||||
}
|
||||
|
||||
public Section<NodeProfile> getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public Section<List<MultiProtocolSocketAddress>> getExternalEndpoints() {
|
||||
return externalEndpoints;
|
||||
}
|
||||
|
||||
public Section<List<String>> getExtraRoutes() {
|
||||
return extraRoutes;
|
||||
}
|
||||
}
|
||||
|
||||
private final class Pending {
|
||||
private final CompletableFuture<Result> result = new CompletableFuture<>();
|
||||
private final AtomicBoolean finished = new AtomicBoolean();
|
||||
private int remaining = 3;
|
||||
private Section<NodeProfile> profile = Section.missing();
|
||||
private Section<List<MultiProtocolSocketAddress>> externalEndpoints = Section.missing();
|
||||
private Section<List<String>> extraRoutes = Section.missing();
|
||||
private java.util.concurrent.ScheduledFuture<?> deadline;
|
||||
|
||||
private synchronized void profile(NodeProfile value) {
|
||||
if (profile.isReceived()) return;
|
||||
profile = Section.received(value, NodeInfoQueryStatus.OK);
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void profileFailed() {
|
||||
if (profile.isReceived()) return;
|
||||
profile = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void externalEndpoints(ExternalEndpointsResult value) {
|
||||
if (externalEndpoints.isReceived()) return;
|
||||
if (value == null) {
|
||||
externalEndpoints = Section.missing();
|
||||
} else {
|
||||
List<MultiProtocolSocketAddress> endpoints = value.getExternalEndpoints();
|
||||
externalEndpoints = Section.received(endpoints == null
|
||||
? Collections.<MultiProtocolSocketAddress>emptyList()
|
||||
: Collections.unmodifiableList(new ArrayList<MultiProtocolSocketAddress>(endpoints)),
|
||||
value.getStatus());
|
||||
}
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void externalEndpointsFailed() {
|
||||
if (externalEndpoints.isReceived()) return;
|
||||
externalEndpoints = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void extraRoutes(ExtraRoutesResult value) {
|
||||
if (extraRoutes.isReceived()) return;
|
||||
if (value == null) {
|
||||
extraRoutes = Section.missing();
|
||||
} else {
|
||||
List<String> routes = value.getExtraRoutes();
|
||||
extraRoutes = Section.received(routes == null
|
||||
? Collections.<String>emptyList()
|
||||
: Collections.unmodifiableList(new ArrayList<String>(routes)), value.getStatus());
|
||||
}
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void extraRoutesFailed() {
|
||||
if (extraRoutes.isReceived()) return;
|
||||
extraRoutes = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void issue(ThrowingRequest request, Runnable failure) {
|
||||
if (finished.get() || result.isDone() || result.isCancelled()) return;
|
||||
try {
|
||||
request.run();
|
||||
} catch (Exception e) {
|
||||
failure.run();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void cancel() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void completeSection() {
|
||||
remaining--;
|
||||
if (remaining == 0) finish();
|
||||
}
|
||||
|
||||
private synchronized void timeout() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
if (!finished.compareAndSet(false, true)) return;
|
||||
if (deadline != null) deadline.cancel(false);
|
||||
result.complete(new Result(profile, externalEndpoints, extraRoutes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,13 @@ import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.srv6.KLALBNodeInformation;
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
||||
|
||||
public class NetworkGraphPanel extends GraphPanel {
|
||||
@@ -205,7 +205,7 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestNodeInfoTiny(final IPv6Address address) {
|
||||
private void requestNodeProfile(final IPv6Address address) {
|
||||
final long requestTime = System.currentTimeMillis();
|
||||
final long requestGeneration;
|
||||
synchronized (nodeInfoLock) {
|
||||
@@ -225,9 +225,9 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
nodeInfoRequestGenerations.put(address, requestGeneration);
|
||||
}
|
||||
try {
|
||||
nodeInfoClient.requestNodeInfoTiny(
|
||||
nodeInfoClient.requestNodeProfile(
|
||||
new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||
(info) -> SwingUtilities.invokeLater(() -> handleNodeInfo(address, requestGeneration, info)));
|
||||
(profile) -> SwingUtilities.invokeLater(() -> handleNodeProfile(address, requestGeneration, profile)));
|
||||
} catch (IOException e) {
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoRequestGenerations.get(address) != null
|
||||
@@ -239,12 +239,12 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNodeInfo(IPv6Address address, long requestGeneration, KLALBNodeInformation info) {
|
||||
private void handleNodeProfile(IPv6Address address, long requestGeneration, NodeProfile profile) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
boolean currentRequest;
|
||||
boolean nodeExists = getNodes().containsKey(address);
|
||||
String deviceName = info == null ? "" : info.getDeviceName();
|
||||
String deviceName = profile == null ? "" : profile.getDeviceName();
|
||||
if (deviceName == null || deviceName.isEmpty())
|
||||
deviceName = "";
|
||||
synchronized (nodeInfoLock) {
|
||||
@@ -295,7 +295,7 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
node.updateLabel();
|
||||
}
|
||||
repaint();
|
||||
requestNodeInfoTiny(address);
|
||||
requestNodeProfile(address);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@@ -349,7 +349,7 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
getNodes().put(inet6Address,new InetGraphNode(inet6Address,Color.BLACK,v2pos.x,v2pos.y,inet6Address.equals(controller.getIpv6Router().getLocator().getAddress())));
|
||||
}
|
||||
if(!inet6Address.equals(localAddress))
|
||||
requestNodeInfoTiny(inet6Address);
|
||||
requestNodeProfile(inet6Address);
|
||||
}
|
||||
Set<IPv6Address> kns=getNodes().keySet();
|
||||
for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
@@ -22,10 +22,13 @@ import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import org.kne.cloud.klalb.uitool.XDefaultListModel;
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.klalb.NodeInfoQueryCoordinator;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
@@ -37,8 +40,9 @@ import java.awt.event.ActionEvent;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class NodeInformationPanel extends JPanel {
|
||||
private IPv6Address address;
|
||||
private KLALBRoutingProtocolAPIClient client;
|
||||
private IPv6Address address;
|
||||
private KLALBRoutingProtocolAPIClient client;
|
||||
private NodeInfoQueryCoordinator queryCoordinator;
|
||||
private KLALBController controller;
|
||||
private Image image;
|
||||
|
||||
@@ -174,6 +178,7 @@ public class NodeInformationPanel extends JPanel {
|
||||
|
||||
KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
|
||||
client=new KLALBRoutingProtocolAPIClient(routingProtocol);
|
||||
queryCoordinator=new NodeInfoQueryCoordinator(client);
|
||||
nodeInfoUpdateListener=(updatedAddress, flags) -> {
|
||||
if(active && address.equals(updatedAddress)
|
||||
&& (flags & org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
||||
@@ -186,27 +191,77 @@ public class NodeInformationPanel extends JPanel {
|
||||
|
||||
private void requestFullInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel) {
|
||||
final long generation=fullInfoGeneration.incrementAndGet();
|
||||
if (controller.getIpv6Router().getLocator().getAddress().equals(address)) {
|
||||
KLALBController.PublishedNodeInfo published=controller.getPublishedNodeInfo();
|
||||
applyNodeInfo(overviewArea, extraRoutesEmptyLabel, published.getDeviceName(),
|
||||
published.getDeviceDescription(), published.getExternalEndpoints(), published.getExtraRoutes());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
client.requestNodeInfoFull(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (info)->{
|
||||
queryCoordinator.query(new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT)).whenComplete((info, error)->{
|
||||
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||
javax.swing.SwingUtilities.invokeLater(() -> {
|
||||
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||
listModel.clear();
|
||||
if(info.getOpenLines()!=null) for (MultiProtocolSocketAddress item : info.getOpenLines()) listModel.addElement(item);
|
||||
extraRoutesModel.clear();
|
||||
List<String> routes=info.getExtraRoutes();
|
||||
if(routes!=null) for(String route : routes) extraRoutesModel.addElement(route);
|
||||
extraRoutesEmptyLabel.setText(UIEnv.getRsb().getString("noextraroutes"));
|
||||
extraRoutesEmptyLabel.setVisible(extraRoutesModel.isEmpty());
|
||||
String dd=info.getDeviceDescription();
|
||||
overviewArea.setText(buildOverviewText(info.getDeviceName(), dd==null||dd.isEmpty()?null:dd));
|
||||
if (error != null || info == null) {
|
||||
return;
|
||||
}
|
||||
applyRemoteNodeInfo(overviewArea, extraRoutesEmptyLabel, info);
|
||||
});
|
||||
});
|
||||
} catch (IOException e) {
|
||||
} catch (RuntimeException e) {
|
||||
if(active) e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyNodeInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel,
|
||||
String deviceName, String deviceDescription, List<MultiProtocolSocketAddress> endpoints,
|
||||
List<String> routes) {
|
||||
applyProfile(overviewArea, deviceName, deviceDescription);
|
||||
applyEndpoints(endpoints == null ? Collections.<MultiProtocolSocketAddress>emptyList() : endpoints);
|
||||
applyRoutes(extraRoutesEmptyLabel, routes == null ? Collections.<String>emptyList() : routes);
|
||||
}
|
||||
|
||||
private void applyRemoteNodeInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel,
|
||||
NodeInfoQueryCoordinator.Result info) {
|
||||
if (info.getProfile().isReceived()) {
|
||||
NodeProfile profile=info.getProfile().getValue().orElse(null);
|
||||
applyProfile(overviewArea, profile == null ? null : profile.getDeviceName(),
|
||||
profile == null ? null : profile.getDeviceDescription());
|
||||
}
|
||||
|
||||
if (info.getExternalEndpoints().isReceived()) {
|
||||
List<MultiProtocolSocketAddress> endpoints=info.getExternalEndpoints().getStatus()
|
||||
.filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
? info.getExternalEndpoints().getValue().orElse(Collections.<MultiProtocolSocketAddress>emptyList())
|
||||
: Collections.<MultiProtocolSocketAddress>emptyList();
|
||||
applyEndpoints(endpoints);
|
||||
}
|
||||
|
||||
if (info.getExtraRoutes().isReceived()) {
|
||||
List<String> routes=info.getExtraRoutes().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
? info.getExtraRoutes().getValue().orElse(Collections.<String>emptyList())
|
||||
: Collections.<String>emptyList();
|
||||
applyRoutes(extraRoutesEmptyLabel, routes);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyProfile(JTextArea overviewArea, String deviceName, String deviceDescription) {
|
||||
overviewArea.setText(buildOverviewText(deviceName,
|
||||
deviceDescription == null || deviceDescription.isEmpty() ? null : deviceDescription));
|
||||
}
|
||||
|
||||
private void applyEndpoints(List<MultiProtocolSocketAddress> endpoints) {
|
||||
listModel.clear();
|
||||
if (endpoints != null) for (MultiProtocolSocketAddress item : endpoints) listModel.addElement(item);
|
||||
}
|
||||
|
||||
private void applyRoutes(javax.swing.JLabel extraRoutesEmptyLabel, List<String> routes) {
|
||||
extraRoutesModel.clear();
|
||||
if (routes != null) for (String route : routes) extraRoutesModel.addElement(route);
|
||||
extraRoutesEmptyLabel.setText(UIEnv.getRsb().getString("noextraroutes"));
|
||||
extraRoutesEmptyLabel.setVisible(extraRoutesModel.isEmpty());
|
||||
}
|
||||
|
||||
public synchronized void close() {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
@@ -20,10 +20,11 @@ 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.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
import org.kne.cloud.network.srv6.NeighborInfo;
|
||||
import org.kne.cloud.network.srv6.RouterInfo;
|
||||
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
||||
@@ -54,7 +55,7 @@ public class KLALBWebServer {
|
||||
this.gson = proxySystem.getGson();
|
||||
this.nodeInfoUpdateListener = (address, flags) -> {
|
||||
if ((flags & RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
||||
nodeInfoFullRevisions.merge(address, 1L, Long::sum);
|
||||
nodeInfoRevisions.merge(address, 1L, Long::sum);
|
||||
}
|
||||
if ((flags & RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED) != 0) {
|
||||
long lifecycleGeneration;
|
||||
@@ -73,7 +74,6 @@ public class KLALBWebServer {
|
||||
|
||||
public synchronized void start() throws IOException {
|
||||
if (running.get()) return;
|
||||
nodeInfoFullRevisionEpoch = UUID.randomUUID().toString();
|
||||
if (sseExecutor == null || sseExecutor.isShutdown()) {
|
||||
sseExecutor = createSseExecutor();
|
||||
}
|
||||
@@ -503,14 +503,13 @@ public class KLALBWebServer {
|
||||
private final Set<IPv6Address> tinyNameRequestsInFlight = ConcurrentHashMap.newKeySet();
|
||||
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestTimes = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestGenerations = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<IPv6Address, Long> nodeInfoFullRevisions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<IPv6Address, Long> nodeInfoRevisions = new ConcurrentHashMap<>();
|
||||
private final Object tinyNameStateLock = new Object();
|
||||
private static final long TINY_NAME_CACHE_TTL_MS = 60000L;
|
||||
private long nextTinyNameRequestGeneration;
|
||||
private KLALBRoutingProtocol nodeInfoRoutingProtocol;
|
||||
private final BiConsumer<IPv6Address, Integer> nodeInfoUpdateListener;
|
||||
private long nodeInfoLifecycleGeneration;
|
||||
private volatile String nodeInfoFullRevisionEpoch = UUID.randomUUID().toString();
|
||||
|
||||
private static ScheduledExecutorService createSseExecutor() {
|
||||
return Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
@@ -605,14 +604,14 @@ public class KLALBWebServer {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
client.requestNodeInfoTiny(
|
||||
client.requestNodeProfile(
|
||||
new InetSocketAddress(addr.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||
info -> {
|
||||
profile -> {
|
||||
synchronized (tinyNameStateLock) {
|
||||
Long activeRequestGeneration = tinyNameRequestGenerations.get(addr);
|
||||
if (!Long.valueOf(requestGenerationToken).equals(activeRequestGeneration)) return;
|
||||
tinyDeviceNameCache.put(addr,
|
||||
info != null && info.getDeviceName() != null ? info.getDeviceName() : "");
|
||||
profile != null && profile.getDeviceName() != null ? profile.getDeviceName() : "");
|
||||
tinyDeviceNameCacheTimes.put(addr, System.currentTimeMillis());
|
||||
tinyNameRequestsInFlight.remove(addr);
|
||||
tinyNameRequestTimes.remove(addr);
|
||||
@@ -689,6 +688,7 @@ public class KLALBWebServer {
|
||||
// 本机:描述直接取本地控制器配置
|
||||
KLALBController.PublishedNodeInfo published = kc.getPublishedNodeInfo();
|
||||
resp.addProperty("isSelf", true);
|
||||
resp.addProperty("reachable", true);
|
||||
resp.addProperty("deviceName", published.getDeviceName() != null ? published.getDeviceName() : "");
|
||||
resp.addProperty("deviceDescription",
|
||||
published.getDeviceDescription() != null ? published.getDeviceDescription() : "");
|
||||
@@ -710,41 +710,36 @@ public class KLALBWebServer {
|
||||
|
||||
// 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待)
|
||||
resp.addProperty("isSelf", false);
|
||||
CompletableFuture<KLALBNodeInformation> future = new CompletableFuture<>();
|
||||
try {
|
||||
KLALBRoutingProtocolAPIClient client = getNodeInfoClient(kc);
|
||||
if (client == null) {
|
||||
sendError(exchange, 503, "Node info service stopped");
|
||||
return;
|
||||
}
|
||||
client.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()
|
||||
: "";
|
||||
NodeInfoQueryCoordinator.Result info = new NodeInfoQueryCoordinator(client)
|
||||
.query(new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT)).get();
|
||||
NodeInfoQueryCoordinator.Section<NodeProfile> profile = info.getProfile();
|
||||
NodeProfile nodeProfile = profile.getValue().orElse(null);
|
||||
String dname = nodeProfile != null && nodeProfile.getDeviceName() != null
|
||||
&& !nodeProfile.getDeviceName().isEmpty() ? nodeProfile.getDeviceName() : "";
|
||||
resp.addProperty("deviceName", dname);
|
||||
resp.addProperty("deviceDescription",
|
||||
info != null && info.getDeviceDescription() != null ? info.getDeviceDescription() : "");
|
||||
resp.addProperty("reachable", info != null);
|
||||
nodeProfile != null && nodeProfile.getDeviceDescription() != null
|
||||
? nodeProfile.getDeviceDescription() : "");
|
||||
resp.addProperty("reachable", profile.isReceived()
|
||||
|| info.getExternalEndpoints().isReceived() || info.getExtraRoutes().isReceived());
|
||||
JsonArray lines = new JsonArray();
|
||||
if (info != null && info.getOpenLines() != null) {
|
||||
for (MultiProtocolSocketAddress mpsa : info.getOpenLines()) {
|
||||
if (info.getExternalEndpoints().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
&& info.getExternalEndpoints().getValue().isPresent()) {
|
||||
for (MultiProtocolSocketAddress mpsa : info.getExternalEndpoints().getValue().get()) {
|
||||
lines.add(new JsonPrimitive(mpsa.toString()));
|
||||
}
|
||||
}
|
||||
resp.add("openLines", lines);
|
||||
JsonArray extraRoutes = new JsonArray();
|
||||
if (info != null && info.getExtraRoutes() != null) {
|
||||
for (String route : info.getExtraRoutes()) {
|
||||
if (info.getExtraRoutes().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
&& info.getExtraRoutes().getValue().isPresent()) {
|
||||
for (String route : info.getExtraRoutes().getValue().get()) {
|
||||
extraRoutes.add(new JsonPrimitive(route != null ? route : ""));
|
||||
}
|
||||
}
|
||||
@@ -799,7 +794,7 @@ public class KLALBWebServer {
|
||||
tinyNameRequestTimes.keySet().removeIf(address -> !activeAddresses.contains(address));
|
||||
tinyNameRequestGenerations.keySet().removeIf(address -> !activeAddresses.contains(address));
|
||||
}
|
||||
nodeInfoFullRevisions.keySet().removeIf(address -> !activeAddresses.contains(address));
|
||||
nodeInfoRevisions.keySet().removeIf(address -> !activeAddresses.contains(address));
|
||||
|
||||
if (addrs != null) {
|
||||
for (IPv6Address addr : addrs.keySet()) {
|
||||
@@ -811,10 +806,9 @@ public class KLALBWebServer {
|
||||
String dname = addr.equals(selfAddr) ? kc.getIpv6Router().getDeviceName()
|
||||
: getTinyDeviceName(kc, addr);
|
||||
nodeObj.addProperty("deviceName", dname != null ? dname : "");
|
||||
long fullRevision = addr.equals(selfAddr) ? published.getFullRevision()
|
||||
: nodeInfoFullRevisions.getOrDefault(addr, 0L);
|
||||
nodeObj.addProperty("nodeInfoFullRevision", fullRevision);
|
||||
nodeObj.addProperty("nodeInfoFullRevisionEpoch", nodeInfoFullRevisionEpoch);
|
||||
long nodeInfoRevision = addr.equals(selfAddr) ? published.getFullRevision()
|
||||
: nodeInfoRevisions.getOrDefault(addr, 0L);
|
||||
nodeObj.addProperty("nodeInfoRevision", nodeInfoRevision);
|
||||
nodesArray.add(nodeObj);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user