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:
@@ -1,54 +1,39 @@
|
|||||||
# KLALB Repository Guide
|
# KLALB Repository Guide
|
||||||
|
|
||||||
KLALB is a Java SRv6/load-balancing system. The executable entrypoint is `org.kne.cloud.network.klalb.KLALBMain`; runtime configuration is `klalb-config.json` in the repository root.
|
KLALB is a Java SRv6/load-balancing system. Start at `org.kne.cloud.network.klalb.KLALBMain`; runtime configuration is root-relative `klalb-config.json`.
|
||||||
|
|
||||||
## Build And Run
|
## Build And Run
|
||||||
|
|
||||||
- This is a plain Eclipse/IntelliJ Java project: sources are `src/`, vendored dependencies are `lib/`, and output is `bin/`. There is no Maven or Gradle.
|
- This is an Eclipse Java project, not Maven or Gradle: `src/` contains sources, `lib/` vendored dependencies, and `bin/` compiled output. `.classpath` targets Java 25.
|
||||||
- `.classpath` targets `JavaSE-25`. Sources contain Chinese text, so manual compilation must use UTF-8:
|
- Source files contain Chinese text, so compile with UTF-8 from the repository root:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run from the repository root. Manual `javac` does not copy resources, so keep `src` on the runtime classpath:
|
- Run from the repository root. `src` must stay on the classpath because manual compilation does not copy resource bundles:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
& "C:\Program Files\Zulu\zulu-25\bin\java.exe" --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
|
& "C:\Program Files\Zulu\zulu-25\bin\java.exe" --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
|
||||||
```
|
```
|
||||||
|
|
||||||
- The native libraries and `klalb-config.json` are resolved from the current directory. Restart a running JVM after recompiling.
|
- Native libraries and `klalb-config.json` are resolved from the current directory; restart the JVM after recompiling. TUN creation requires elevation, so use `"enableTUN": false` for non-admin checks.
|
||||||
- TUN creation normally needs elevation. For non-admin UI/routing checks, set `"enableTUN": false`.
|
|
||||||
- Current full compilation emits 11 pre-existing varargs/deprecation warnings; exit code `0` is success.
|
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
- `dashboard/` is a Git submodule. Commit dashboard changes inside it, then update the parent repository's submodule pointer.
|
- `dashboard/` is a Git submodule. Commit dashboard changes in that repository, then update the parent submodule pointer.
|
||||||
- Run frontend commands from `dashboard/` with pnpm:
|
- Run frontend commands from `dashboard/`: `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm typecheck`, and `pnpm build`. The build is `tsc -b && vite build` and produces `dashboard/dist`, which the Java server hosts.
|
||||||
|
- Vite proxies `/api` to `http://127.0.0.1:4665`; use `pnpm dev` only with the Java API running there.
|
||||||
```powershell
|
|
||||||
pnpm install --frozen-lockfile
|
|
||||||
pnpm lint
|
|
||||||
pnpm typecheck
|
|
||||||
pnpm build
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
- `pnpm build` runs `tsc -b` then Vite and writes `dashboard/dist`, which the Java web server hosts. Vite development proxies `/api` to `http://127.0.0.1:4665`.
|
|
||||||
- Add shadcn components through `pnpm dlx shadcn@latest add <component>`; do not hand-create replacements for installed shadcn primitives.
|
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- There is no CI or automated test suite. `*Test*` classes are manual harnesses that require real network peers.
|
- No repository test runner or CI workflow is configured. For Java changes, compile and launch the app; for dashboard changes, run `pnpm typecheck` and `pnpm build`.
|
||||||
- For Java changes, compile and launch the app. For dashboard changes, run `pnpm typecheck` and `pnpm build`.
|
|
||||||
|
|
||||||
## Important Boundaries
|
## Important Boundaries
|
||||||
|
|
||||||
- `KLALBConfigItem` is a polymorphic JSON array keyed by `Type`. Adding a type requires a subclass and cases in both default config serializer and deserializer; unknown types must remain preserved.
|
- `KLALBConfigItem` is a polymorphic JSON array keyed by case-sensitive `Type`. New types need serializer and deserializer support; preserve unknown items' raw JSON.
|
||||||
- `/api/config` is field-by-field parsing, not whole-object Gson mapping. Keep legacy key aliases in sync with new fields.
|
- `/api/config` parses fields and legacy aliases explicitly. Web and Swing writes must use `KLALBProxySystem`'s revision-checked detached-candidate commit path, never mutate the canonical config directly.
|
||||||
- Vendored Gson is `2.1`: HTTP responses that are `JsonElement` instances must be serialized with `JsonElement.toString()`, not reflective `gson.toJson(Object)`; configuration files must use the configured pretty-print Gson path rather than `JsonElement.toString()`.
|
- Vendored Gson is 2.1: serialize HTTP `JsonElement` values with `toString()`; serialize configuration through the configured pretty-print Gson path.
|
||||||
- UI strings use `UIEnv.getRsb()`; add keys to both `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
|
- UI strings use `UIEnv.getRsb()`; add keys to both `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
|
||||||
- Web and Swing configuration writes must use `KLALBProxySystem`'s revisioned detached-candidate commit/event path; do not mutate the canonical config object directly.
|
- `KLALBController.PublishedNodeInfo` is the thread-safe node-info source. Publish profile, effective external endpoints, and extra routes through the controller so snapshots and update flags remain coherent. Peer queries are separate profile, endpoint, and extra-route requests; use `NodeInfoQueryCoordinator` when a consumer needs an aggregate detail result.
|
||||||
- `KLALBController.PublishedNodeInfo` is the thread-safe source for Tiny/Full node-info responses. Publish name, description, external endpoints, and Extra Routes through the controller method so snapshots and Tiny/Full update flags stay consistent.
|
- `RouterInfo` retains an empty legacy UTF slot. `RouterInfoPacket` appends optional update flags behind a marker; treat binary codec changes as mesh-compatibility work and preserve old-reader behavior.
|
||||||
- `RouterInfo` no longer carries a device name. Its wire format retains an empty legacy UTF slot and `RouterInfoPacket` has optional Tiny/Full invalidation flags. Treat codec changes as compatibility work: preserve old-reader behavior and review a whole-mesh rollout.
|
|
||||||
- Full node-info carries `extraRoutes` separately from the endpoint `data` list. Keep absent fields compatible with older peers.
|
|
||||||
|
|||||||
+1
-1
Submodule dashboard updated: 0b705f9ba0...8c13af680a
@@ -56,6 +56,7 @@ import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
|||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
|
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.SRv6Router;
|
||||||
import org.kne.cloud.network.srv6.SRv6RouterListener;
|
import org.kne.cloud.network.srv6.SRv6RouterListener;
|
||||||
import org.kne.cloud.network.tcp.UDPPacket;
|
import org.kne.cloud.network.tcp.UDPPacket;
|
||||||
@@ -389,9 +390,11 @@ public class KLALBController {
|
|||||||
try {
|
try {
|
||||||
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
|
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
|
||||||
KLALBRoutingProtocol.DEFAULT_PORT);
|
KLALBRoutingProtocol.DEFAULT_PORT);
|
||||||
apiClient.requestNodeInfoFull(iaddr, (v) -> {
|
apiClient.requestExternalEndpoints(iaddr, (v) -> {
|
||||||
|
if (v == null || v.getStatus() != NodeInfoQueryStatus.OK) return;
|
||||||
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
|
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
|
||||||
for (MultiProtocolSocketAddress msa : v.getOpenLines()) {
|
if (v.getExternalEndpoints() == null) return;
|
||||||
|
for (MultiProtocolSocketAddress msa : v.getExternalEndpoints()) {
|
||||||
// System.out.print(msa);
|
// System.out.print(msa);
|
||||||
addRemoteLines(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,10 +25,10 @@ import javax.swing.SwingUtilities;
|
|||||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||||
import org.kne.cloud.network.klalb.KLALBController;
|
import org.kne.cloud.network.klalb.KLALBController;
|
||||||
import org.kne.cloud.network.srv6.KLALBNodeInformation;
|
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||||
|
import org.kne.cloud.network.srv6.NodeProfile;
|
||||||
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
||||||
|
|
||||||
public class NetworkGraphPanel extends GraphPanel {
|
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 requestTime = System.currentTimeMillis();
|
||||||
final long requestGeneration;
|
final long requestGeneration;
|
||||||
synchronized (nodeInfoLock) {
|
synchronized (nodeInfoLock) {
|
||||||
@@ -225,9 +225,9 @@ public class NetworkGraphPanel extends GraphPanel {
|
|||||||
nodeInfoRequestGenerations.put(address, requestGeneration);
|
nodeInfoRequestGenerations.put(address, requestGeneration);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
nodeInfoClient.requestNodeInfoTiny(
|
nodeInfoClient.requestNodeProfile(
|
||||||
new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||||
(info) -> SwingUtilities.invokeLater(() -> handleNodeInfo(address, requestGeneration, info)));
|
(profile) -> SwingUtilities.invokeLater(() -> handleNodeProfile(address, requestGeneration, profile)));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
synchronized (nodeInfoLock) {
|
synchronized (nodeInfoLock) {
|
||||||
if (nodeInfoRequestGenerations.get(address) != null
|
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)
|
if (nodeInfoClosed)
|
||||||
return;
|
return;
|
||||||
boolean currentRequest;
|
boolean currentRequest;
|
||||||
boolean nodeExists = getNodes().containsKey(address);
|
boolean nodeExists = getNodes().containsKey(address);
|
||||||
String deviceName = info == null ? "" : info.getDeviceName();
|
String deviceName = profile == null ? "" : profile.getDeviceName();
|
||||||
if (deviceName == null || deviceName.isEmpty())
|
if (deviceName == null || deviceName.isEmpty())
|
||||||
deviceName = "";
|
deviceName = "";
|
||||||
synchronized (nodeInfoLock) {
|
synchronized (nodeInfoLock) {
|
||||||
@@ -295,7 +295,7 @@ public class NetworkGraphPanel extends GraphPanel {
|
|||||||
node.updateLabel();
|
node.updateLabel();
|
||||||
}
|
}
|
||||||
repaint();
|
repaint();
|
||||||
requestNodeInfoTiny(address);
|
requestNodeProfile(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
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())));
|
getNodes().put(inet6Address,new InetGraphNode(inet6Address,Color.BLACK,v2pos.x,v2pos.y,inet6Address.equals(controller.getIpv6Router().getLocator().getAddress())));
|
||||||
}
|
}
|
||||||
if(!inet6Address.equals(localAddress))
|
if(!inet6Address.equals(localAddress))
|
||||||
requestNodeInfoTiny(inet6Address);
|
requestNodeProfile(inet6Address);
|
||||||
}
|
}
|
||||||
Set<IPv6Address> kns=getNodes().keySet();
|
Set<IPv6Address> kns=getNodes().keySet();
|
||||||
for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) {
|
for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import java.awt.BorderLayout;
|
|||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.datatransfer.StringSelection;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
@@ -24,8 +24,11 @@ import org.kne.cloud.klalb.uitool.XDefaultListModel;
|
|||||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||||
import org.kne.cloud.network.klalb.KLALBController;
|
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.KLALBRoutingProtocol;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
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.JTabbedPane;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
@@ -39,6 +42,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
public class NodeInformationPanel extends JPanel {
|
public class NodeInformationPanel extends JPanel {
|
||||||
private IPv6Address address;
|
private IPv6Address address;
|
||||||
private KLALBRoutingProtocolAPIClient client;
|
private KLALBRoutingProtocolAPIClient client;
|
||||||
|
private NodeInfoQueryCoordinator queryCoordinator;
|
||||||
private KLALBController controller;
|
private KLALBController controller;
|
||||||
private Image image;
|
private Image image;
|
||||||
|
|
||||||
@@ -174,6 +178,7 @@ public class NodeInformationPanel extends JPanel {
|
|||||||
|
|
||||||
KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
|
KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
|
||||||
client=new KLALBRoutingProtocolAPIClient(routingProtocol);
|
client=new KLALBRoutingProtocolAPIClient(routingProtocol);
|
||||||
|
queryCoordinator=new NodeInfoQueryCoordinator(client);
|
||||||
nodeInfoUpdateListener=(updatedAddress, flags) -> {
|
nodeInfoUpdateListener=(updatedAddress, flags) -> {
|
||||||
if(active && address.equals(updatedAddress)
|
if(active && address.equals(updatedAddress)
|
||||||
&& (flags & org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
&& (flags & org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
||||||
@@ -186,25 +191,75 @@ public class NodeInformationPanel extends JPanel {
|
|||||||
|
|
||||||
private void requestFullInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel) {
|
private void requestFullInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel) {
|
||||||
final long generation=fullInfoGeneration.incrementAndGet();
|
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 {
|
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;
|
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||||
javax.swing.SwingUtilities.invokeLater(() -> {
|
javax.swing.SwingUtilities.invokeLater(() -> {
|
||||||
if(!active || generation!=fullInfoGeneration.get()) return;
|
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||||
|
if (error != null || info == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyRemoteNodeInfo(overviewArea, extraRoutesEmptyLabel, info);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} 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();
|
listModel.clear();
|
||||||
if(info.getOpenLines()!=null) for (MultiProtocolSocketAddress item : info.getOpenLines()) listModel.addElement(item);
|
if (endpoints != null) for (MultiProtocolSocketAddress item : endpoints) listModel.addElement(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyRoutes(javax.swing.JLabel extraRoutesEmptyLabel, List<String> routes) {
|
||||||
extraRoutesModel.clear();
|
extraRoutesModel.clear();
|
||||||
List<String> routes=info.getExtraRoutes();
|
|
||||||
if (routes != null) for (String route : routes) extraRoutesModel.addElement(route);
|
if (routes != null) for (String route : routes) extraRoutesModel.addElement(route);
|
||||||
extraRoutesEmptyLabel.setText(UIEnv.getRsb().getString("noextraroutes"));
|
extraRoutesEmptyLabel.setText(UIEnv.getRsb().getString("noextraroutes"));
|
||||||
extraRoutesEmptyLabel.setVisible(extraRoutesModel.isEmpty());
|
extraRoutesEmptyLabel.setVisible(extraRoutesModel.isEmpty());
|
||||||
String dd=info.getDeviceDescription();
|
|
||||||
overviewArea.setText(buildOverviewText(info.getDeviceName(), dd==null||dd.isEmpty()?null:dd));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
if(active) e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ import org.kne.cloud.network.ipv6.IPv6NetworkLink;
|
|||||||
import org.kne.cloud.network.ipv6.RouteItem;
|
import org.kne.cloud.network.ipv6.RouteItem;
|
||||||
import org.kne.cloud.network.klalb.*;
|
import org.kne.cloud.network.klalb.*;
|
||||||
import org.kne.cloud.network.monitor.LinkStatus;
|
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;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
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.NeighborInfo;
|
||||||
import org.kne.cloud.network.srv6.RouterInfo;
|
import org.kne.cloud.network.srv6.RouterInfo;
|
||||||
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
||||||
@@ -54,7 +55,7 @@ public class KLALBWebServer {
|
|||||||
this.gson = proxySystem.getGson();
|
this.gson = proxySystem.getGson();
|
||||||
this.nodeInfoUpdateListener = (address, flags) -> {
|
this.nodeInfoUpdateListener = (address, flags) -> {
|
||||||
if ((flags & RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
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) {
|
if ((flags & RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED) != 0) {
|
||||||
long lifecycleGeneration;
|
long lifecycleGeneration;
|
||||||
@@ -73,7 +74,6 @@ public class KLALBWebServer {
|
|||||||
|
|
||||||
public synchronized void start() throws IOException {
|
public synchronized void start() throws IOException {
|
||||||
if (running.get()) return;
|
if (running.get()) return;
|
||||||
nodeInfoFullRevisionEpoch = UUID.randomUUID().toString();
|
|
||||||
if (sseExecutor == null || sseExecutor.isShutdown()) {
|
if (sseExecutor == null || sseExecutor.isShutdown()) {
|
||||||
sseExecutor = createSseExecutor();
|
sseExecutor = createSseExecutor();
|
||||||
}
|
}
|
||||||
@@ -503,14 +503,13 @@ public class KLALBWebServer {
|
|||||||
private final Set<IPv6Address> tinyNameRequestsInFlight = ConcurrentHashMap.newKeySet();
|
private final Set<IPv6Address> tinyNameRequestsInFlight = ConcurrentHashMap.newKeySet();
|
||||||
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestTimes = new ConcurrentHashMap<>();
|
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestTimes = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestGenerations = 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 final Object tinyNameStateLock = new Object();
|
||||||
private static final long TINY_NAME_CACHE_TTL_MS = 60000L;
|
private static final long TINY_NAME_CACHE_TTL_MS = 60000L;
|
||||||
private long nextTinyNameRequestGeneration;
|
private long nextTinyNameRequestGeneration;
|
||||||
private KLALBRoutingProtocol nodeInfoRoutingProtocol;
|
private KLALBRoutingProtocol nodeInfoRoutingProtocol;
|
||||||
private final BiConsumer<IPv6Address, Integer> nodeInfoUpdateListener;
|
private final BiConsumer<IPv6Address, Integer> nodeInfoUpdateListener;
|
||||||
private long nodeInfoLifecycleGeneration;
|
private long nodeInfoLifecycleGeneration;
|
||||||
private volatile String nodeInfoFullRevisionEpoch = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
private static ScheduledExecutorService createSseExecutor() {
|
private static ScheduledExecutorService createSseExecutor() {
|
||||||
return Executors.newSingleThreadScheduledExecutor(r -> {
|
return Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
@@ -605,14 +604,14 @@ public class KLALBWebServer {
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
client.requestNodeInfoTiny(
|
client.requestNodeProfile(
|
||||||
new InetSocketAddress(addr.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
new InetSocketAddress(addr.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||||
info -> {
|
profile -> {
|
||||||
synchronized (tinyNameStateLock) {
|
synchronized (tinyNameStateLock) {
|
||||||
Long activeRequestGeneration = tinyNameRequestGenerations.get(addr);
|
Long activeRequestGeneration = tinyNameRequestGenerations.get(addr);
|
||||||
if (!Long.valueOf(requestGenerationToken).equals(activeRequestGeneration)) return;
|
if (!Long.valueOf(requestGenerationToken).equals(activeRequestGeneration)) return;
|
||||||
tinyDeviceNameCache.put(addr,
|
tinyDeviceNameCache.put(addr,
|
||||||
info != null && info.getDeviceName() != null ? info.getDeviceName() : "");
|
profile != null && profile.getDeviceName() != null ? profile.getDeviceName() : "");
|
||||||
tinyDeviceNameCacheTimes.put(addr, System.currentTimeMillis());
|
tinyDeviceNameCacheTimes.put(addr, System.currentTimeMillis());
|
||||||
tinyNameRequestsInFlight.remove(addr);
|
tinyNameRequestsInFlight.remove(addr);
|
||||||
tinyNameRequestTimes.remove(addr);
|
tinyNameRequestTimes.remove(addr);
|
||||||
@@ -689,6 +688,7 @@ public class KLALBWebServer {
|
|||||||
// 本机:描述直接取本地控制器配置
|
// 本机:描述直接取本地控制器配置
|
||||||
KLALBController.PublishedNodeInfo published = kc.getPublishedNodeInfo();
|
KLALBController.PublishedNodeInfo published = kc.getPublishedNodeInfo();
|
||||||
resp.addProperty("isSelf", true);
|
resp.addProperty("isSelf", true);
|
||||||
|
resp.addProperty("reachable", true);
|
||||||
resp.addProperty("deviceName", published.getDeviceName() != null ? published.getDeviceName() : "");
|
resp.addProperty("deviceName", published.getDeviceName() != null ? published.getDeviceName() : "");
|
||||||
resp.addProperty("deviceDescription",
|
resp.addProperty("deviceDescription",
|
||||||
published.getDeviceDescription() != null ? published.getDeviceDescription() : "");
|
published.getDeviceDescription() != null ? published.getDeviceDescription() : "");
|
||||||
@@ -710,41 +710,36 @@ public class KLALBWebServer {
|
|||||||
|
|
||||||
// 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待)
|
// 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待)
|
||||||
resp.addProperty("isSelf", false);
|
resp.addProperty("isSelf", false);
|
||||||
CompletableFuture<KLALBNodeInformation> future = new CompletableFuture<>();
|
|
||||||
try {
|
try {
|
||||||
KLALBRoutingProtocolAPIClient client = getNodeInfoClient(kc);
|
KLALBRoutingProtocolAPIClient client = getNodeInfoClient(kc);
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
sendError(exchange, 503, "Node info service stopped");
|
sendError(exchange, 503, "Node info service stopped");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.requestNodeInfoFull(
|
NodeInfoQueryCoordinator.Result info = new NodeInfoQueryCoordinator(client)
|
||||||
new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
.query(new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT)).get();
|
||||||
info -> future.complete(info));
|
NodeInfoQueryCoordinator.Section<NodeProfile> profile = info.getProfile();
|
||||||
|
NodeProfile nodeProfile = profile.getValue().orElse(null);
|
||||||
KLALBNodeInformation info;
|
String dname = nodeProfile != null && nodeProfile.getDeviceName() != null
|
||||||
try {
|
&& !nodeProfile.getDeviceName().isEmpty() ? nodeProfile.getDeviceName() : "";
|
||||||
info = future.get(3, TimeUnit.SECONDS);
|
|
||||||
} catch (TimeoutException te) {
|
|
||||||
info = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String dname = info != null && info.getDeviceName() != null && !info.getDeviceName().isEmpty()
|
|
||||||
? info.getDeviceName()
|
|
||||||
: "";
|
|
||||||
resp.addProperty("deviceName", dname);
|
resp.addProperty("deviceName", dname);
|
||||||
resp.addProperty("deviceDescription",
|
resp.addProperty("deviceDescription",
|
||||||
info != null && info.getDeviceDescription() != null ? info.getDeviceDescription() : "");
|
nodeProfile != null && nodeProfile.getDeviceDescription() != null
|
||||||
resp.addProperty("reachable", info != null);
|
? nodeProfile.getDeviceDescription() : "");
|
||||||
|
resp.addProperty("reachable", profile.isReceived()
|
||||||
|
|| info.getExternalEndpoints().isReceived() || info.getExtraRoutes().isReceived());
|
||||||
JsonArray lines = new JsonArray();
|
JsonArray lines = new JsonArray();
|
||||||
if (info != null && info.getOpenLines() != null) {
|
if (info.getExternalEndpoints().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||||
for (MultiProtocolSocketAddress mpsa : info.getOpenLines()) {
|
&& info.getExternalEndpoints().getValue().isPresent()) {
|
||||||
|
for (MultiProtocolSocketAddress mpsa : info.getExternalEndpoints().getValue().get()) {
|
||||||
lines.add(new JsonPrimitive(mpsa.toString()));
|
lines.add(new JsonPrimitive(mpsa.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp.add("openLines", lines);
|
resp.add("openLines", lines);
|
||||||
JsonArray extraRoutes = new JsonArray();
|
JsonArray extraRoutes = new JsonArray();
|
||||||
if (info != null && info.getExtraRoutes() != null) {
|
if (info.getExtraRoutes().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||||
for (String route : info.getExtraRoutes()) {
|
&& info.getExtraRoutes().getValue().isPresent()) {
|
||||||
|
for (String route : info.getExtraRoutes().getValue().get()) {
|
||||||
extraRoutes.add(new JsonPrimitive(route != null ? route : ""));
|
extraRoutes.add(new JsonPrimitive(route != null ? route : ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -799,7 +794,7 @@ public class KLALBWebServer {
|
|||||||
tinyNameRequestTimes.keySet().removeIf(address -> !activeAddresses.contains(address));
|
tinyNameRequestTimes.keySet().removeIf(address -> !activeAddresses.contains(address));
|
||||||
tinyNameRequestGenerations.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) {
|
if (addrs != null) {
|
||||||
for (IPv6Address addr : addrs.keySet()) {
|
for (IPv6Address addr : addrs.keySet()) {
|
||||||
@@ -811,10 +806,9 @@ public class KLALBWebServer {
|
|||||||
String dname = addr.equals(selfAddr) ? kc.getIpv6Router().getDeviceName()
|
String dname = addr.equals(selfAddr) ? kc.getIpv6Router().getDeviceName()
|
||||||
: getTinyDeviceName(kc, addr);
|
: getTinyDeviceName(kc, addr);
|
||||||
nodeObj.addProperty("deviceName", dname != null ? dname : "");
|
nodeObj.addProperty("deviceName", dname != null ? dname : "");
|
||||||
long fullRevision = addr.equals(selfAddr) ? published.getFullRevision()
|
long nodeInfoRevision = addr.equals(selfAddr) ? published.getFullRevision()
|
||||||
: nodeInfoFullRevisions.getOrDefault(addr, 0L);
|
: nodeInfoRevisions.getOrDefault(addr, 0L);
|
||||||
nodeObj.addProperty("nodeInfoFullRevision", fullRevision);
|
nodeObj.addProperty("nodeInfoRevision", nodeInfoRevision);
|
||||||
nodeObj.addProperty("nodeInfoFullRevisionEpoch", nodeInfoFullRevisionEpoch);
|
|
||||||
nodesArray.add(nodeObj);
|
nodesArray.add(nodeObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.kne.cloud.network.srv6;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||||
|
|
||||||
|
public final class ExternalEndpointsResult {
|
||||||
|
private final List<MultiProtocolSocketAddress> externalEndpoints;
|
||||||
|
private final NodeInfoQueryStatus status;
|
||||||
|
|
||||||
|
public ExternalEndpointsResult(List<MultiProtocolSocketAddress> externalEndpoints, NodeInfoQueryStatus status) {
|
||||||
|
this.externalEndpoints = Collections.unmodifiableList(new ArrayList<MultiProtocolSocketAddress>(
|
||||||
|
externalEndpoints == null ? Collections.<MultiProtocolSocketAddress>emptyList() : externalEndpoints));
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
|
||||||
|
return externalEndpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeInfoQueryStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(externalEndpoints, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof ExternalEndpointsResult)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ExternalEndpointsResult other = (ExternalEndpointsResult) obj;
|
||||||
|
return Objects.equals(externalEndpoints, other.externalEndpoints) && status == other.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExternalEndpointsResult [externalEndpoints=" + externalEndpoints + ", status=" + status + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.kne.cloud.network.srv6;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class ExtraRoutesResult {
|
||||||
|
private final List<String> extraRoutes;
|
||||||
|
private final NodeInfoQueryStatus status;
|
||||||
|
|
||||||
|
public ExtraRoutesResult(List<String> extraRoutes, NodeInfoQueryStatus status) {
|
||||||
|
this.extraRoutes = Collections.unmodifiableList(new ArrayList<String>(
|
||||||
|
extraRoutes == null ? Collections.<String>emptyList() : extraRoutes));
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExtraRoutes() {
|
||||||
|
return extraRoutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeInfoQueryStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(extraRoutes, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof ExtraRoutesResult)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ExtraRoutesResult other = (ExtraRoutesResult) obj;
|
||||||
|
return Objects.equals(extraRoutes, other.extraRoutes) && status == other.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExtraRoutesResult [extraRoutes=" + extraRoutes + ", status=" + status + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package org.kne.cloud.network.srv6;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 节点信息(开放线路 + 设备名称 + 设备描述),由路由协议 JSON API 查询获得。
|
|
||||||
*/
|
|
||||||
public class KLALBNodeInformation {
|
|
||||||
private List<MultiProtocolSocketAddress> openLines;
|
|
||||||
private String deviceName;
|
|
||||||
private String deviceDescription;
|
|
||||||
private List<String> extraRoutes;
|
|
||||||
|
|
||||||
public KLALBNodeInformation(List<MultiProtocolSocketAddress> openLines, String deviceName,
|
|
||||||
String deviceDescription) {
|
|
||||||
this(openLines, deviceName, deviceDescription, new ArrayList<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public KLALBNodeInformation(List<MultiProtocolSocketAddress> openLines, String deviceName,
|
|
||||||
String deviceDescription, List<String> extraRoutes) {
|
|
||||||
super();
|
|
||||||
this.openLines = openLines;
|
|
||||||
this.deviceName = deviceName;
|
|
||||||
this.deviceDescription = deviceDescription;
|
|
||||||
this.extraRoutes = extraRoutes == null ? new ArrayList<String>() : extraRoutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MultiProtocolSocketAddress> getOpenLines() {
|
|
||||||
return openLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOpenLines(List<MultiProtocolSocketAddress> openLines) {
|
|
||||||
this.openLines = openLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceName() {
|
|
||||||
return deviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeviceName(String deviceName) {
|
|
||||||
this.deviceName = deviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceDescription() {
|
|
||||||
return deviceDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeviceDescription(String deviceDescription) {
|
|
||||||
this.deviceDescription = deviceDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getExtraRoutes() {
|
|
||||||
return extraRoutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExtraRoutes(List<String> extraRoutes) {
|
|
||||||
this.extraRoutes = extraRoutes == null ? new ArrayList<String>() : extraRoutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "KLALBNodeInformation [openLines=" + openLines + ", deviceName=" + deviceName
|
|
||||||
+ ", deviceDescription=" + deviceDescription + ", extraRoutes=" + extraRoutes + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,16 +3,19 @@ package org.kne.cloud.network.srv6;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||||
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
|
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
|
||||||
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
||||||
|
import org.kne.cloud.network.klalb.SendItem;
|
||||||
import org.kne.opencl64.Releaser;
|
import org.kne.opencl64.Releaser;
|
||||||
|
|
||||||
public class KLALBRoutingProtocolAPIClient {
|
public class KLALBRoutingProtocolAPIClient {
|
||||||
@@ -24,42 +27,106 @@ public class KLALBRoutingProtocolAPIClient {
|
|||||||
|
|
||||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||||
InetSocketAddress addrs = (InetSocketAddress) addr;
|
|
||||||
UUID ruid = dataobj.getUuid();
|
UUID ruid = dataobj.getUuid();
|
||||||
JsonDataPacket relate = null;
|
|
||||||
//System.out.println(ruid + " " + window.getSendmap());
|
|
||||||
switch (dataobj.getType()) {
|
switch (dataobj.getType()) {
|
||||||
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
|
case KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP:
|
||||||
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:
|
PendingRequest profileRequest = acknowledgeResponse(ruid,
|
||||||
|
KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr);
|
||||||
if ((relate = window.ack(ruid)) != null) {
|
if (profileRequest != null) {
|
||||||
List<?> connects = (List<?>) dataobj.getData();
|
((Consumer<NodeProfile>) profileRequest.callback)
|
||||||
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
|
.accept(new NodeProfile(dataobj.getDeviceName(), dataobj.getDeviceDescription()));
|
||||||
connects == null ? 0 : connects.size());
|
|
||||||
if (connects != null) {
|
|
||||||
for (Object open : connects) {
|
|
||||||
if (open instanceof MultiProtocolSocketAddress) {
|
|
||||||
connectsm.add((MultiProtocolSocketAddress) open);
|
|
||||||
} else {
|
|
||||||
connectsm.add(new MultiProtocolSocketAddress((String) open));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP:
|
||||||
|
PendingRequest endpointsRequest = acknowledgeResponse(ruid,
|
||||||
|
KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr);
|
||||||
|
if (endpointsRequest != null) {
|
||||||
|
((Consumer<ExternalEndpointsResult>) endpointsRequest.callback).accept(
|
||||||
|
new ExternalEndpointsResult(decodeExternalEndpoints(dataobj.getData()), dataobj.getStatus()));
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
List<String> extraRoutes = dataobj.getExtraRoutes();
|
case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP:
|
||||||
if(extraRoutes == null) {
|
PendingRequest routesRequest = acknowledgeResponse(ruid,
|
||||||
extraRoutes = new ArrayList<String>();
|
KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr);
|
||||||
}
|
if (routesRequest != null) {
|
||||||
// 组装节点信息(线路 + 设备名称 + 设备描述,精简模式下线路与描述为 null)
|
((Consumer<ExtraRoutesResult>) routesRequest.callback).accept(
|
||||||
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
|
new ExtraRoutesResult(decodeExtraRoutes(dataobj.getData()), dataobj.getStatus()));
|
||||||
dataobj.getDeviceDescription(), extraRoutes);
|
|
||||||
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private PendingRequest acknowledgeResponse(UUID uuid, String expectedRequestType, SocketAddress sourceAddress) {
|
||||||
|
SendItem<JsonDataPacket> pending = window.getSendmap().get(uuid);
|
||||||
|
if (pending == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
KLALBRoutingProtocolJsonData request = pending.getPacket().getDecodedData();
|
||||||
|
if (request == null || !expectedRequestType.equals(request.getType())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object callback = pending.getPacket().getUserCallback();
|
||||||
|
if (!(callback instanceof PendingRequest)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PendingRequest pendingRequest = (PendingRequest) callback;
|
||||||
|
if (!expectedRequestType.equals(pendingRequest.expectedRequestType)
|
||||||
|
|| !sameDestination(pendingRequest.expectedDestination, sourceAddress)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return window.ack(uuid) == null ? null : pendingRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean sameDestination(SocketAddress expected, SocketAddress actual) {
|
||||||
|
if (expected instanceof InetSocketAddress && actual instanceof InetSocketAddress) {
|
||||||
|
InetSocketAddress expectedInet = (InetSocketAddress) expected;
|
||||||
|
InetSocketAddress actualInet = (InetSocketAddress) actual;
|
||||||
|
if (expectedInet.getPort() != actualInet.getPort()) return false;
|
||||||
|
InetAddress expectedAddress = expectedInet.getAddress();
|
||||||
|
InetAddress actualAddress = actualInet.getAddress();
|
||||||
|
if (expectedAddress != null && actualAddress != null) return expectedAddress.equals(actualAddress);
|
||||||
|
if (expectedAddress == null && actualAddress == null) {
|
||||||
|
return expectedInet.getHostString().equalsIgnoreCase(actualInet.getHostString());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return expected != null && expected.equals(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MultiProtocolSocketAddress> decodeExternalEndpoints(Object data) {
|
||||||
|
if (!(data instanceof List<?>)) {
|
||||||
|
return new ArrayList<MultiProtocolSocketAddress>();
|
||||||
|
}
|
||||||
|
List<?> encoded = (List<?>) data;
|
||||||
|
List<MultiProtocolSocketAddress> endpoints = new ArrayList<MultiProtocolSocketAddress>(encoded.size());
|
||||||
|
for (Object value : encoded) {
|
||||||
|
if (value instanceof MultiProtocolSocketAddress) {
|
||||||
|
endpoints.add((MultiProtocolSocketAddress) value);
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
try {
|
||||||
|
endpoints.add(new MultiProtocolSocketAddress((String) value));
|
||||||
|
} catch (RuntimeException ignored) {
|
||||||
|
// Ignore malformed entries while preserving the rest of the response.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> decodeExtraRoutes(Object data) {
|
||||||
|
if (!(data instanceof List<?>)) {
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
List<?> encoded = (List<?>) data;
|
||||||
|
List<String> routes = new ArrayList<String>(encoded.size());
|
||||||
|
for (Object value : encoded) {
|
||||||
|
if (value instanceof String) {
|
||||||
|
routes.add((String) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
|
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
|
||||||
this.routingProtocol = routingProtocol;
|
this.routingProtocol = routingProtocol;
|
||||||
routingProtocol.addReceiver(rec);
|
routingProtocol.addReceiver(rec);
|
||||||
@@ -67,31 +134,29 @@ public class KLALBRoutingProtocolAPIClient {
|
|||||||
clr.register(this, releaser);
|
clr.register(this, releaser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void requestNodeProfile(SocketAddress addr, Consumer<NodeProfile> callback) throws IOException {
|
||||||
* 精简查询:仅获取对端设备名称(开销最小,适用于未查看节点详情的场景)。
|
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr, callback);
|
||||||
*/
|
|
||||||
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
|
||||||
throws IOException {
|
|
||||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void requestExternalEndpoints(SocketAddress addr, Consumer<ExternalEndpointsResult> callback)
|
||||||
* 完整查询:获取对端开放线路 + 设备名称 + 设备描述(查看节点信息时使用)。
|
|
||||||
*/
|
|
||||||
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
|
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
public void requestExtraRoutes(SocketAddress addr, Consumer<ExtraRoutesResult> callback) throws IOException {
|
||||||
throws IOException {
|
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendNodeInfoRequest(String type, SocketAddress addr, Object callback) throws IOException {
|
||||||
UUID suid = UUID.randomUUID();
|
UUID suid = UUID.randomUUID();
|
||||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
||||||
JsonDataPacket packet = new JsonDataPacket(json);
|
JsonDataPacket packet = new JsonDataPacket(json);
|
||||||
packet.setUserCallback(callback);
|
packet.setUserCallback(new PendingRequest(addr, type, callback));
|
||||||
|
synchronized (this) {
|
||||||
|
if (closed.get() || releaser.isReleased()) throw new IOException("Node info client is closed");
|
||||||
window.put(suid, packet);
|
window.put(suid, packet);
|
||||||
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||||
@@ -100,13 +165,27 @@ public class KLALBRoutingProtocolAPIClient {
|
|||||||
|
|
||||||
private KLALBRoutingProtocolAPIClientReleaser releaser;
|
private KLALBRoutingProtocolAPIClientReleaser releaser;
|
||||||
|
|
||||||
public void close() {
|
public synchronized void close() {
|
||||||
releaser.run();
|
if (closed.compareAndSet(false, true)) releaser.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return releaser.isReleased();
|
return closed.get() || releaser.isReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class PendingRequest {
|
||||||
|
private final SocketAddress expectedDestination;
|
||||||
|
private final String expectedRequestType;
|
||||||
|
private final Object callback;
|
||||||
|
|
||||||
|
private PendingRequest(SocketAddress expectedDestination, String expectedRequestType, Object callback) {
|
||||||
|
this.expectedDestination = expectedDestination;
|
||||||
|
this.expectedRequestType = expectedRequestType;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AtomicBoolean closed = new AtomicBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package org.kne.cloud.network.srv6;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import org.kne.cloud.network.klalb.KLALBController;
|
import org.kne.cloud.network.klalb.KLALBController;
|
||||||
@@ -15,30 +13,40 @@ public class KLALBRoutingProtocolAPIServer {
|
|||||||
private KLALBController controller;
|
private KLALBController controller;
|
||||||
private KLALBRoutingProtocol routingProtocol;
|
private KLALBRoutingProtocol routingProtocol;
|
||||||
|
|
||||||
|
|
||||||
private static final Cleaner clr = Cleaner.create();
|
private static final Cleaner clr = Cleaner.create();
|
||||||
|
|
||||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||||
try {
|
try {
|
||||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||||
InetSocketAddress addrs=(InetSocketAddress) addr;
|
|
||||||
switch (dataobj.getType()) {
|
switch (dataobj.getType()) {
|
||||||
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ:
|
case KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ:
|
||||||
// 精简查询:仅返回设备名称(设备名称随路由信息广播公开,不受 denyExternalEndpointQuery 限制)
|
KLALBController.PublishedNodeInfo profilePublished = controller.getPublishedNodeInfo();
|
||||||
KLALBController.PublishedNodeInfo published = controller.getPublishedNodeInfo();
|
KLALBRoutingProtocolJsonData profile = new KLALBRoutingProtocolJsonData(
|
||||||
KLALBRoutingProtocolJsonData tinyjson=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP,dataobj.getUuid(),null);
|
KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP, dataobj.getUuid(), null);
|
||||||
tinyjson.setDeviceName(published.getDeviceName());
|
profile.setDeviceName(profilePublished.getDeviceName());
|
||||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(tinyjson),addr);
|
profile.setDeviceDescription(profilePublished.getDeviceDescription());
|
||||||
|
profile.setStatus(NodeInfoQueryStatus.OK);
|
||||||
|
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(profile), addr);
|
||||||
break;
|
break;
|
||||||
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ:
|
case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ:
|
||||||
// 完整查询:设备名称与描述总是正常响应;denyExternalEndpointQuery 仅隐藏外部端点列表
|
boolean denyEndpoints = controller.getConfigItem() != null
|
||||||
boolean denyEndpoints=controller.getConfigItem()!=null&&controller.getConfigItem().isDenyExternalEndpointQuery();
|
&& controller.getConfigItem().isDenyExternalEndpointQuery();
|
||||||
KLALBController.PublishedNodeInfo fullPublished = controller.getPublishedNodeInfo();
|
KLALBController.PublishedNodeInfo endpointsPublished = controller.getPublishedNodeInfo();
|
||||||
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP,dataobj.getUuid(),denyEndpoints?null:new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(fullPublished.getExternalEndpoints()));
|
KLALBRoutingProtocolJsonData endpoints = new KLALBRoutingProtocolJsonData(
|
||||||
json.setDeviceName(fullPublished.getDeviceName());// 附带本机设备名称
|
KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP, dataobj.getUuid(),
|
||||||
json.setDeviceDescription(fullPublished.getDeviceDescription());// 附带本机设备描述
|
denyEndpoints ? new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>()
|
||||||
json.setExtraRoutes(new ArrayList<String>(fullPublished.getExtraRoutes()));
|
: new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(
|
||||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
|
endpointsPublished.getExternalEndpoints()));
|
||||||
|
endpoints.setStatus(denyEndpoints ? NodeInfoQueryStatus.DENIED : NodeInfoQueryStatus.OK);
|
||||||
|
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(endpoints), addr);
|
||||||
|
break;
|
||||||
|
case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ:
|
||||||
|
KLALBController.PublishedNodeInfo routesPublished = controller.getPublishedNodeInfo();
|
||||||
|
KLALBRoutingProtocolJsonData routes = new KLALBRoutingProtocolJsonData(
|
||||||
|
KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP, dataobj.getUuid(),
|
||||||
|
new ArrayList<String>(routesPublished.getExtraRoutes()));
|
||||||
|
routes.setStatus(NodeInfoQueryStatus.OK);
|
||||||
|
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(routes), addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -62,24 +70,23 @@ public class KLALBRoutingProtocolAPIServer {
|
|||||||
return routingProtocol;
|
return routingProtocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
releaser.run();
|
releaser.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return releaser.isReleased();
|
return releaser.isReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||||
|
|
||||||
private KLALBRoutingProtocol routingProtocol;
|
private KLALBRoutingProtocol routingProtocol;
|
||||||
|
|
||||||
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,
|
||||||
|
BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||||
super(resource);
|
super(resource);
|
||||||
this.routingProtocol = routingProtocol;
|
this.routingProtocol = routingProtocol;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +1,89 @@
|
|||||||
package org.kne.cloud.network.srv6;
|
package org.kne.cloud.network.srv6;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class KLALBRoutingProtocolJsonData {
|
public class KLALBRoutingProtocolJsonData {
|
||||||
public static final String NODE_INFO_TINY_REQ="nodeinfotinyreq";// 精简查询:仅设备名称
|
public static final String NODE_PROFILE_REQ = "nodeprofilereq";
|
||||||
public static final String NODE_INFO_TINY_RESP="nodeinfotinyresp";
|
public static final String NODE_PROFILE_RESP = "nodeprofileresp";
|
||||||
public static final String NODE_INFO_FULL_REQ="nodeinfofullreq";// 完整查询:开放线路+设备名称+设备描述
|
public static final String NODE_EXTERNAL_ENDPOINTS_REQ = "nodeexternalendpointsreq";
|
||||||
public static final String NODE_INFO_FULL_RESP="nodeinfofullresp";
|
public static final String NODE_EXTERNAL_ENDPOINTS_RESP = "nodeexternalendpointsresp";
|
||||||
|
public static final String NODE_EXTRA_ROUTES_REQ = "nodeextraroutesreq";
|
||||||
|
public static final String NODE_EXTRA_ROUTES_RESP = "nodeextraroutesresp";
|
||||||
|
|
||||||
private String type;
|
private String type;
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private Object data;
|
private Object data;
|
||||||
private String deviceName;// 对端设备名称
|
private String deviceName;
|
||||||
private String deviceDescription;// 对端设备描述
|
private String deviceDescription;
|
||||||
private List<String> extraRoutes;
|
private NodeInfoQueryStatus status;
|
||||||
public String getDeviceName() {
|
|
||||||
return deviceName;
|
|
||||||
}
|
|
||||||
public void setDeviceName(String deviceName) {
|
|
||||||
this.deviceName = deviceName;
|
|
||||||
}
|
|
||||||
public String getDeviceDescription() {
|
|
||||||
return deviceDescription;
|
|
||||||
}
|
|
||||||
public void setDeviceDescription(String deviceDescription) {
|
|
||||||
this.deviceDescription = deviceDescription;
|
|
||||||
}
|
|
||||||
public List<String> getExtraRoutes() {
|
|
||||||
return extraRoutes;
|
|
||||||
}
|
|
||||||
public void setExtraRoutes(List<String> extraRoutes) {
|
|
||||||
this.extraRoutes = extraRoutes;
|
|
||||||
}
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
public Object getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
public UUID getUuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((data == null) ? 0 : data.hashCode());
|
|
||||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
|
||||||
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
|
|
||||||
result = prime * result + ((deviceName == null) ? 0 : deviceName.hashCode());
|
|
||||||
result = prime * result + ((deviceDescription == null) ? 0 : deviceDescription.hashCode());
|
|
||||||
result = prime * result + ((extraRoutes == null) ? 0 : extraRoutes.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj;
|
|
||||||
if (data == null) {
|
|
||||||
if (other.data != null)
|
|
||||||
return false;
|
|
||||||
} else if (!data.equals(other.data))
|
|
||||||
return false;
|
|
||||||
if (type == null) {
|
|
||||||
if (other.type != null)
|
|
||||||
return false;
|
|
||||||
} else if (!type.equals(other.type))
|
|
||||||
return false;
|
|
||||||
if (uuid == null) {
|
|
||||||
if (other.uuid != null)
|
|
||||||
return false;
|
|
||||||
} else if (!uuid.equals(other.uuid))
|
|
||||||
return false;
|
|
||||||
if (deviceName == null) {
|
|
||||||
if (other.deviceName != null)
|
|
||||||
return false;
|
|
||||||
} else if (!deviceName.equals(other.deviceName))
|
|
||||||
return false;
|
|
||||||
if (deviceDescription == null) {
|
|
||||||
if (other.deviceDescription != null)
|
|
||||||
return false;
|
|
||||||
} else if (!deviceDescription.equals(other.deviceDescription))
|
|
||||||
return false;
|
|
||||||
if (extraRoutes == null) {
|
|
||||||
if (other.extraRoutes != null)
|
|
||||||
return false;
|
|
||||||
} else if (!extraRoutes.equals(other.extraRoutes))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
|
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
|
||||||
super();
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public String toString() {
|
public String getType() {
|
||||||
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + ", deviceName="
|
return type;
|
||||||
+ deviceName + ", deviceDescription=" + deviceDescription + ", extraRoutes=" + extraRoutes + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UUID getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceName(String deviceName) {
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceDescription() {
|
||||||
|
return deviceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceDescription(String deviceDescription) {
|
||||||
|
this.deviceDescription = deviceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeInfoQueryStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(NodeInfoQueryStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(type, uuid, data, deviceName, deviceDescription, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof KLALBRoutingProtocolJsonData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj;
|
||||||
|
return Objects.equals(type, other.type) && Objects.equals(uuid, other.uuid)
|
||||||
|
&& Objects.equals(data, other.data) && Objects.equals(deviceName, other.deviceName)
|
||||||
|
&& Objects.equals(deviceDescription, other.deviceDescription)
|
||||||
|
&& Objects.equals(status, other.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data
|
||||||
|
+ ", deviceName=" + deviceName + ", deviceDescription=" + deviceDescription + ", status=" + status
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.kne.cloud.network.srv6;
|
||||||
|
|
||||||
|
public enum NodeInfoQueryStatus {
|
||||||
|
OK,
|
||||||
|
DENIED
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.kne.cloud.network.srv6;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class NodeProfile {
|
||||||
|
private final String deviceName;
|
||||||
|
private final String deviceDescription;
|
||||||
|
|
||||||
|
public NodeProfile(String deviceName, String deviceDescription) {
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
this.deviceDescription = deviceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceDescription() {
|
||||||
|
return deviceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(deviceName, deviceDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof NodeProfile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NodeProfile other = (NodeProfile) obj;
|
||||||
|
return Objects.equals(deviceName, other.deviceName)
|
||||||
|
&& Objects.equals(deviceDescription, other.deviceDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "NodeProfile [deviceName=" + deviceName + ", deviceDescription=" + deviceDescription + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user